哈希表

两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标

示例 1:

1
2
3
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

1
2
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

1
2
输入:nums = [3,3], target = 6
输出:[0,1]

暴力解法

遍历,时间复杂度为$O(N^2)$,空间复杂度为$O(1)$

1
2
3
4
5
6
7
8
9
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
for i in range(n):
for j in range(i+1, n):
if nums[i] + nums[j] == target:
return [i, j]

return []

哈希表

1
2
3
4
5
6
7
8
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashtable = dict()
for i, num in enumerate(nums):
if target - num in hashtable:
return [hashtable[target - num], i]
hashtable[nums[i]] = i
return []

时间复杂度$O(N)$,空间复杂度$O(N)$,为哈希表的开销

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hashtable;
for (int i = 0; i<nums.size(); ++i){
auto it = hashtable.find(target - nums[i]);
if(it != hashtable.end()){
return {it->second, i}
}
hashtable[nums[i]] = i
}
return [];
}
};

python 的 dict 和 C++ 的 unordered_map 基本一致

字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

示例 1:

输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]

输出: [[“bat”],[“nat”,”tan”],[“ate”,”eat”,”tea”]]

解释:

  • 在 strs 中没有字符串可以通过重新排列来形成 "bat"
  • 字符串 "nat""tan" 是字母异位词,因为它们可以重新排列以形成彼此
  • 字符串 "ate""eat""tea" 是字母异位词,因为它们可以重新排列以形成彼此

示例 2:

输入: strs = [“”]

输出: [[“”]]

示例 3:

输入: strs = [“a”]

输出: [[“a”]]


可以重构的字符串在排序后结果相同,可以以此作为哈希表的键,原字符串作为值,最后输出哈希表值的元组

哈希表排序

c++不排序直接考虑unordered_map,键为排序后的字符串,值为vector容器,存储原始的字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for(auto& str : strs){
string key = str;
sort(key.begin(), key.end());
mp[key].emplace_back(str);
}
vector<vector<string>> ans;
for(auto it = mp.begin(); it != mp.end(); ++it){
ans.emplace_back(it->second);
}
return ans;
}
};

python解法感觉没那么有逻辑

1
2
3
4
5
6
7
8
9
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
mp = collections.defaultdict(list)

for st in strs:
key = "".join(sorted(st))
mp[key].append(st)

return list(mp.values())
  • 会写 dict[key].xxx(),而且 key 可能第一次出现 → 用 defaultdict
  • 写的是 dict[key] = value,并且读之前会判断 → 不要用 defaultdict

最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度

请你设计并实现时间复杂度为 O(n) 的算法解决此问题

示例 1:

1
2
3
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

1
2
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

示例 3:

1
2
输入:nums = [1,0,1,2]
输出:3

既然要时间复杂度为O(n)那必然不能暴力枚举了

如果对每个数都向左、向右扩展,那最坏会退化成O(n²)

破解方法:只有当一个数是“连续序列起点”时,才从它开始向右数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> num_set;
for(auto& num:nums){
num_set.insert(num);
}
int maxlen = 0;
for(auto& num: num_set){
if(!num_set.count(num-1)){
int currentNum = num;
int currentLen = 1;

while(num_set.count(currentNum+1)){
currentNum +=1;
currentLen +=1;
}
maxlen = max(maxlen, currentLen);
}
}
return maxlen;
}
};

count快速判断是否存在

双指针

移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序

请注意 ,必须在不复制数组的情况下原地对数组进行操作

示例 1:

1
2
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

示例 2:

1
2
输入: nums = [0]
输出: [0]

使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部[快慢指针]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int n = nums.size(), left = 0, right =0;
while(right < n){
if(nums[right])
{
swap(nums[left], num[right]);
left++;
}
right++;
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
left = right = 0
while right < n:
if nums[right] != 0:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right += 1