1 哈希的基础知识
选择哈希:一般哈希表都是用来快速判断一个元素是否出现集合里。
哈希存储:【1】数组【2】set【3】map。
关于set——当我们要使用set来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set(set最主要的作用是自动去重并按升序排序),如果要求不仅有序还要有重复数据的话,那么就用multiset。
写的不错的博客:
2 哈希题(1)
class Solution {
public:
bool isAnagram(string s, string t)
{
int s_num[26];
int t_num[26];
for(int i = 0; i < 26; i++) //初始化一下
{
s_num[i] = 0;
t_num[i] = 0;
}
for(int i = 0; i < s.size();i++)
s_num[s[i] - 'a']++;
for(int i = 0; i < t.size();i++)
t_num[t[i] - 'a']++;
for(int i = 0; i < 26; i++)
if(s_num[i] != t_num[i])return 0;
return 1;
}
};
3 哈希题(2)
我的第一版AC 用set的版本,注意一个set find不到指定元素时候 返回的是set.end()迭代器。
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
{
// 本题可以用数组 现在尝试用一下set
set<int> set1;
set<int> set2;
for(int i = 0; i < nums1.size();i++)
set1.insert(nums1[i]);
for(int i = 0; i < nums2.size();i++)
set2.insert(nums2[i]);
vector<int> a;
for(auto x = set1.begin(); x != set1.end();x++)
{
int tmp = *x;
if(set2.find(tmp) != set2.end()) // find没找到的话 会返回set2.end()
a.push_back(tmp);
}
return a;
}
};
其实这样做不是最优的,应该用unordered_set !因为本题不用有序且unordered_set效率最高,所以本题unordered_set比set好。
4 哈希题(3)
本题的关键是看到题目中,判断非快乐数的条件是无限"循环"——即这个sum会重复出现!这时候判断sum是否在之前出现过,自然想到哈希表。
class Solution {
public:
bool isHappy(int n)
{
// 关键:不是快乐数的这个数会无限循环 就是会最少出现2次
unordered_set<int> out;
bool flag = 1; // 非快乐数的结束flag
while(n != 1)
{
vector<int> tmp;
tmp.clear();
// 获得n的每一位数
while(n)
{
tmp.push_back(n % 10);
n /= 10;
}
int sum = 0;
for(int i = 0; i < tmp.size();i++)
sum += tmp[i]*tmp[i];
if(out.find(sum) != out.end())// 找到重复了
return 0;
out.insert(sum);
n = sum;
}
return 1;
}
};
2024-4-11 二刷:
惊奇的发现如果没有set,设个最大100的max_ite也能过。但还是采用正经写法,更精简了如下:
class Solution {
public:
bool isHappy(int n)
{
int i = 0;
unordered_set<int> set;
while(n != 1 )
{
long long sum = 0;
while(n)
{
int unit = n%10;
sum += unit*unit;
n /=10;
}
n = sum;
if(set.find(n) == set.end())
set.insert(n);
else return false;
}
return true;
}
};
/*
19
1*1+9*9 = 82
8*8+2*2 = 68
6*6+8*8=100
2
2*2 = 4
4*4 = 16
1*1+6*6=37
3*3+7*7=9+49=58
5*5+8*8=25+64=89
8*8+9*9=64+81=145
*/
5 哈希题(4)
自己的原始想法unordered_set ,做着做着发现要加个map,就AC了。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target)
{
// 第一反应还是用unordered_set的做法 做着做着发现需要map
unordered_set<int> x;
map<int,int> y;
vector<int> out;
for(int i = 0; i < nums.size();i++)
{
x.insert(nums[i]);
y[nums[i]] = i;
}
for(int i = 0; i < nums.size();i++)
{
int rest = target - nums[i];
if(x.find(rest) != x.end()) // 有目标rest
{
if(y[rest] == i)continue; // 不能自己和自己相加
out.push_back(i);
out.push_back(y[rest]);
break;
}
}
return out;
}
};
看了题解发现,【1】这里的unordered_set 没必要用,map可以替代这个功能,且map还能存键值对。【2】应该用unordered_map 不用map ,因为这里不要求有序,前者底层是哈希表,更快一些。
直接放carl的代码:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map <int,int> map;
for(int i = 0; i < nums.size(); i++) {
// 遍历当前元素,并在map中寻找是否有匹配的key
auto iter = map.find(target - nums[i]);
if(iter != map.end()) {
return {iter->second, i};
}
// 如果没找到匹配对,就把访问过的元素和下标加入到map中
map.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
总结哈希的选择【重点】
选择哈希:
需要在一堆数里面,O(1)查找某数在不在这堆数里面。
选择数组:
【1】要查找的数据有范围 ,比如,小写字母表0-25 、题目告知的1-1000等等。
【2】数据在数组中分布比较密集。 如果是3个数:1,5,100000 的话,开100000就很浪费空间。
选择set:
【1】首选 unordered_set (去重,无序)
【2】需要 去重,有序 就用set
【3】需要 不去重,有序 就multiset
选择map:
【1】在set基础上,对于每个元素不能只存一个数,需要存键值对。选择unordered_map ,map,multimap 判断标准和set类似。
感受:
这样的题目复习一下STL 感觉效果最显著的!赞。总共用时:近3h。