哈希表,也叫散列表,是根据key value
而直接进行访问的数据结构。它把key value
通过散列函数(哈希函数)映射到表中一个位置来访问。
接下来列举一个场景:
f(张三)
,使得:f(张三)=存储位置(大锤)
通过对key
的映射,找到val
在数组中索引,就可以O(1)
的复杂度快速找到元素。
所有的key
组成了输入空间,哈希表的索引组成了输出空间,输入空间是大于输出空间的。所以经过哈希函数映射后,就是说不同的key
对应到了同一个索引。解决方法:
根据代码随想录中的总结:什么时候要使用哈希表呢?当我们要快速判断一个元素是否出现在集合里的时候,就要考虑哈希法。
题目链接:有效的字母异位词
若两个字符串为字母异位词,则两个字符串中的字母种类相同且个数相同,因此可以对两个字符串进行排序,如果排序后相同,则为异位词。
class Solution {
public:
bool isAnagram(string s, string t) {
sort(s.begin(), s.end());
sort(t.begin(), t.end());
if(s==t){
return true;
}
return false;
}
};
那我们可以首先统计s
中每个字符出现的次数,然后统计t
中每个字符出现的次数,再看两个相不相等。再进一步考虑,使用什么来存储字符出现的次数,可以使用数组。然后呢,比如说字母a
对应数组的哪一个索引,这样我们才能在字母a
出现时在数组中的对应位置元素+1
。可以使用哈希的思想,对每个字母-'a'
就可以找到唯一的索引。
我们也不需要同时对s
和t
做统计,先对s
做字母计数统计,然后对s
做统计时,对应位置--
。如果最后数组中的每个元素都为0,则s
和t
是异位词。
class Solution {
public:
bool isAnagram(string s, string t) {
int result[26] = {0};
for(int i=0; i<s.length(); i++){
result[s[i]-'a']++;
}
for(int j=0; j<t.length(); j++){
result[t[j]-'a']--;
}
for(int i=0; i<26; i++){
if(result[i] != 0){
return false;
}
}
return true;
}
};
题目链接:两个数组的交集
一个比较自然的想法,先统计nums1
中的元素,去重后存起来记为A
。然后遍历nums[2]
中的元素,看其是否在A
中,如果在A
中,则就是要返回的元素。然后一个粗糙的解法如下:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
set<int> result;
set<int> result1;
for(int i=0; i<nums1.size();i++){
if(find(result.begin(),result.end(), nums1[i]) == result.end()){
result.insert(nums1[i]);
}
}
for(int j=0; j<nums2.size(); j++){
if(find(result.begin(), result.end(), nums2[j]) != result.end()){
result1.insert(nums2[j]);
}
}
return vector<int>{result1.begin(),result1.end()};
}
};
还有一个更nice的写法:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result1(nums1.begin(), nums1.end());
unordered_set<int> result2;
for(int num:nums2){
if(result1.find(num) != result1.end()){
result2.insert(num);
}
}
return vector<int>(result2.begin(), result2.end());
}
};
有以下几个有点:
set
的底层是红黑树,查询和增删是O(logn)
,而unordered_set
的底层是哈系数,查询和增删是O(1)
begin和end
的这种方式对一个容器进行初始化find
,可以直接通过容器.find()
来查找元素题目链接:快乐数
这道题目正常模拟就可以了,但是有很重要的一点是:如果开始循环重复则肯定不是快乐数,返回false
。那么用unordered_set
来存储每次的结果,如果有哪一次出现在了unordered_set
中,则直接返回false。
class Solution {
public:
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;
}
// 如果这个sum出现过,说明已经陷入了循环,立刻return false
if(set.find(sum) != set.end()){
return false;
}
else{
set.insert(sum);
}
n = sum;
}
}
};
题目链接:两数之和
遍历,两个for
循环解决问题。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
for(int i=0; i<nums.size();i++){
for(int j=i+1; j<nums.size();j++){
if(nums[i]+nums[j] == target){
return {i,j};
}
}
}
return {};
}
};
在数组中找两个元素,其和等于target
。我们使用一个for
循环遍历每一个元素,把a + b = target
转换为b = target - a
。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> res;
for(int i=0; i<nums.size(); i++){
auto iter = res.find(target-nums[i]);
if(iter != res.end()){
return {iter->second, i};
}
else{
res.insert(pair<int,int>(nums[i],i));
}
}
return {};
}
};