算法(七)--哈希
算法(七)--哈希
哈希
解决哈希表问题的精髓就在如何设计键。利用键的唯一性去解决问题。
cpp数据结构
- unordered_map
1
2
3
4
5
6
std::unordered_map<int, std::string> myMap;
myMap[1] = "One"; // 插入或更新
myMap.insert({2, "Two"}); // 插入
myMap.erase(1); // 删除
auto it = myMap.find(2); // 查找键为2的元素
- unordered_set / 可以做到O(1)的查找某个元素在不在集合中
1
2
3
4
5
std::unordered_set<int> mySet;
mySet.insert(10); // 插入
mySet.erase(10); // 删除
auto it = mySet.find(10); // 查找元素10
- unordered_multimap / 可以存储重复的键
1
2
3
4
std::unordered_multimap<int, std::string> myMultimap;
myMultimap.insert({1, "One"});
myMultimap.insert({1, "Uno"}); // 允许相同的键1
- unordered_multiset
1
2
3
4
std::unordered_multiset<int> myMultiset;
myMultiset.insert(10);
myMultiset.insert(10); // 允许重复元素
两数之和
对于每个x,先查找哈希表中是否存在target - x这样的键,如果有,返回{i, hash[target - x]},否则将x插入哈希表中。
字母异位词
如何判断两个字符串的无序相同性,最好的办法是采用map。map的键是字符串中出现的字符,值是字符出现的次数,判断这两个map是否相同即可判断是否是字母异位词
1
2
3
4
unordered_map<char, int> map;
for(int i = 0; i < 26; i++) {
map['a' + i] = 0;
}
字母异位词分组(好题)
本题的难点在于如何确定一个字母异位词的键的形式。这里采用排序的形式,将字母异位词进行排序后储存到哈希表中。同样是先查找,如果没有说明是新的词,插入到哈希表中,值就是该异位词的集合。
1
2
3
4
5
6
7
8
9
10
11
12
13
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> map;
for(string str : strs) {
string cur = str;
sort(cur.begin(), cur.end());
map[cur].push_back(str);
}
vector<vector<string>> ans;
for(auto pair : map) {
ans.push_back(pair.second);
}
return ans;
}
最长连续序列
对每个当前数x,去找n次数组中有没有x+1,x+2,x+3,x+4,…;再更新长度最小值. 优化就是利用undered_set的查找时间复杂度O(1)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int longestConsecutive(vector<int>& nums) {
unordered_set<int> hash(nums.begin(), nums.end());
int ans = 0;
for(int num : nums) {
if(hash.find(num - 1) == hash.end()) {
int cur = num;
int len = 1;
while(hash.find(cur + 1) != hash.end()) {
cur++;
len++;
}
ans = max(ans, len);
}
}
return ans;
}
同构字符串
给定两个字符串 s 和 t ,判断它们是否是同构的。如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。
思路:用两个哈希表记录s到t和t到s的映射关系,如果出现s到t1和s到t2的映射,说明不成立。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool isIsomorphic(string s, string t) {
unordered_map<char, char> s2t, t2s;
for(int i = 0; i < s.size(); i++) {
char a = s[i], b = t[i];
// 这是判断一对一关系是否成立的经典模式,需要记住
if(s2t.count(a) > 0 && s2t[a] != b ||
t2s.count(b) > 0 && t2s[b] != a) {
return false;
}
s2t[a] = b;
t2s[b] = a;
}
return true;
}
单词规律
给定一种规律 pattern 和一个字符串 s ,判断 s 是否遵循相同的规律。这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律。
就是在同构字符串的基础上加上了s的单词拆分parse的过程。这里用stringstream来拆分字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool wordPattern(string pattern, string s) {
unordered_map<char, string> p2s;
unordered_map<string, char> s2p;
stringstream ss(s);
string cur;
for(char c : pattern) {
if(!(ss >> cur)) return false;
if(p2s.count(c) > 0 && p2s[c] != cur ||
s2p.count(cur) > 0 && s2p[cur] != c) {
return false;
}
p2s[c] = cur;
s2p[cur] = c;
}
return !(ss >> cur);
}
有环检测
用哈希表储存出现过的节点,如果出现过就说明有环。但是空间开销要比双指针的大。
1
2
3
4
5
6
7
8
9
bool hasCycle(ListNode *head) {
unordered_set<ListNode*> hash;
while(head) {
if(hash.count(head) > 0) return true;
hash.insert(head);
head = head->next;
}
return false;
}
This post is licensed under CC BY 4.0 by the author.