算法思想之哈希表
欢迎拜访:雾里看山-CSDN博客
本篇主题:算法思想之哈希表
发布时间:2025.4.28
隶属专栏:算法
目录
- 算法介绍
- 哈希表作用
- 什么时候使用哈希表
- 怎么使用哈希表
- 例题
- 两数之和
- 题目链接
- 题目描述
- 算法思路
- 代码实现
- 判定是否互为字符重排
- 题目链接
- 题目描述
- 算法思路
- 代码实现
- 存在重复元素
- 题目链接
- 题目描述
- 算法思路
- 代码实现
- 存在重复元素 II
- 题目链接
- 题目描述
- 算法思路
- 代码实现
- 字母异位词分组
- 题目链接
- 题目描述
- 算法思路
- 代码实现
算法介绍
哈希表(Hash Table
)是一种基于键值对(Key-Value
)的高效数据结构,通过哈希函数将键映射到存储位置,实现快速数据访问。其核心优势是平均时间复杂度 O(1)
,广泛应用于缓存、数据库索引、字典等场景。
哈希表作用
快速查找某个元素,时间复杂度是O(1)
,空间复杂度是O(n)
。
什么时候使用哈希表
频繁的查找某一个数的时候
怎么使用哈希表
- 容器
C++中的哈希表是unordered_map
- 用数组模拟简易的哈希表
存储字符串中的字符的时候
数据范围很小的时候
例题
两数之和
题目链接
1. 两数之和
题目描述
给定一个整数数组
nums
和一个整数目标值target
,请你在该数组中找出 和为目标值target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
示例 1:输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
- 只会存在一个有效答案
算法思路
- 如果我们可以事先将数组内的元素和下标绑定在⼀起存入哈希表中,然后直接在哈希表中查找每一个元素的
target - nums[i]
,就能快速的找到目标和的下标。 - 这里有一个小技巧,我们可以不用将元素全部放入到哈希表之后,再来二次遍历(因为要处理元素相同的情况)。而是在将元素放入到哈希表中的同时,直接来检查表中是否已经存在当前元素所对应的目标元素(即
target - nums[i]
)。如果它存在,那我们已经找到了对应解,并立即将其返回。无需将元素全部放入哈希表中,提高效率。 - 因为哈希表中查找元素的时间复杂度是
O(1)
,遍历一遍数组的时间复杂度为O(N)
,因此可以将时间复杂度降到O(N)
。
这是一个典型的用空间交换时间的方式。
代码实现
class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> hash;for(int i = 0; i < nums.size(); i++){int x = target - nums[i];if(hash.count(x))return {i, hash[x]};elsehash[nums[i]] = i;}return {-1, -1};}
};
判定是否互为字符重排
题目链接
面试题 01.02. 判定是否互为字符重排
题目描述
给定两个由小写字母组成的字符串
s1
和s2
,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。
示例 1:输入: s1 = “abc”, s2 = “bca”
输出: true示例 2:
输入: s1 = “abc”, s2 = “bad”
输出: false说明:
0 <= len(s1) <= 100
0 <= len(s2) <= 100
算法思路
- 当两个字符串的长度不相等的时候,是不可能构成互相重排的,直接返回
false
; - 如果两个字符串能够构成互相重排,那么每个字符串中各个字符出现的次数一定是相同的。因此,我们可以分别统计出这两个字符串中各个字符出现的次数,然后逐个比较是否相等即可。这样的话,我们就可以选择哈希表来统计字符串中字符出现的次数。
代码实现
class Solution {
public:bool CheckPermutation(string s1, string s2) {if(s1.size() != s2.size())return false;int hash[128] = {0};for(auto c : s1)hash[c]++;for(auto c : s2)hash[c]--;for(int i = 0; i < 128; i++){if(hash[i] != 0)return false;}return true;}
};
存在重复元素
题目链接
217. 存在重复元素
题目描述
给你一个整数数组
nums
。如果任一值在数组中出现 至少两次 ,返回true
;如果数组中每个元素互不相同,返回false
。
示例 1:输入:nums = [1,2,3,1]
输出:true
解释:
元素 1 在下标 0 和 3 出现。示例 2:
输入:nums = [1,2,3,4]
输出:false
解释:
所有元素都不同。示例 3:
输入:nums = [1,1,1,3,3,4,3,2,4,2]
输出:true提示:
1 <= nums.length <= 105
-109 <= nums[i] <= 109
算法思路
分析一下题目,出现至少两次的意思就是数组中存在着重复的元素,因此我们可以无需统计元素出现的数目。仅需在遍历数组的过程中,检查当前元素是否在之前已经出现过即可。
因此我们可以利用哈希表,仅需存储数组内的元素。在遍历数组的时候,一边检查哈希表中是否已经出现过当前元素,一边将元素加入到 哈希表中。
代码实现
class Solution {
public:bool containsDuplicate(vector<int>& nums) {unordered_set<int> hash;for(auto x : nums){if(hash.count(x))return true;elsehash.insert(x);}return false;}
};
存在重复元素 II
题目链接
219. 存在重复元素 II
题目描述
给你一个整数数组
nums
和一个整数k
,判断数组中是否存在两个 不同的索引i
和j
,满足nums[i] == nums[j]
且abs(i - j) <= k
。如果存在,返回true
;否则,返回false
。
示例 1:输入:nums = [1,2,3,1], k = 3
输出:true示例 2:
输入:nums = [1,0,1,1], k = 1
输出:true示例 3:
输入:nums = [1,2,3,1,2,3], k = 2
输出:false提示:
1 <= nums.length <= 105
-109 <= nums[i] <= 109
0 <= k <= 105
算法思路
解决该问题需要我们快速定位到两个信息:
- 两个相同的元素;
- 这两个相同元素的下标。
因此,我们可以使用哈希表,令数组内的元素做 key
值,该元素所对应的下标做 val
值,将数组元素和下标绑定在一起,存入到哈希表中。
我们按照下标从小到大的顺序遍历数组,当遇到两个元素相同,并且比较它们的下标时,这两个下标一定是距离最近的,因为:
- 如果当前判断符合条件直接返回
true
,无需继续往后查找。 - 如果不符合条件,那么前一个下标一定不可能与后续相同元素的下标匹配(因为下标在逐渐变大),那么我们可以大胆舍去前一个存储的下标,转而将其换成新的下标,继续匹配。
代码实现
class Solution {
public:bool containsNearbyDuplicate(vector<int>& nums, int k) {unordered_map<int, int> hash;for(int i = 0; i < nums.size(); i++){if(hash.count(nums[i]))if(i-hash[nums[i]] <= k)return true;hash[nums[i]] = i;}return false;}
};
字母异位词分组
题目链接
49. 字母异位词分组
题目描述
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
示例 1:输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]]示例 2:
输入: strs = [“”]
输出: [[“”]]示例 3:
输入: strs = [“a”]
输出: [[“a”]]提示:
1 <= strs.length <= 104
-0 <= strs[i].length <= 100
strs[i]
仅包含小写字母
算法思路
互为字母异位词的单词有一个特点:将它们排序之后,两个单词应该是完全相同的。
所以,我们可以利用这个特性,将单词按照字典序排序,如果排序后的单词相同的话,就划分到同一组中。
这时我们就要处理两个问题:
- 排序后的单词与原单词需要能互相映射;
- 将排序后相同的单词,划分到同一组;
利用语言提供的「容器」的强大的功能就能实现这两点:
- 将排序后的字符串(
string
)当做哈希表的key
值; - 将字母异位词数组(
string[]
)当成val
值。
定义一个哈希表即可解决问题。
代码实现
class Solution {
public:vector<vector<string>> groupAnagrams(vector<string>& strs) {unordered_map<string, vector<string>> hash;for(auto &s : strs){string tmp = s;sort(tmp.begin(), tmp.end());hash[tmp].push_back(s);}vector<vector<string>> ret;for(auto &[x,y] : hash)ret.push_back(y);return ret;}
};
⚠️ 写在最后:以上内容是我在学习以后得一些总结和概括,如有错误或者需要补充的地方欢迎各位大佬评论或者私信我交流!!!