力扣 Hot 100 题解 (js版)更新ing
🚩哈希表
✅ 1. 两数之和
Code:
暴力法
复杂度分析:
- 时间复杂度: ∗ O ( N 2 ) ∗ *O(N^2)* ∗O(N2)∗,其中 N 是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。
- 空间复杂度:O(1)。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let len = nums.length
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (nums[i] + nums[j] === target) {
return [i, j]
}
}
}
};
哈希表
复杂度分析:
- 时间复杂度:O(N),其中 N 是数组中的元素数量。对于每一个元素 x,我们可以 O(1) 地寻找 target - x。
- 空间复杂度:O(N),其中 N 是数组中的元素数量。主要为哈希表的开销。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
// 查找表
let map = new Map()
for(let i = 0; i < nums.length; i++) {
if(map.has(target - nums[i])) {
return [map.get(target - nums[i]), i]
}
map.set(nums[i], i)
}
return []
};
✅ 49. 字母异位词分组
题目 “字母异位词分组” 的目标是将一组字符串按照字母异位词(即字符串由相同的字母组成,字母顺序不同)分组。
我们可以通过以下思路解决:
思路:
- 特征值:排序法
- 字母异位词具有一个共同特点:它们的字母排序后会得到相同的结果。
- 例如:
"eat"
和"tea"
排序后都是"aet"
。 - 将每个字符串的排序结果作为键,将具有相同排序结果的字符串分到同一组。
- 数据结构:
- 使用一个哈希表(
Map
),键是排序后的字符串,值是一个数组,存储具有相同特征值的字符串。
- 使用一个哈希表(
- 步骤:
- 遍历字符串数组,对每个字符串排序。
- 将排序后的字符串作为键存入哈希表,值是对应的字符串数组。
- 遍历完成后,哈希表的值即为分组结果。
Code:
复杂度分析:
- 时间复杂度:
- 对每个字符串进行排序的时间复杂度为 O ( k log k ) O(k \log k) O(klogk),其中 k k k 是字符串的平均长度。
- 遍历数组的时间复杂度为 O ( n ) O(n) O(n),其中 n n n 是字符串数组的长度。
- 总时间复杂度为 O ( n ⋅ k log k ) O(n \cdot k \log k) O(n⋅klogk)。
- 空间复杂度:
- 使用了一个哈希表来存储分组结果,空间复杂度为 O ( n ⋅ k ) O(n \cdot k) O(n⋅k),其中 n n n 是字符串数组的长度, k k k 是字符串的平均长度。
/**
* 字母异位词分组
* @param {string[]} strs
* @return {string[][]}
*/
function groupAnagrams(strs) {
const map = new Map(); // 创建一个哈希表
for (const str of strs) {
// 对字符串排序后作为特征值
const sortedStr = str.split('').sort().join('');
// 如果哈希表中没有该特征值,则初始化为一个数组
if (!map.has(sortedStr)) {
map.set(sortedStr, []);
}
// 将字符串加入对应的组
map.get(sortedStr).push(str);
}
// 返回所有分组的数组
return Array.from(map.values());
}
// 测试用例
const strs = ["eat", "tea", "tan", "ate", "nat", "bat"];
console.log(groupAnagrams(strs));
运行结果:
对于输入 ["eat", "tea", "tan", "ate", "nat", "bat"]
,输出为:
[
["eat", "tea", "ate"],
["tan", "nat"],
["bat"]
]
扩展(优化排序法):
如果希望优化排序的时间,可以考虑使用字符计数代替排序,形成一个唯一的特征值,例如:
- 对于
"eat"
,记录字符频率为"1#1#1#0#0#..."
,用这种方式构建键。
✅ 128. 最长连续序列
题目 128:最长连续序列
给定一个未排序的整数数组,找出其中最长连续元素序列的长度。要求算法的时间复杂度为 O ( n ) O(n) O(n)。
解题思路:
要实现
O
(
n
)
O(n)
O(n) 的时间复杂度,我们可以利用哈希表(Set
)来快速检查连续的数字是否存在:
- 将所有数字存入哈希表(
Set
):- 使用哈希表存储数组中的所有数字,便于快速查找某个数字是否存在。
- 遍历数组,寻找序列的起点:
- 对每个数字
num
,检查是否存在num - 1
。 - 如果不存在
num - 1
,说明num
是一个序列的起点。 - 从
num
开始,依次检查num + 1
是否存在,计算序列的长度。
- 对每个数字
- 记录最长序列的长度:
- 在遍历过程中,更新最长序列长度。
Code:
复杂度分析
- 时间复杂度:
- 将数组转换为哈希表的时间复杂度是 O ( n ) O(n) O(n)。
- 遍历每个数字,并检查哈希表的操作为 O ( 1 ) O(1) O(1)。
- 每个序列中的数字最多只被遍历一次,因此整体时间复杂度为 O ( n ) O(n) O(n)。
- 空间复杂度:
- 需要额外的哈希表存储所有数字,空间复杂度为 O ( n ) O(n) O(n)。
/**
* 最长连续序列
* @param {number[]} nums
* @return {number}
*/
function longestConsecutive(nums) {
// 将数组转化为哈希集合
const numSet = new Set(nums);
let maxLength = 0;
// 遍历数组中的每个数字
for (const num of numSet) {
// 如果 num 是序列的起点(num - 1 不存在)
if (!numSet.has(num - 1)) {
let currentNum = num;
let currentLength = 1;
// 找到当前序列的长度
while (numSet.has(currentNum + 1)) {
currentNum += 1;
currentLength += 1;
}
// 更新最大长度
maxLength = Math.max(maxLength, currentLength);
}
}
return maxLength;
}
// 测试用例
const nums = [100, 4, 200, 1, 3, 2];
console.log(longestConsecutive(nums)); // 输出: 4 (最长序列是 [1, 2, 3, 4])