一、用数组来当作哈希表
二、用set来当作哈希表(unordered_set)
三、用map来当作哈希表(unordered_map)
四、三数之和、四数之和问题(找到不同的元组)(双指针法)
哈希表,是一种逻辑上的定义。通过哈希值可以找到对应需要的内容。例如:26个英文字母,每个英文字母作为一个关键字,可以分别对应一个数组的下标,数组中可以存放英文字母对应的东西,从而构造出哈希表,通过下标既可以找到对应的内容。
哈希表的实现方式:可以使用数组array实现,可以set或者map来实现。数组、集合、映射。set是集合(非重复),可以通过find()方法找到key是否在集合中,适合哈希表。map本身就是key - value的结构,也可以通过map容器的find()方法找到对应key的value,也很适合哈希表。
当哈希值有限时,例如26个英文字母,就可以直接使用数组来构建哈希表
当哈希值较大时,就使用set或者map,一般当只需要一个key时就用set,当需要两个值对应时就需要用到map。
No242.有效的字母异位词
力扣题目链接
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:输入: s = "anagram", t = "nagaram"输出: true
示例 2:输入: s = "rat", t = "car"输出: false
说明:你可以假设字符串只包含小写字母。
思路:
字母异位词的意义是,两个字符串所包含的字母应该完全一致。也就是说通过记录两个字符串中所含字母的数量是否一致,可以得出结论。
使用一个数组record[26] 作为哈希表,哈希表的索引就是数组的下标,通过关键字为a-z26个英文字母,分别对应0-25 这 26个哈希值。哈希表的内容就是数组的内容,也就是存放每个字母的个数。
遍历一次s, 通过record[s[i]-'a'],可以准确找到是哪个字母的次数应该增加。
遍历t时,减少对应字母的次数。
最后查看record数组,如果哪个字母的次数不为0,说明两个字符串不是字母异位词。
时间复杂度是O(n),2*O(n).
空间复杂度,只是定义了一个常量大小的辅助数组,所以空间复杂度是O(1)。
bool isAnagram(string s, string t) {
int record[26] = {0}; //创建记录字母个数的数组 26个字母,分别对应一个索引,0索引处是字母a的个数,25索引处是字母z的个数
//遍历s,找到某个字母就让记录它的个数+1
for (int i = 0; i < s.size();i++)
{
record[s[i] - 'a']++;
}
for (int j = 0; j < t.size();j++)
{
record[t[j] - 'a']--;
}
for (int k = 0; k < 26;k++)
{
if(record[k]!=0) //record数组中如果有的元素不是0,说明s和t一定是有谁多了字符或者少了字符。
{
return false;
}
}
//如果record全是0,则说明字符串s和t是字母异位词
return true;
}
2 . No1002. 查找共用字符
力扣题目链接
给你一个字符串数组 words ,请你找出所有在 words 的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。
示例 1:
输入:words = ["bella","label","roller"]输出:["e","l","l"]示例 2:
输入:words = ["cool","lock","cook"]输出:["c","o"]
提示:
1 <= words.length <= 1001 <= words[i].length <= 100words[i] 由小写英文字母组成
思路:
计算字符串数组中每个字符串中字母出现的个数。保存每个字母的最小的频数,如果某个字母最小出现的频数是0, 说明在某个字符串中这个字母没有出现过,说明它不是共用字符。如果频数是1,说明是共用字符,每个字符串中它最少出现一次。如果是2,每个字符串最少出现两次。
还是采用具有record[26]来当作哈希表,记录每个字母出现的频数。
vector commonChars(vector &words)
{
vector result; //创建结果数组
int hash[26] = {0}; //hash数组用来存放每个字符串中 每个字符出现的频率,没有出现就是0
//hash同时也是用来存放每个字符频率最小值的数组,最后就是需要将他转成string格格式输出
//用第一个字符串的字符频率来初始化hash数组
for (int i = 0; i < words[0].size();i++)
{
hash[words[0][i] - 'a']++;
}
//接下来就是把另外的字符串的字符频率放在另一个数组中
for (int i = 1; i < words.size();i++)
{
int hashother[26] = {0};
for (int j = 0; j < words[i].size();j++)
{
hashother[words[i][j] - 'a']++;
}
for (int k = 0; k < 26;k++)
{
hash[k] = min(hash[k], hashother[k]); //每次得到一个字符串中字符的频率,就和现有频率最小值 比较一下,选取最小的放到hash中,
}
}
for(int k = 0; k < 26;k++)
{
if(hash[k]!=0)
{
while(hash[k]--) //具有重复的字符,比如这个字符出现的最低频率是2,则每个字符串里都最少出现两次这个字符
{
string c = string(1, (char)(k + 97)); // string的一种构造函数,就是string(int n, char a); 强转类型 (char)val;
result.push_back(c);
}
}
}
return result;
}
No383. 赎金信
力扣题目链接
给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)
注意:
你可以假设两个字符串均只含有小写字母。
canConstruct("a", "b") -> false canConstruct("aa", "ab") -> false canConstruct("aa", "aab") -> true
思路
这道题目和242.有效的字母异位词很像,242.有效的字母异位词相当于求 字符串a 和 字符串b 是否可以相互组成 ,而这道题目是求 字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a。
本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点。
第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思” 这里说明杂志里面的字母不可重复使用。
第二点 “你可以假设两个字符串均只含有小写字母。” 说明只有小写字母,这一点很重要
代码如下:
bool canConstruct(string ransomNote, string magazine)
{
int hash[26] = {0};
for (int i = 0; i < magazine.size();i++)
{
hash[magazine[i] - 'a']++; // 记录字母出现的次数 加加
}
for (int j = 0; j < ransomNote.size();j++)
{
hash[ransomNote[j] - 'a']--; // 记录字母出现的次数 减减
}
for (int k = 0; k < 26;k++)
{
if(hash[k]<0) // 如果记录数出现负值,说明在magazine中没有,在ransomNote中有。所以magazine中的不能构成ransomNote
{ // 就应该返回false
return false;
}
}
return true;
}
No349 两个数组的交集
力扣题目链接
题意:给定两个数组,编写一个函数来计算它们的交集。
说明:输出结果中的每个元素一定是唯一的。我们可以不考虑输出结果的顺序。
思路:
这个题由于它不想前面两个是固定的哈希值,这个题的哈希值我们不知道是多好,所以就不能用数组,而需要使用set来进行构造哈希表。通过哈希表,我们可以用set.find()来找到对应元素。
找两个数组的交集,是需要找到两个数组的公共部分
首先把一个数组放入一个无序set中,set中的元素是不重复的,这道题不需要有序。然后遍历另一个数组,如果能在这个无序set中,找到另一个数组中的元素,那么这个元素就是两个数组的公共元素了。
代码如下:
vector intersection(vector &nums1, vector &nums2)
{
unordered_set result_set; //存放结果
unordered_set nums_set(nums1.begin(), nums1.end()); //unordered_set的拷贝构造函数unordered_set nums_set(st.begin(),st.end())
//将st begin()到end()范围的内容,拷贝给nums_set;
for(int num: nums2) //范围for循环,num是单个元素,nums2是一个可迭代的序列。
{ //如果发现在nums2中的元素在集合中有,那就
if (nums_set.find(num) != nums_set.end()) //find函数可以用于查找元素。find(key)查找key是否存在,如果存在,返回该元素的迭代器,如果不存在,返回set.end();
{
result_set.insert(num);
}
}
return vector(result_set.begin(), result_set.end()); //vector的拷贝函数。因为最终输出要求一个vector容器
}
No202 快乐数
力扣题目链接
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
示例:
输入:19 输出:true 解释: 1^2 + 9^2 = 82 8^2 + 2^2 = 68 6^2 + 8^2 = 100 1^2 + 0^2 + 0^2 = 1
思路:
因为每得到一个数,都需要做一个求平方和的操作,所以先封装一个函数求平方和。取出一个正整数的每一位上的数,先膜10,取出最后一位,再除10,去掉最后一位,使倒数第二位成为最后一位,继续进行,直到除10等于0。
本题的解题关键在于,如果不是快乐数一直求每一位平方和,总会出现循环。循环意味着会出现和之前相同的和。所以我们把每一次求和的结果保留下来存进哈希表中。如果出现了和集合中相同的元素,那么就return false。如果出现了1,那就返回true。
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 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;
}
}
};
经典哈希法!!!
当需要对应的两个值的时候,例如同时需要元素的值和下标,就需要map来存放,key是更重要一点的元素,需要通过key进行查找整个map。
No1 两数之和
力扣题目链接
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路:
两数之和的暴力解法就是双重for循环。
哈希法:
建立一个map,map的key是元素值,value是这个值在数组中的对应下标。
遍历数组,遍历nums[0], 在map中找是否存在key == target-nums[0] ,如果不存在,就把这个nums[0]和他的下标,存放在map之中。
继续遍历数组,遍历到nums[1],在map中找 是否 存在 key == target-nums[1],
存在对应的key,即可返回这两个数了。
注意: map的构造和定义! unordered_map
pair
代码如下:
vector twoSum(vector & nums, int target)
{
unordered_map mp;
for (int i = 0; i < nums.size();i++) //最多遍历一次数组即可得到结果。
{
auto c = mp.find(target-nums[i]); // 将差值保存一下
if(c!=mp.end()) // 如果在map的现有key中找到了这个c值,就说明已经找到了这两个数
{
vector v = {i, c->second};
return v;
}
mp.insert(pair(nums[i], i)); //如果在map的现有key中没有找到,就把它加入到map中
}
return {};
}
//或者这样写也一样的
vector twoSum(vector & nums, int target)
{
vector result;
unordered_map mp;
for (int i = 0; i < nums.size();i++)
{
if(mp.find(target-nums[i]!=mp.end())
{
result.push_back(i);
result.push_back(mp.find(target - nums[i])->second);
}
mp.insert(pair(nums[i], i));
}
return result;
}
No454 四数之和II
力扣题目链接
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -2^28 到 2^28 - 1 之间,最终结果不会超过 2^31 - 1 。
例如:
输入:A = [ 1, 2]B = [-2,-1]C = [-1, 2]D = [ 0, 2]输出:2
思路:
注意!!!!这道题只需要输出一共有多少个就可以,而不用得到是哪些元组。
将四个数组分成两部分,先使用双层for循环计算a+b的和,将其放进map中,map的key 是a+b的值,value是a+b出现的次数。
这样的话,如果后面找到了可以和a+b匹配为0的c和d,就可以直接统计个数。
利用和两数之和一样的思路,在遍历c和d时,使用target-(c+d)的方式,看现在的map中,是否有a+b可以匹配c+d 等于target。
int fourSumCount(vector &A ,vector &B,vector& C,vector & D)
{
unordered_map umap; // key;a+b的数值,value: a+b数值出现的次数
//遍历A和B数组,统计两数之和,并把他们都放在map中
for (int a: A)
{
for(int b:B)
{
umap[a + b]++;
}
}
//创建int变量count
int count = 0;
//遍历C和D数组,如果出现0-(c+d) 在map中的,就把这个value取出来。
for(int c:C)
{
for(int d:D)
{
if(umap.find(0-(c+d))!=umap.end())
{
count += umap.find(0 - (c + d))->second;
}
}
}
return count;
}
No 15 三数之和
力扣题目链接
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:[ [-1, 0, 1], [-1, -1, 2]]
思路:
// 这道题用哈希法不太合适,因为去重操作中有很多细节需要注意,再面试中很难写出没有bug的代码
// 使用双指针法
// 1. 首先将数组排序,有一层for循环,i从下标0的地方开始,同时定一个下标left定义在i+1的位置,定义下标right在数组结尾的位置
// 2. 相当于a = nums[i] b = nums[left] c=nums[right] 要找到a+b+c=0
// 3. 移动left和right的规则:
// 1. 如果nums[i]+nums[left]+nums[right]>0,由于已经是排序过后的数组了,说明此时三数之和大了,可以将right向左移动一下
// 2. 如果nums[i]+nums[left]+nums[right]<0,说明此时三数之和小了,left就向右移动,
// 直到,left和right相遇为止
// 时间复杂度 O(n2)
代码如下:
No 18 四数之和
力扣题目链接
题意:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。满足要求的四元组集合为:[ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2]]
思路:
代码如下:
声明: 本人是个小白。根据代码随想录 Carl哥 的攻略自己总结,思路都是代码随想录中的。
转载其标明原出处。