难度标识:
⭐:简单,⭐⭐:中等,⭐⭐⭐:困难。
tips:这里的难度不是根据LeetCode难度定义的,而是根据我解题之后体验到题目的复杂度定义的。
哈希
1.两数之和 ⭐
- 经典LeetCode第一题。
代码:
var twoSum = function (nums, target) {
// 直接遍历数组,做差值寻找另一个数的下标
for (let i = 0; i < nums.length; i++) {
const index = nums.indexOf(target - nums[i])
if (index !== -1 && index !== i) {
return [i, index]
}
}
}
上面这种写法的时间复杂度是 O(n²),空间复杂度是 O(1)。
var twoSum = function (nums, target) {
// 定义一个对象判断差值是否存在
const obj = {}
for (let i = 0; i < nums.length; i++) {
const num = target - nums[i]
if (obj[num] !== undefined) {
return [i, obj[num]]
}
obj[nums[i]] = i
}
}
上面
if(obj[num] !== undefined)为什么不写成if(obj[num]),这是处理obj[num]为0的情况。
上面这种写法的时间复杂度是 O(n),空间复杂度是 O(n)。
2.字母异位词分组 ⭐
思路:将数组中的每个字符串进行排序,然后排序相同的push到同一个数组中。
代码:
var groupAnagrams = function (strs) {
const obj = {}
for (let s of strs) {
const sOrder = s.split('').sort().join('')
if (!obj[sOrder]) {
obj[sOrder] = []
}
obj[sOrder].push(s)
}
return Object.values(obj)
}
3.最长连续序列 ⭐⭐
思路:
-
1.将数组放到一个Set集合中,因为题目不要求序列元素在原数组中连续,所以可以去重。
-
2.遍历集合,如果当前数num-1不在集合中,那么当前数就是起点,然后判断后续num+1在不在集合中,在的话一直+1判断,一直到num+1不在集合中时,然后计算序列长度。
解这题的关键就是找到每个序列段的起点。不明白的也可以看这个视频。
代码:
var longestConsecutive = function (nums) {
const set = new Set(nums)
let count = 0
for (let num of set) {
if (!set.has(num - 1)) {
let val = num + 1
while (set.has(val)) val++;
count = Math.max(count, val - num)
}
}
return count
}
时间复杂度:
-
const set = new Set(nums):创建一个 Set 需要遍历整个nums数组,因此时间复杂度为 O(n)。 -
循环:
for (let num of set):最坏的情况是遍历整个set,即 O(n)。-
检查
set.has(num - 1)的时间复杂度为 O(1)。 -
while (set.has(val)) val++;:这个循环可能看起来是一个内部循环,让人觉得是二重循环。但实际上,由于一个数字只会进入这个循环一次(之后它的前一个数字会在Set中,所以不会再执行该循环),因此,所有数字总共进入这个while循环的次数最多是 n 次。因此,这个循环的累积时间复杂度是 O(n)。 -
count = Math.max(count, val - num):时间复杂度为 O(1)。
-
- 因此,整个代码块的时间复杂度是:
O(n)+O(n)×O(1)+O(1)+O(n))=O(n)+O(2n)=O(3n)=O(n)
所以,整体时间复杂度为 O(n)。