算法之哈希表| 青训营
哈希表
理论基础哈希表
哈希表是一种根据关键码的值而直接进行访问的数据结构。它通过散列函数将关键码映射到数组的索引位置,从而实现快速的查找、插入和删除操作。一般哈希表都是用来快速判断一个元素是否出现在集合里。
牺牲了空间换取了时间
哈希表的优势在于其平均时间复杂度为 O(1),但在某些情况下,哈希碰撞可能会导致查找性能下降。
哈希碰撞
哈希碰撞是指两个不同的关键码被映射到了同一个索引位置的情况。在哈希表中,解决哈希碰撞的主要方法有拉链法和线性探测法。
拉链法
拉链法是解决哈希碰撞的常见方法之一。发生冲突的元素都被存储在同一个索引位置的链表中,每个索引位置维护一个链表结构。
线性探测法
线性探测法是另一种解决哈希碰撞的方法。当发生碰撞时,它会依次检查哈希表的下一个位置,直到找到一个空位为止。
常见的三种哈希结构
- 数组:哈希表可以使用数组来实现,数组的索引表示哈希值。
- Set(集合):哈希集合是不重复元素的集合,它可以快速判断元素是否存在。
- Map(映射):哈希映射是一种键值对的集合,可以根据键快速查找对应的值。
示例题目
1. 有效的字母异位词
给定两个字符串 s 和 t,编写一个函数来判断 t 是否是 s 的字母异位词。
解法: 使用哈希表统计字符串 s 中每个字符出现的次数,并在遍历字符串 t 时,对哈希表进行相应的减少。最后检查哈希表中的计数是否全为零。
暴力解法O(n^2)
两层for循环,同时还要记录字符是否重复出现
哈希表O(n)
只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。最后检查一下,record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。
bool isAnagram(string s, string t) {
unordered_map<char, int> count;
for (char c : s) {
count[c]++;
}
for (char c : t) {
count[c]--;
}
for (const auto& entry : count) {
if (entry.second != 0) {
return false;
}
}
return true;
}
2. 两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。
解法: 使用哈希表记录数组1中元素出现的次数,然后遍历数组2,找到数组1中存在的元素并加入结果集。
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
unordered_set<int> nums_set(nums1.begin(), nums1.end());
for (int num : nums2) {
// 发现nums2的元素在nums_set里又出现过
if (nums_set.find(num) != nums_set.end()) {
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());
}
3. 快乐数
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为1,或者无限循环但不为1。如果可以变为1,那么这个数就是快乐数。
解法: 使用哈希表记录每次计算的结果,判断是否出现循环或者得到1。
int getSum(int n) {
int sum = 0;
while (n) {
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> set;
while (1) {
int sum = getSum(n);
if (sum == 1) {
return true;
}
if (set.find(sum) != set.end()) {
return false;
} else {
set.insert(sum);
}
n = sum;
}
}
4. 两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回它们的数组下标。
解法: 使用哈希表记录每个元素的索引,遍历数组时查找是否存在目标值与当前元素的差值,如果存在,则找到了两个数的和为目标值。
cppCopy code
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> map; // key: 数组元素, value: 数组元素的索引
for (int i = 0; i < nums.size(); i++) {
int complement = target - nums[i];
if (map.find(complement) != map.end()) {
return {map[complement], i};
}
map[nums[i]] = i;
}
return {};
}
5. 四数相加II(哈希法)
给定四个包含整数的数组列表 A, B, C, D,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
解法: 使用两个哈希表,分别记录 A 和 B 两个数组中元素的和出现的次数。然后在遍历 C 和 D 两个数组时,查找是否存在目标值与当前元素的相反数 。
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
// 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
for (int a : A) {
for (int b : B) {
umap[a + b]++;
}
}
int count = 0; // 统计a+b+c+d = 0 出现的次数
// 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : C) {
for (int d : D) {
if (umap.find(0 - (c + d)) != umap.end()) {
count += umap[0 - (c + d)];
}
}
}
return count;
}
浙公网安备 33010602011771号