代码随想录Day6 | 哈希表理论基础 242.有效的字母异位词349. 两个数组的交集 202. 快乐数1. 两数之和
哈希表基础
哈希表是根据关键码的值而直接进行访问的数据结构
其作用:判断一个元素是否出现在集合里
哈希函数和哈希碰撞
将一段数据映射到哈希表上的处理方法,但对于一个数组来说,其长度是有限的,如果数据元素个数大于其长度,会产生不同数据出现在统一索引位置这就是哈希碰撞。
哈希碰撞的解决方法:拉链法和线性检测法
- 拉链法:将冲突的元素存储在链表中,链表的起点就是被冲突的索引地址
- 线性探测法:哈希表的容量大于数据个数,利用冲突位置的下一个空位置来存放冲突元素。
三种哈希结构
- 数组
对于映射长度可控的可以使用,比如英文字母 - set集合
代码随想录
- map映射
代码随想录
- 使用场景
优先使用unordered_set,利用其较好的查找删除效率,如果要求集合是有序的那么可以利用set,如果集合中需要存在重复数据那么就利用multiset
有效的字母异位词
文档讲解:代码随想录
视频讲解: 学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词
状态:×
- 思路
由于对于小写字母来说,只有26个,所以可以利用数组来充当哈希表,那么如何构造哈希函数–将字母变为数字下标,利用字母的ASCII码值即可,char-‘a’。
遍历s的时候对下标对应的值进行+1操作,在遍历t的时候对下标对应的值进行-1操作。
最后的检测就是数组中的数据是否全为0。
class Solution {
public:
bool isAnagram(string s, string t) {
vector<int> check(26,0);
for(int i = 0;i<s.length();i++)
{
check[s[i]-'a']++;
}
for(int i = 0;i<t.length();i++)
{
check[t[i]-'a']--;
}
for(int i = 0;i<26;i++)
{
if(check[i] != 0)
{
return false;
}
}
return true;
}
};
时间复杂度: O ( 3 n ) O(3n) O(3n)
两个数组的交集
文档讲解:代码随想录
视频讲解: 学透哈希表,set使用有技巧!Leetcode:349. 两个数组的交集
状态:可以用数组,不会set
- 数组思路
与上一题类似,因为元素的个数最多是1001个,所以可以考虑使用数组。那么就i需要自己构建哈希函数,我们将元素值作为哈希表的索引值即可。
但这道题还需要考虑一些情况,因为这道题是需要找出相同的数,并且不重复的输出。
那么上一道题利用等于一个val的判断就不适用了,因为初始化哈希数组,会将所有元素变为这个val。第二点是不重复,如果我们在第一数组进行映射的时候还是将索引对应的值++,那么最后判断的时候会出现重复,或者避免了重复会出现结果为空,比如nums2中只有1个值,而nums1中有多个。
所以做法是,每次映射值的时候将索引对应的值变为1,然后找到值进行–操作,这样当哈希数组中的值变为0的时候,那说明存在相交,且这个值只会被加入一次,因为之后再存在就是负数了。
对于避免加入本来就不存在数的情况,将返回数组的更新放在检查第二个数组的循环里面进行。
具体代码如下
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> res;
vector<int> check(1001);
for(int i = 0;i<nums1.size();i++)
{
check[nums1[i]] = 1;
}
for(int i = 0;i<nums2.size();i++)
{
check[nums2[i]]--;
if(check[nums2[i]] == 0)
{
res.push_back(nums2[i]);
}
}
return res;
}
};
- 哈希表思路
对于不重复且不排序就考虑使用unordered_set<>
//定义哈希表
unordered_set<TYPE> Name;
//定义同时赋值
unordered_set<TYPE> Name(list1.begin(),list1.end());
//查找元素是否出现
Name.find(num) != Name.end() //当num对应的位置不是最后一个说明存在
//插入元素
Name.insert(num);
本题的具体代码
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> res;//用于去重
unordered_set<int> check(nums1.begin(),nums1.end());//用于存储检测
for(int num:nums2)
{
if(check.find(num) != check.end())
{
res.insert(num);
}
}
return vector<int>(res.begin(),res.end());
}
};
快乐数
文档讲解:代码随想录
视频讲解:
状态:×
- 思路
无限循环说明会有重复的结果出现,可以考虑使用哈希表,n比较大所以使用数组不再合适,使用unordered_set当之后出现的数在set里面发现,那么就可以返回false并终止循环。
本题代码如下
class Solution {
public:
bool isHappy(int n) {
//无限循环说明会有重复
unordered_set<int> check;
while(1)
{
//每次开始循环之前初始化平方和结果
int final = 0;
//计算平方和结果
while(n)
{
final += (n%10)*(n%10);
n = n/10;
}
//将平方和结果变为下一次循环的n
n = final;
//当平方和结果为1 返回true跳出循环
if(final == 1)
{
return true;
break;
}
//当平方和结果之前存在,返回false跳出循环
if(check.find(final) != check.end())
{
return false;
break;
}
//当平方和结果不为1,且不存在添加到set中
else
{
check.insert(final);
}
}
}
};
时间复杂度: O ( l o g n ∗ 1 ) O(logn * 1) O(logn∗1) logn是求平方和的时间
两数之和
文档讲解:代码随想录
视频讲解: 梦开始的地方,Leetcode:1.两数之和,学透哈希表,map使用有技巧!
状态:只会暴力
- 思路
不重复出现,说明我们需要利用unordered来存放满足条件的值。
返回下标,不是返回值,说明我们需要存放一个类似字典的数据(val,index)
所以考虑使用map
//返回一个数组
return {a,b};
//map的一些操作
//定义
unordered_map<int,int> check;
//定义查找的变量利用auto pair<int,int>
auto i= check.find(target);
//pair<int,int>第一个数据存放的是我们的target,第二个数据存放的是我们的下标,map去find的时候,是去搜寻第一个值,所以第一个值应当为我们的target
//获取方式
i->first;
i->second;
//插入一组数据,与查询相对应
check.insert(pair<int,int>(nums[i),i);
本题代码如下
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> check;
for(int i = 0;i<nums.size();i++)
{
int mubiao = target-nums[i];
if(check.find(mubiao) != check.end())
{
return {i,check.find(mubiao)->second};
}
else
{
check.insert(pair<int, int>(nums[i], i));
}
}
return {};
}
};