文章目录
哈希表理论基础
1.哈希表
哈希表即为散列表。
哈希表是根据关键码的值而直接进行访问的数据结构。
什么时候使用哈希表呢?当需要快速判断一个元素是否出现集合里时。
2.哈希函数
查询信息与哈希表索引的映射关系则需要哈希函数来确定。
例如,把学生的姓名直接映射为哈希表上的索引。
通过特定的编码方式,把学生名字转化为数值,映射为哈希表的索引,这样就可以通过查询索引下标来快速确认该学生是否存在。
3.常见的三种哈希结构
-
数组
数组本身就是一种简单的哈希表。当数值比较小且数值可控时,可以使用数组作为哈希表,通过数组下标来直接访问数组中的元素。
-
set(集合)
数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
这时候就可以使用set作为哈希表,可以大大减少内存空间的浪费。
-
map(映射)
map 是一个key value 的数据结构。当同时需要索引和数值信息时,即使用map作为哈希表。
242_有效的字母异位词
题目链接:242. 有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
解法:用数组实现HashMap
-
解法
首先比较容易想到的就是暴力解法,两层for循环来记录重复字符出现的次数。
优化一下可以使用哈希表。
上面有提到数组就是一种简单的哈希表。本题数值较小且范围可控,所以可以使用数组来实现哈希表。
因为a~z为26个小写字母,因此定义一个大小为26的record数组,初始化为0,用来存放每个字母的出现次数。
遍历s字符串,将
s[i] - 'a'
所在的元素进行 +1 操作,证明s[i]
字符出现了一次;再遍历t字符串,将s[i] - 'a'
所在的元素进行 -1 操作。最后再遍历一遍record数组,如果数组中存在不为0的元素,则 s 和 t 不互为字母异位词。 -
代码展示
bool isAnagram(string s, string t) { int record[26] = { 0 }; for (int i = 0; i < s.size(); i++) { record[s[i] - 'a']++; } for (int i = 0; i < t.size(); i++) { record[t[i] - 'a']--; } for (int i = 0; i < 26; i++) { if (record[i] != 0) { return false; } } return true; }
-
复杂度分析:
- 时间复杂度:O(n)
- 空间复杂度:O(1)
相关题目:
- 383.赎金信
- 49.字母异位词分组
- 438.找到字符串中所有字母异位词
349_两个数组的交集
题目链接:349. 两个数组的交集
给定两个数组 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] 也是可通过的
解法一:用set实现HashMap
- 解法
nums1和nums2为题中所给数组。
- 定义两个set哈希表,分别是nums1_set和result,nums1_set用来存放nums1中的元素,result用来存放最终交集结果。
- 将nums1中元素存入nums1_set,记录了nums1中的元素,顺便完成了去重操作。
- 遍历nums2,找到与nums1_set中相同的元素,存入result。
-
代码实现
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { unordered_set<int> nums1_set(nums1.begin(), nums1.end()); unordered_set<int> res_set; for (int num : nums2) { if (nums1_set.find(num) != nums1_set.end()) { res_set.insert(num); } } return vector<int>(res_set.begin(), res_set.end()); }
-
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
解法二:用数组实现HashMap
-
解法
因为本题LeetCode上给了限制
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
这样就属于较小且可控的数值范围,所以也可以用数组来实现哈希表。
- 定义一个比1000稍大一点的数组作为哈希表,数组索引表示数值,对应值为1即为出现过;为0即为没出现过。
- 遍历nums1,把出现过的数组索引对应的值置为1
- 定义一个res_set,使用set,因为最终要去重
- 遍历nums2,如果对应哈希表中的值为1,则将索引值插入res_set
-
代码实现
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { unordered_set<int> res_set; int hash[1005] = { 0 }; for (int num : nums1) { hash[num] = 1; } for (int num : nums2) { if (hash[num] == 1) { res_set.insert(num); } } return vector<int>(res_set.begin(), res_set.end()); }
-
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
相关题目:
- 350.两个数组的交集 II
202_快乐数
题目链接:202. 快乐数
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
示例 1:
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例 2:
输入:n = 2
输出:false
解法一:用set实现HashMap
-
解法
存在两种可能:
-
最终值为1
-
最终进入循环
所以本题的关键就在于判断是否存在循环。
- 首先定义一个哈希表set,用来存放每一轮的sum值。
- 每次对当前的sum值进行判断,如果sum值为1,则是快乐数;如果不为1且不在set中,则继续计算;如果不为1且存在于set中,证明已经发生循环,不是快乐数。
-
-
代码展示
int getSum(int n) { // 求各位数平方和 int sum = 0; while (n) { sum += (n % 10) * (n % 10); n = n / 10; } return sum; } bool isHappy(int n) { unordered_set<int> sum_set; while (1) { int sum = getSum(n); if (sum == 1) { return true; } if (sum_set.find(sum) != sum_set.end()) { return false; } else { sum_set.insert(sum); } n = sum; } }
-
复杂度分析
- 时间复杂度:O(logn)
- 空间复杂度:O(logn)
1_两数之和
题目链接: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]
解法:用map实现HashMap
-
解法
- 为什么用哈希表?
-
为什么用map
对于本题来说,我们不仅要能确认数组对应的值,也要能返回数组下标,因此需要使用map,即key-value结构存放。
key——数组元素
value——数组下标
-
解题过程
- 代码展示
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> res_map; // 定义哈希表
for (int i = 0; i < nums.size(); i++) {
auto iter = res_map.find(target - nums[i]); // 查找对应元素是否出现过
if (iter != res_map.end()) { // 出现过,则返回{查找元素下标, 当前元素下标}
return { iter->second, i };
}
res_map.insert(pair<int, int>(nums[i], i)); // 没出现过,则把(下标,数值)插入到哈希表中
}
return {};
}
-
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)