概述
文章目录
- 哈希表的定义
- 哈希表解决什么问题
- 哈希函数
- 哈希碰撞
- 拉链法
- 线性探测法
- 常见的三种哈希结构
- 有效的字母异位词
- 两个数组的交集
- 快乐数
- 两数之和
- 四数相加
- find用法
- count用法
- 赎金信
- 三数之和
- 四数之和
- 总结
哈希表的定义
哈希表是根据关键码的值而直接进行访问的数据结构
简单点说:数组就是一个哈希表,哈希表中的关键码就是数组的索引下标,然而通过下标直接访问数组中的元素。
哈希表解决什么问题
哈希表都是用来快速判断一个元素是否出现集合里
例如要查询一个名字是否在这所学校里。
要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
我们只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。
将学生姓名映射到哈希表上就涉及到了hash function ,也就是哈希函数。
哈希函数
哈希函数,把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。
通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。
如果hashcode得到的数值大于哈希表大小,也就是大于tableSize了,我们应该做个取模操作,这样就保证了学生姓名一定可以映射到哈希表上。
哈希碰撞
如果学生的数量大于哈希表的大小怎么办,此时就算哈希函数计算的再均匀,也避免不了会有几位学生的名字同时映射到哈希表 同一个索引下标的位置。
拉链法
刚刚小李和小王在索引1的位置发生了冲突,发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到小李和小王了
其实拉链法就是要寻找适当的哈希表的大小,这样既不会因为数组空值而大量浪费内存
线性探测法
使用线性探测法要保证tablesize大于datasize。我们需要依靠哈希表中的空位来解决碰撞问题
常见的三种哈希结构
-
数组
-
set集合
-
map
有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1: 输入: s = "anagram", t = "nagaram" 输出: true
示例 2: 输入: s = "rat", t = "car" 输出: false
说明: 你可以假设字符串只包含小写字母
class Solution {
public:
bool isAnagram(string s, string t) {
if (s.length() != t.length())
{
return false;
}
vector<int> table(26,0);
for(auto& ch : s)
{
table[ch-'a']++;
}
for(auto& ch : t)
{
table[ch-'a']--;
if(table[ch-'a']<0)
{
return false;
}
}
return true;
// if(s.length()!=t.length())
// return false;
// sort(s.begin(),s.end());
// sort(t.begin(),t.end());
// return s==t;
}
};
两个数组的交集
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
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());
// for (int num : nums2) {
//
if(nums_set.find(num) != nums_set.end()) {
//
result_set.insert(num);
//
}
// }
// return vector<int>(result_set.begin(),result_set.end());
vector<int> ans;
unordered_set<int> us{nums1.begin(),nums1.end()};
for(auto& iter : nums2) {
if(us.find(iter)!=us.end()) {
us.erase(iter);
ans.emplace_back(iter);
}
}
return ans;
}
};
快乐数
「快乐数」 定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
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;
}
if(set.find(sum)!=set.end()) {
return false;
}
set.insert(sum);
n=sum;
}
}
};
两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map <int,int> map;
for(int i = 0; i< nums.size();i++) {
//遍历当前元素,并在map中寻找是否有匹配的key
auto iter = map.find(target-nums[i]);
if(iter != map.end()) {
return {iter->second,i};
}
//如果没有找到匹配对,就把访问过的元素和下标加入到map中
map.insert(pair<int,int>(nums[i],i));
}
return {};
}
};
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// std::unordered_map <int,int> map;
// for(int i = 0; i< nums.size();i++) {
//
//遍历当前元素,并在map中寻找是否有匹配的key
//
auto iter = map.find(target-nums[i]);
//
if(iter != map.end()) {
//
return {iter->second,i};
//
}
//
//如果没有找到匹配对,就把访问过的元素和下标加入到map中
//
map.insert(pair<int,int>(nums[i],i));
// }
// return {};
unordered_map<int,int> hashtable;
for(int i = 0;i<nums.size();i++) {
auto it = hashtable.find(target-nums[i]);
if( it != hashtable.end()){
return {it->second,i};
}
hashtable[nums[i]]=i;
}
return {};
}
};
四数相加
给定四个包含整数的数组列表 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
解释:
两个元组如下:
(0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
(1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
//遍历A和B数组,统计两个数组元素之和,和出现的次数,放带map中
for (int a:nums1) {
for(int b:nums2){
umap[a+b]++;
}
}
int count = 0;
//统计a+b+c+d=0出现的次数
//在遍历C和D数组,找到-(c+d)=(a+b)的话,就把map中key对应的value也就是出现次数统计出来
for (int c:nums3){
for(int d : nums4)
{
if(umap.find(-(c+d)) !=umap.end()) {
count += umap[-(c+d)];
}
}
}
return count;
}
};
find用法
find()方法返回值是一个迭代器,成功返回迭代器指向要查找的元素,失败返回的迭代器指向end。
map.find(xx)!=map.end()=>在map中找到与xx相同的数
为什么是!= ?
因为如果相等说明map已经走到底end,使用没有走到底就说明之前已经找到了。
count用法
count()方法返回值是一个整数,1表示有这个元素,0表示没有这个元素。
赎金信
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
示例 1:
输入:ransomNote = "a", magazine = "b"
输出:false
public:
bool canConstruct(string ransomNote, string magazine) {
int a[26]={0};
if(ransomNote.size()>magazine.size())
return false;
for(auto & c : magazine){
a[c-'a']++;
}
for(auto & c : ransomNote){
a[c-'a']--;
if(a[c-'a']<0){
return false;
}
}
return true;
}
};
三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());
//找出a+b+c=0
//a=nums[i],b=nums[j],c=-(a+b)
for(int i = 0; i < nums.size(); i++) {
//排序之后如果第一个元素已经大于零那么就不可能凑到三元组
if(nums[i]>0)
return result;
//三元组去重a
if( i >0 && nums[i]== nums[i-1]) {
continue;
}
int left = i+1;
int right = nums.size()-1;
while(right > left) {
// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
if(nums[i] + nums[left] + nums[right]>0)
right--;
else if(nums[i] + nums[left] + nums[right]<0)
left++;
else{
result.push_back(vector<int>{nums[i],nums[left],nums[right]});
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重 while(right>left&&nums[right]==nums[right-1])right--;
while(right>left&&nums[left]==nums[left+1])left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
return result;
}
};
四数之和
题意:给定一个包含 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] ]
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
// 剪枝处理
if (nums[k] > target && nums[k] >= 0) {
break; // 这里使用break,统一通过最后的return返回
}
// 对nums[k]去重
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k + 1; i < nums.size(); i++) {
// 2级剪枝处理
if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
break;
}
// 对nums[i]去重
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if ((long) nums[k] + nums[i] + nums[left] + nums[right]
< target) {
left++;
} else {
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return result;
}
};
总结
最后
以上就是优雅唇彩为你收集整理的c++哈希表章节的全部内容,希望文章能够帮你解决c++哈希表章节所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复