什么时候想到用哈希法,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。 这句话很重要。
LeetCode242.有效的字母异位词
题目链接:https://leetcode.cn/problems/valid-anagram/
思路:定义一个数组叫做record用来记录字符串s里字符出现的次数。把字符映射到数组也就是哈希表的索引下标上,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。
在遍历字符串s的时候,**只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。**这样就将字符串s中字符出现的次数,统计出来了。
接下来在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。
最后检查一下,record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。
C++代码如下:
class Solution
{
public:
bool isAnagram(string s, string t)
{
int record[26] = {0};
for(int i = 0; i < s.size(); i++)
{
record[s[i] - 'a']++;
}
for(int i = 0; i < t.size(); i++)
{
record[t[i] - 'a']--;
}
for(int i = 0; i< 26; i++)
{
if(record[i] != 0)
{
return false;
}
}
return true;
}
};
LeetCode349.两个数组的交集
题目链接:https://leetcode.cn/problems/intersection-of-two-arrays/
思路:看到该题的第一想法是:把nums1中的元素存在set类型的变量s1中,再在s1中找是否存在nums2中的元素,有的话存在一个vector类型变量result中,那么result里存放的就是nums1和nums2共同有的元素,即交集。
C++代码如下:
class Solution
{
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
{
set<int> s1;
vector<int> result;
for(int i = 0; i < nums1.size(); i++)
{
s1.insert(nums1[i]); //插入数据
}
for(int i = 0; i < nums2.size(); i++)
{
set<int>::iterator pos = s1.find(nums2[i]); //定义一个迭代器来接收find()的返回值
if(pos != s1.end())
{
result.push_back(*pos);
}
}
//此时得到的result也算是正确的,但是还没去重,下面部分代码功能是去重。
set<int> s2;
vector<int> res1;
for(int i = 0; i < result.size(); i++)
{
s2.insert(result[i]);
}
for(set<int>::iterator it = s2.begin(); it != s2.end(); it++)
{
res1.push_back(*it);
}
return res1;
}
};
分析:总感觉自己写的有些太啰嗦了,看了卡哥写的,发现是自己的简洁版。我写的代码中,在查找s1中是否存在nums2里的元素时,是把查找结果存在vector类型的result变量中,后面又把result变量中的元素存在set类型的s2中,并且去重之后又把结果存在vector类型的res1变量中再输出。卡哥的做法是:在查找nums_set中是否存在nums2里的元素时,直接把结果存在unordered_set类型的变量result_set中,然后直接输出该变量,而我是先经过vector类型再存在set类型中,而且最后输出之前还是转成vector再输出,就显得很啰嗦冗余。
C++代码如下:
class Solution
{
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
{
unordered_set<int> result_set; //存放结果
unordered_set<int> nums_set(nums1.begin(), nums1.end()); //用nums容器中的元素为新建的nums_set容器初始化。
for (int num : nums2)
{
//发现nums2的元素 在nums_set里又出现过
if (nums_set.find(num) != nums_set.end()) //说明在nums_set中找到num
{
result_set.insert(num); //将出现的数插入result_set中
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
LeetCode202.快乐数
题目链接:https://leetcode.cn/problems/happy-number/
思路:看到这题的第一想法是:把输入的数由int转为string类型,再一位一位的平方相加,再判断是不是1,是的话返回true,否则继续一位一位平方相加;但是题目说了有可能无限循环,所以定义一个哈希表set或unordered_set将之前相加后的结果存进去,之后的每次数字平方和结果还要再在哈希表内查找一遍看是否存在,若存在就会陷入无限循环,直接return false。
思路其实蛮简单的,但是在测试过程中一直出错,后面经过debug找出原因,需要注意以下几点:
每次计算一个正整数的每个位置上数字的平方和,我是使用int型变量result来接收结果,再做后续的判断以及存储在哈希表中。但是在下一回合计算每个位置上数字的平方和之前,记得要把result归零、置位,否则它会把之前的结果累加进去,那就报错了。
输入的是int型变量,由于我打算计算该变量的每个位置上数字的平方和,所以我的做法是把int型变量转为string型,再分别计算string型变量上每一位置的数字的平方和,但是,我忘记一件事情,就是string变量上的每一位都是char型,而不是int型,即它们是char型的’1’而不是int型的’1’,所以我直接s1[i]*s1[i]就变成两个char型变量相乘,这就出错了!后来,我改成用char型变量接收s1[i],再用强制转换把char转为int,再计算两个int型相乘即可,如下图所示:
char tmp01 = s1[i];
int tmp = (int)tmp01 - 48; //因为ascii码的数字(0)从48开始
result += tmp * tmp;
同时要注意的是,ascii码的数字‘0’是从48开始,也就是说char tmp01 = ‘0’; int tmp = (int)tmp01其实是48,所以后面要减去48,为了让tmp从0开始。
C++代码如下:
class Solution {
public:
bool isHappy(int n)
{
unordered_set<double> set1;
set1.insert(n);
string s1 = to_string(n);
int result = 0;
while (1)
{
result = 0;
for (int i = 0; i < s1.size(); i++)
{
char tmp01 = s1[i];
int tmp = (int)tmp01 - 48; //因为ascii码的数字(0)从48开始
result += tmp * tmp;
}
if (result == 1)
{
return true;
}
else
{
unordered_set<double>::iterator pos = set1.find(result);
if (pos != set1.end()) //找到了
{
return false;
}
else
{
set1.insert(result); //存在哈希表中
s1 = to_string(result);
}
}
}
}
};
分析:后来再想,原思路虽然是正确的,但是int型和char型转来转去太麻烦了,容易出错,后面参考卡哥的写法,对输入int型参数使用 % 与 / ,就可以得到该int型变量的个位、十位、百位等等,还不容易出错。
C++代码如下:
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;
}
}
};
LeetCode1.两数之和
题目链接:https://leetcode.cn/problems/two-sum/
思路:想法是使用哈希表,使用unorderewd_map,一开始我的想法是key存放下标,value存放元素,但是使用find()函数的话,该函数是查找key是否存在,可是我需要得到的结果就是下标,所以后来改成key存放元素,value存放下标,通过查找需要的元素来返回对应的下标。
C++代码如下:
class Solution
{
public:
vector<int> twoSum(vector<int>& nums, int target)
{
vector<int> result;
unordered_map<int, int> mp1;
for(int i = 0; i < nums.size(); i++)
{
unordered_map<int, int>::iterator pos = mp1.find(target-nums[i]);
if(pos != mp1.end()) //找到了
{
result.push_back(i);
result.push_back((*pos).second);
return result;
}
else
{
mp1.insert(pair<int, int>(nums[i], i));
}
}
return {};
}
};
总结:整体上比链表容易,但是与set和map相关的语法函数再看看。