我爱学算法之—— 哈希
哈希表高频算法题解析与实现(C++)
哈希表作为一种高效的数据结构,能以近似 O (1) 的时间复杂度实现查找、插入和删除操作,在解决 “两数之和”“重复元素判断”“字符异位词分组” 等经典算法问题中具有显著优势。
一、两数之和
题目解析

给定一个整数数组nums和一个目标值target,要求在数组中找到和为target的两个整数,最终返回这两个整数的下标。核心约束:数组中每个元素只能使用一次,且保证有且仅有一个有效答案。
算法思路
暴力解法
通过双重循环枚举所有可能的二元组,判断其和是否等于target。
- 外层循环遍历数组每个元素(下标
i),内层循环遍历i之前的所有元素(下标j,j < i); - 若
nums[i] + nums[j] == target,直接返回[j, i](需注意下标顺序)。 - 时间复杂度:O (n²)(双重循环),空间复杂度:O (1)(无额外空间消耗)。
哈希表优化
利用哈希表存储 “已遍历元素的值 - 下标” 映射,将内层循环的 O (n) 查找优化为 O (1):
- 遍历数组时,对当前元素
nums[i],计算其 “互补值”x = target - nums[i]; - 检查哈希表中是否存在
x:若存在,直接返回[哈希表中x的下标, i];若不存在,将当前元素nums[i]及其下标i存入哈希表; - 仅需遍历一次数组,且无需处理 “元素重复” 问题(因题目保证仅有一个答案,且互补值必在已遍历元素中)。
- 时间复杂度:O (n),空间复杂度:O (n)(哈希表存储最多 n 个元素)。
代码实现
class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {int n = nums.size();vector<int> ret(2);unordered_map<int, int> hash;for (int i = 0; i < n; i++) {int x = target - nums[i];if (hash.find(x) != hash.end()) {ret[0] = i;ret[1] = hash[x];break;}hash[nums[i]] = i;}return ret;}
};
二、判定是否互为字符重排
题目解析

给定两个仅由小写字母组成的字符串s1和s2,判断二者是否为字符重排(即两个字符串包含的字符种类和每种字符的数量完全相同,仅排列顺序可能不同)。例如:s1 = "abc"、s2 = "cba"是字符重排;s1 = "aab"、s2 = "abb"不是字符重排。
算法思路
字符重排的核心是 “字符种类和数量一致”,因此可通过计数哈希实现:
- 由于字符串仅包含 26 个小写字母,可使用大小为 26 的数组(替代哈希表)存储每种字符的出现次数,数组下标对应字母(
'a'对应 0,'b'对应 1,…,'z'对应 25); - 遍历
s1,统计每种字符的出现次数,存入数组count1; - 遍历
s2,统计每种字符的出现次数,存入数组count2; - 对比
count1和count2:若所有位置的计数均相等,则为字符重排;否则不是。
- 时间复杂度:O (n)(n 为两个字符串的最大长度),空间复杂度:O (1)(仅使用固定大小的数组)。
代码实现
class Solution {
public:int hash1[30];int hash2[30];bool CheckPermutation(string s1, string s2) {for (auto& e : s1)hash1[e - 'a']++;for (auto& e : s2)hash2[e - 'a']++;for (int i = 0; i < 26; i++) {if (hash1[i] != hash2[i])return false;}return true;}
};
三、存在重复元素
题目解析

算法思路
这个算法的思路其实很简单,就是用哈希集合来帮我们查有没有重复的数。
具体来说,就是逐个看数组里的每个数:
- 每拿到一个数,先去哈希集合里找找看有没有这个数
- 如果找到了,那就说明之前已经出现过这个数了,直接返回
true - 如果没找到,就把这个数放进哈希集合里,再看下一个数
- 等把所有数都看完了还没发现重复的,那就返回
false
代码实现
class Solution {
public:bool containsDuplicate(vector<int>& nums) {unordered_set<int> hash;for (auto& e : nums) {if (hash.find(e) != hash.end())return true;hash.insert(e);}return false;}
};
四、存在重复元素 II
题目解析

给定一个整数数组nums和一个整数k,判断数组中是否存在两个相等的元素,且它们的下标之差的绝对值不超过k(即abs(i - j) <= k,其中nums[i] == nums[j]且i != j)。例如:nums = [1,2,3,1]、k = 3返回true(下标 0 和 3 的差为 3,满足 <=3);nums = [1,0,1,1]、k = 1返回true。
算法思路
这个算法的思路挺好懂的,就是用一个哈希表(map)来记每个数字最后一次出现的位置,然后边遍历数组边检查:
- 遍历数组里的每个元素,记下当前元素的下标 i
- 对每个元素
nums [i],先看看哈希表里有没有它:- 如果有,就算算现在的下标 i 和哈希表里记录的那个下标差多少
- 要是这个差值小于等于 k,那就说明找到符合条件的两个重复元素了,直接返回 true
- 不管哈希表里有没有这个元素,最后都把当前元素和它的下标 i 存进哈希表(这样能保证表里存的总是这个元素最近出现的位置)
- 等把所有元素都看完了还没找到符合条件的,就返回 false
代码实现
class Solution {
public:bool containsNearbyDuplicate(vector<int>& nums, int k) {map<int, int> hash;int n = nums.size();for (int i = 0; i < n; i++) {if ((hash.count(nums[i]) != 0) && (i - hash[nums[i]] <= k)) {return true;}hash[nums[i]] = i;}return false;}
};
五、字母异位词分组
题目解析

给定一个字符串数组strs,将其中所有字母异位词分组(字母异位词定义同 “字符重排”,即字符种类和数量相同,排列顺序不同),最终返回分组后的二维数组。
算法思路
这道题我们可以将 排序后的字符串 作为key值,将字符串数组作为value值
- 对于数组里的每个字符串,先做一个副本并对副本进行排序
- 比如 “eat” 排序后是 “aet”,“tea” 排序后也是 “aet”,这样异位词就会有相同的排序结果
- 用一个哈希表(unordered_map)来存分组:
- 表的 key 就是排序后的字符串(比如 “aet”)
- 表的 value 是一个数组,专门存所有排序后等于这个 key 的原字符串(比如 [“eat”,“tea”,“ate”])
- 遍历完所有字符串后,哈希表里每个 value 就是一组字母异位词
- 最后把这些 value 收集起来,就是要返回的分组结果
代码实现
class Solution {
public:vector<vector<string>> groupAnagrams(vector<string>& strs) {unordered_map<string, vector<string>> hash;for (auto& e : strs) {string tmp = e;sort(tmp.begin(), tmp.end());hash[tmp].push_back(e);}// 构建结果vector<vector<string>> ret;for (auto& e : hash) {ret.push_back(e.second);}return ret;}
};
本篇文章到这里就结束了,感谢支持
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws
