代码随想录算法训练营Day5&6 hash_map
关于哈希表
官方定义:哈希表是根据关键码的值而直接进行访问的数据结构。
用一下我的理解:哈希表就像是一个输入法。我们只需要输入对应的“拼音”,输入法就会返回给我们“汉字”。换句话说,我们可以认为,“拼音”与“汉字”间具有某种映射关系(哈希函数)。汉字中有很多同音字,单纯输入拼音获得的“汉字”有可能不唯一(哈希冲突)。
关于哈希实现
一般来说,C++中直接使用STL容器就行了。粗浅的分类如下:
- vector 储存数据值域不大
- set 需要去重、排序、数据量稍大
- map 需要同时获得key、val
Leetcode 242. 有效的字母异位词
题目链接:https://leetcode.cn/problems/valid-anagram/description/
题目描述:给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的 字母异位词。
题目分析:如果你能够想到“构建一种映射,输入一个字符,输出它出现的次数”,那你肯定能想到哈希的做法。顺带一提,这个题最暴力的做法应该是\(O(nlogn * mlogm)\)(\(n、m\)分别是二者的长度),排序后比较大小就行了。
至于哈希的实现,因为只有小写字母,所以直接用数组模拟就行了。
// (法一:暴力)
class Solution {
public:
bool isAnagram(string s, string t) {
std::sort(s.begin(), s.end());
std::sort(t.begin(), t.end());
return s == t;
}
};
// (法二:哈希)
class Solution {
public:
bool isAnagram(string s, string t) {
std::array<int, 26> cnt{};
for (auto& c : s) {
cnt[c - 'a'] ++;
}
for (auto& c : t) {
cnt[c - 'a'] --;
}
return cnt == array<int, 26>();
}
};
Leetcode 349. 两个数组的交集
题目链接:https://leetcode.cn/problems/intersection-of-two-arrays/description/
题目描述:给定两个数组 nums1 和 nums2 ,返回它们的交集。输出结果中的每个元素一定是唯一的。我们可以 不考虑输出结果的顺序。
题目分析:考虑快速判断一个元素是否存在集合中,可以使用哈希。这里我们选择使用set来存一个数组,然后枚举另一个数组的所有元素,只要这个元素在set中,我们就把他加入答案。(这里set起一个去重的作用)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
std::vector<int> ans;
std::set<int> st(nums1.begin(), nums1.end());
for (auto& x : nums2) {
if (st.count(x)) {
st.erase(x);
ans.push_back(x);
}
}
return ans;
}
};
Leetcode 202. 快乐数
题目链接:https://leetcode.cn/problems/happy-number/description/
题目描述:编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
题目分析:考虑模拟。模拟每一轮的演变,然后把出现的数字加入哈希表中,如果发现已经出现过了,return \(false\),直到出现1为止。
也可以使用快慢指针(欧几里得判环法),如果有环,return \(false\)既可。
// (法一:哈希)
class Solution {
public:
bool isHappy(int n) {
auto sum = [&](int num) {
long long res = 0;
while (num) {
int digit = num % 10;
res += digit * digit;
num /= 10;
}
return res;
};
std::set<int> seen;
while (n != 1 && seen.find(n) == seen.end()) {
seen.insert(n);
n = sum(n);
}
return n == 1;
}
};
// (法二:双指针)
class Solution {
public:
bool isHappy(int n) {
auto sum = [&](int num) {
long long res = 0;
while (num) {
int digit = num % 10;
res += digit * digit;
num /= 10;
}
return res;
};
int slow = n, fast = n;
do {
slow = sum(slow);
fast = sum(fast);
fast = sum(fast);
} while (slow != fast);
return slow == 1;
}
};
Leetcode 1. 两数之和
题目链接:https://leetcode.cn/problems/two-sum/description/
题目描述:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
题目分析:一眼暴力,跑两个\(for\)循环就结束了。那么能不能更优呢?
对于$$nums[i] + nums[j] = target$$移项可得:$$nums[i] = target - nums[j]$$
我们可以从前往后枚举所有元素,遇到一个元素,我们就问哈希表“有没有\(target - nums[i]\)?”。如果有,我们直接返回;如果没有,我们更新货物。
// 哈希写法
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map<int, int> idx;
for (int i = 0; i < nums.size(); i ++) {
auto it = idx.find(target - nums[i]);
if (it != idx.end()) {
return {it->second, i}; // 语法糖
}
idx[nums[i]] = i;
}
return {};
}
};

浙公网安备 33010602011771号