均为C++实现
哈希表
哈希表是根据关键码的值而直接进行访问的数据结构。将值通过哈希函数映射到哈希表上存储,查找效率很高,时间复杂度为O(1),牺牲了空间换取了时间。
数组就是简单的哈希表,关键码为下标索引。
一般哈希表都是用来快速判断一个元素是否出现在集合里。
常见的三种哈希结构:
- 数组
- set
- map
详见代码随想录
242.有效的字母异位词
原题链接
本题用数组类型的哈希表即可解决,考虑到s 和 t 仅包含小写字母,利用ascii码构造哈希函数进行映射。
class Solution {
public:
bool isAnagram(string s, string t) {
int hash[26] = {0};
int i;
for (i = 0; i < s.size(); i++) {
hash[s[i] - 'a']++;
}
for (i = 0; i < t.size(); i++) {
hash[t[i] - 'a']--;
}
for (i = 0; i < 26; i++) {
if (hash[i] != 0)
return false;
}
return true;
}
};
容易想到的方法是开辟两个数组分别存储两个字符串的映射值然后遍历比较。
优化思路为开辟一个数组映射第一个字符串的字母数量,然后查找第二个字符串的映射减去即可。
然后遍历数组,有不为0的值说明s或t中有数量不匹配的字符。优化后空间复杂度更小一些。
349. 两个数组的交集
原题链接
同样是基于判断元素是否在集合中的题目,依然可以优先考虑使用哈希表。
上一题可以明确哈希表长度为26,本题0 <= nums1[i], nums2[i] <= 1000,哈希值少,跨度大,可以使用数组,但哈希表密度比较小,会浪费空间。如果没有数值限制是不能用数组的。
根据题意选择使用 unordered_set 这种数据结构最佳(底层使用哈希表实现,无需排序,不重复),映射、查询效率更高。
class Solution {
public:
vector<int> intersection(vector<int> &nums1, vector<int> &nums2) {
unordered_set<int> res;
unordered_set<int> set1(nums1.begin(), nums1.end()); //通过传入迭代器初始化
for (int num : nums2) {
if (set1.find(num) !=set1.end())
//find成功返回一个指向该元素的迭代器;
//反之,则返回一个指向容器中最后一个元素之后位置的迭代器
res.insert(num);
}
return vector<int>(res.begin(), res.end());
}
};
思路:
202.快乐数
原题链接
题目要求重复取位数的平方和直到为1或陷入无限循环。可以看出若重复出现同样的位数的平方和,就是陷入了无限循环。
因此可以套用上一题的思路,判断元素是否在集合中 。
//
class Solution {
public:
int getsum(int n) {//取位数
int sum = 0, digit = 0;
while (n) {
digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> res;
while (1) {
int sum;
sum = getsum(n);
if (sum == 1)
return true;
//如果出现相同的sum,说明进入了无限循环
//同样是基于查找元素是否出现在集合里的操作,使用set最佳
if (res.find(sum) != res.end())
return false;
else
res.insert(sum);
n = sum;
}
}
};
1. 两数之和
- 思路1:暴力法。
两层循环嵌套遍历寻找和为target的两个数。时间复杂度O(n2)
- 思路2:哈希表。
当我们遍历数组时,每遍历到一个新的元素,我们可以寻找在已经遍历过的元素中是否存在一个元素,可以使得两个元素和为target。这也是判断元素是否在集合中出现过,因此可以使用查询效率高的哈希表。
本题是返回两个和为target的元素的下标,因此我们需要保存的是两个东西:元素值+下标。使用数组或者set都不可行,我们应该选用存储键值对的map。这道题目中并不需要key有序,选择unordered_map 效率更高。
map中保存的是键值对,find方法的定义:
在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。
我们查找的是元素值,故存储元素值为key,下标为value。
class Solution {
public:
vector<int> twoSum(vector<int> &nums, int target) {
vector<int> res;
unordered_map<int, int> umap;
for (int i = 0; i < nums.size(); i++) {
if (umap.find(target - nums[i]) != umap.end()) {
res.push_back(umap.at(target - nums[i])); //找到 map 容器中 key 键对应的值
res.push_back(i);
break;//题目假设每种输入只会对应一个答案
} else {
umap.emplace(nums[i], i);
}
}
return res;
}
};
还可以不设置res数组,直接设置一个迭代器。
auto iter = map.find(target - nums[i]);
if(iter != map.end()) {
return {iter->second, i};
}