哈希表理论学习
哈希表是一种数据类型,通常在寻找一集合元素是否出现过时使用,其寻找指定元素的时间复杂度为O(1),非常之高效,c++中有数组,set,map来实现哈希表这一数据类型,具体内容还没有学到。
在C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:
集合 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
std::set | 红黑树 | 有序 | 否 | 否 | O(log n) | O(log n) |
std::multiset | 红黑树 | 有序 | 是 | 否 | O(logn) | O(logn) |
std::unordered_set | 哈希表 | 无序 | 否 | 否 | O(1) | O(1) |
std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
映射 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
std::map | 红黑树 | key有序 | key不可重复 | key不可修改 | O(logn) | O(logn) |
std::multimap | 红黑树 | key有序 | key可重复 | key不可修改 | O(log n) | O(log n) |
std::unordered_map | 哈希表 | key无序 | key不可重复 | key不可修改 | O(1) | O(1) |
242.有效的字母异位词
给定两个字符串,看其字符出现次数是否一样。
本题通过数组直接实现哈希表的表示,将各字符直接映射到对应数组下标中。
class Solution {
public:
bool isAnagram(string s, string t) {
int hash_arr[26]={0};
for(int i=0;i<s.size();i++)
{
hash_arr[s[i]-'a']++;
}
for(int j=0;j<t.size();j++)
{
hash_arr[t[j]-'a']--;
}
for(int k=0;k<26;k++)
{
if(hash_arr[k]!=0)
{
return false;
}
}
return true;
}
};
349. 两个数组的交集
给定两个数组,返回其交集,相同的元素只返回一个。
本题用到了set容器,其特性还需要学习,大概就是set容器只能存放一个相同的元素,如果没找到就返回set容器最后元素的迭代器。 将nums1的元素放入hash_set容器中,然后遍历nums2,找到相同元素返回即可,时间复杂度为O(n)。
#include<set>
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> hash_set(nums1.begin(),nums1.end());
unordered_set<int> result_set;
for(int num:nums2)
{
if(hash_set.find(num)!=hash_set.end())
result_set.insert(num);
}
return vector<int>(result_set.begin(),result_set.end());
}
};
202. 快乐数
题目讲解略。
本题主要就是计算各位的平方和,然后需要判断该平方和是否为1,为1返回true。如果不为1,则重新计算,需要知道的就是什么时候能知道他不是快乐数,也就是出现了之前出现过的各位平方和sum时,因此需要记录每次的sum值并且快速检查是否出现过,因此采用set实现的hash表数据来解决。具体用法及代码实现很简单,就是编程思路和对题目的理解需要明白。
int get_sum(int n)
{
int sum=0;
while(n)
{
sum+=(n%10)*(n%10);
n=n/10;
}
return sum;
}
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> hash_set;
int sum=0;
while(1)
{
sum=get_sum(n);
if(sum==1)
return true;
if(hash_set.find(sum)!=hash_set.end())
{
return false;
}
else
{
hash_set.insert(sum);
}
n=sum;
}
}
};
1. 两数之和
本题给定一个数值,和一个目标值,求数组中两个元素相加的和等于目标值的元素下标。正常做法是两层循环暴力解法。但可以采取一次遍历,每次遍历找目标和该元素的差值也就是能和该数组元素相加等于目标值的值,如果找不到,就把该元素值与下标存放起来。本题也是需要找元素,而且有数值和小标两个元素,因此map容器实现的hash表十分合适,map中的key用于存放数组元素,value用来存放下标。一次遍历数组即可,因此时间复杂度为O(n)。要注意的是,map中的key和value都在pair结构体中,因此加入插入元素的语法格式是map.insert(pari<数据类型,数据类型>(key,value));。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> hash_map;
for(int i=0;i<nums.size();i++)
{
auto iter =hash_map.find(target-nums[i]);
if(iter!=hash_map.end())
{
return {iter->second,i};
}
else
{
hash_map.insert(pair<int ,int>(nums[i],i));
}
}
return {};
}
};
哈希表总结
c++中STL提供了很多很好的容器,关于容器,算法和迭代器的知识得好好学学,在比赛中对速度提升很大。特别是hash以前感觉是很高级很难学的东西,今天学习后发现也没有那么难学,十分的好用且方便。总的来说,时候时候要用哈希表,就是需要查找一元素是否出现过的时候,然后使用什么样的数据结构来实现哈希表,得看具体情况自己斟酌,哈希表通常能把两次遍历的数据只需要一次遍历,在每次遍历处理数据即可解决,对于有速度限制的题目帮助真的很大。