第三章 哈希表 part01
哈希表基础(主要为map):
此基础是我根据网上的内容和自己写的结合的
1. 常见函数
begin(),end()函数:
begin( )函数:该函数返回一个指向哈希表开始位置的迭代器
end( )函数:作用于begin函数相同,返回一个指向哈希表结尾位置的下一个元素的迭代器
unordered_map<int, int>::iterator iter = hmap.begin(); //begin
//申请迭代器,并初始化为哈希表的起始位置
cout << iter->first << ":" << iter->second;
unordered_map<int, int>::iterator iter = hmap.end();//end
注:cbegin() 和 cend():这两个函数的功能和begin()与end()的功能相同,唯一的区别是cbegin()和cend()是面向不可变的哈希表
empty()函数:
判断哈希表是否为空,空则返回true,非空返回false
bool isEmpty = hmap.empty();
size()函数:
返回哈希表的大小
int size = hmap.size();
erase()函数:
删除某个位置的元素,或者删除某个位置开始到某个位置结束这一范围内的元素, 或者传入key值删除键值对
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int>::iterator iter_begin = hmap.begin();
unordered_map<int, int>::iterator iter_end = hmap.end();
hmap.erase(iter_begin); //删除开始位置的元素
hmap.erase(iter_begin, iter_end); //删除开始位置和结束位置之间的元素
hmap.erase(3); //删除key==3的键值对
at()函数:
根据key查找哈希表中的元素
int elem = hmap.at(3);
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
clear()函数:
清空哈希表中的元素
hmap.clear();
find()函数:
以key作为参数寻找哈希表中的元素,如果哈希表中存在该key值则返回该位置上的迭代器,否则返回哈希表最后一个元素下一位置上的迭代器
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int>::iterator iter;
iter = hmap.find(2); //返回key==2的迭代器,可以通过iter->second访问该key对应的元素
if(iter != hmap.end()) cout << iter->second;
2. 遍历:
unordered_map<int,int> map;
map[1] = 11;
map[2] = 22;
//第一种遍历方法 it是对象 用 . 运算符
for (auto it : map)
cout<<it.first;
//第二种遍历方法 it是指针 用 -> 运算符
cout<<endl;
unordered_map<int,int>::iterator it;
for(it=map.begin();it!=map.end();it++){
cout<<it->first;
}
3. 初始化:
//法1
unordered_map<int, int> hmap;
unordered_map<int, int> hmap{ {1,11},{2,12},{3,13} };
//如果知道要创建的哈希表的元素个数时,也可以在初始化列表中指定元素个数
unordered_map<int, int> hmap{ {{1,11},{2,12},{3,13}},3 };
//法2(复制构造)
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int> hmap1(hmap);
4. 插入:
//法1
//当我们想向哈希表中添加元素时也可以直接通过下标运算符添加元素,格式为: mapName[key]=value;
//如:hmap[4] = 14;
//但是这样的添加元素的方式会产生覆盖的问题,也就是当hmap中key为4的存储位置有值时,
//再用hmap[4]=value添加元素,会将原哈希表中key为4存储的元素覆盖
hmap[4] = 14;
hmap[4] = 15;
cout << hmap[4]; //结果为15
//法2
//通过insert()函数来添加元素的结果和通过下标来添加元素的结果一样,不同的是insert()可以避免覆盖问题,
//insert()函数在同一个key中插入两次,第二次插入会失败
hmap.insert({ 5,15 });
hmap.insert({ 5,16 });
cout << hmap[5]; //结果为15
LeetCode 242.有效的字母异位词
思路:建立两个数组,由于s
和 t
仅包含小写字母,所以设数组hash长为26,hash[0]就代表在字符串s中字母a出现的次数,26个位置每一位都代表一个小写字母,里面的值是在s中对应字母出现的次数,之后再遍历一遍t字符串,对应减去一次hash表中对应字母的值,最后如果两个字符串是满足条件,则hash中应全为0。
完整C++代码如下:
//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
public:
bool isAnagram(string s, string t) {
int hash[26]={};
for(auto c:s){
hash[c-'a']++;
}
for(auto c:t){
hash[c-'a']--;
}
for(int i=0;i<26;i++){
if(hash[i]!=0)return false;
}
return true;
}
};
LeetCode 349.两个数组的交集
思路:
- 哈希法
由于返回的交集中每个元素唯一,所以不考虑重复的数,如下例子:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
所以采用unordered_set集合形式(其不记录重叠的数),(用nums_set)存储nums1中的数,之后遍历nums2,如果在nums_set中找到了nums2中的数,将该数塞到res数组中,继续nums2中下一个数。
- 排序+双指针遍历
-
先将两个数组分别非递减排序
-
之后设两个指针p,q从头开始比较
-
哪一个指针指向位置的数小,哪一个指针就往后移动,当两个指针指向的数大小相等,就将其插入res数组中,并同时往后移,直到新处的位置上的数比之前插入的数大(避免重复数插入,可以调用vector的back函数)
完整C++代码如下:
//1
//时间复杂度:O(m + n),其中 m 和 n 分别为 nums1 和 nums2 的长度
//空间复杂度:O(m),其中 m 为 nums1 中不重复的元素个数
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> nums_set(nums1.begin(),nums1.end());//存储nums1
unordered_set<int> result_set;
for(int num:nums2){
if(nums_set.find(num)!=nums_set.end())//判断nums_set中有没有要找的数
result_set.insert(num);
}
return vector<int>(result_set.begin(),result_set.end());
}
};
//2
//时间复杂度:O(mlogm + nlogn),其中 m 和 n 分别为 nums1 和 nums2 的长度
//空间复杂度:O(1)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
sort(nums1.begin(), nums1.end());//排序
sort(nums2.begin(), nums2.end());
int length1 = nums1.size(), length2 = nums2.size();
int index1 = 0, index2 = 0;
vector<int> res;
while (index1 < length1 && index2 < length2) {
if (nums1[index1] == nums2[index2]) {
// 保证加入元素的唯一性
if (!res.size() || nums1[index1] != res.back()) {
res.push_back(nums1[index1]);
}
index1++;
index2++;
} else if (nums1[index1] < nums2[index2]) {
index1++;
} else {
index2++;
}
}
return res;
}
};
LeetCode 202.快乐数
思路:
-
哈希表存储每次得到的平方和,每轮的新的平方和都和哈希表中已经存储的数比较,若为1,return true,若有相同的return false
-
循环就想快慢指针法,使用 “快慢指针” 思想,找出循环:“快指针” 每次走两步,“慢指针” 每次走一步,当二者相等时,即为一个循环周期。此时,判断是不是因为 1 引起的循环,是的话就是快乐数,否则不是快乐数。
完整C++代码如下:
//1
//时间复杂度:取决于数字 n 的位数,因为最大迭代次数取决于 n 的位数。假设 n 有 d 位,则最大迭代次数为 O(d * log(n)),其中 log(n) 是 n 的位数。
//空间复杂度:O(d),即哈希集合所需的空间,其中 d 是 n 的位数。
class Solution {
public:
bool isHappy(int n) {
unordered_set<int>set;
while(1){
int now_num=0;
while(n>0){
now_num+=pow(n%10,2);
n/=10;
}
n=now_num;
if(now_num==1)return true;
if(set.find(now_num)!=set.end())return false;
set.insert(now_num);
}
}
};
//2
//时间复杂度:取决于数字 n 的位数 d ,最大迭代次数为 O(d * log(n))。
//空间复杂度:O(1)
class Solution {
public:
int getNext(int n) {
int sum = 0;
while (n > 0) {
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
bool isHappy(int n) {
int slow = n;
int fast = getNext(n);
while (fast != 1 && slow != fast) {
slow = getNext(slow);
fast = getNext(getNext(fast));
}
return fast == 1;
}
};
LeetCode 1.两数之和
思路:
-
哈希
用map存储,思路很直观,和前几道区别不大。
-
排序+双指针
- 先创建一个vector<pair<int,int>>型数组pair_num,第一个数存原数组的数,第二个数存下标,再进行pair_num的快排(sort调用)(注:pair调用sort时默认排第一个数非递减,遇到别的情况时可以使用lambda表达式重写规则)
- 设两个指针在pair_num两端,向中间移动,当两指针指向位置的数的和为target时,返回指向位置数的second(该位置pair的第二个数)
完整C++代码如下:
//1
//时间复杂度:O(n)
//空间复杂度:O(n)
class Solution {
public:
vector<int>twoSum(vector<int>&nums,int target){
unordered_map<int,int>map;
for(int i=0;i<nums.size();i++){
auto iter=map.find(target-nums[i]);
if(iter!=map.end())return {iter->second,i};
map.insert(pair<int,int>(nums[i],i));
}
return {};
}
};
//2
//时间复杂度:O(nlogn)
//空间复杂度:O(n)
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int _size=nums.size();
vector<pair<int,int>> pair_num(_size);
for(int i=0;i<_size;i++){
pair_num[i].first=nums[i];
pair_num[i].second=i;
}
sort(pair_num.begin(),pair_num.end());
int p=0,q=_size-1;
while(1){
if(pair_num[p].first+pair_num[q].first==target)return {pair_num[p].second,pair_num[q].second};
else if(pair_num[p].first+pair_num[q].first<target)p++;
else q--;
}
}
};