「算法刷题」哈希表的相关经典题目
LeetCode题目 | 相关题目类型 | 相关链接 |
---|---|---|
383 | 赎金信(简单难度) | 383. 赎金信 - 力扣(LeetCode) (leetcode-cn.com) |
1 | class Solution { |
类似题目:
LeetCode题目 | 相关题目类型 | 相关链接 |
---|---|---|
242 | 有效的字母异位词(简单难度) | 242. 有效的字母异位词 - 力扣(LeetCode) (leetcode-cn.com) |
数组其实就是一个简单哈希表,而且这道题目中字符串只有小写字符,那么就可以定义一个数组,来记录字符串s里字符出现的次数。
定义一个数组叫做record用来上记录字符串s里字符出现的次数。
1、需要把字符映射到数组也就是哈希表的索引下表上,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下表0,相应的字符z映射为下表25。
再遍历 字符串s的时候,只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。 这样就将字符串s中字符出现的次数,统计出来了。
2、那看一下如何检查字符串t中是否出现了这些字符,同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。
3、那么最后检查一下,record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。
最后如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。
时间复杂度为O(n),空间上因为定义是的一个常量大小的辅助数组,所以空间复杂度为O(1)。
1 | class Solution { |
知识补充:
1、c++ memset()函数
memset 函数是内存赋值函数,用来给某一块内存空间进行赋值的;
包含在<string.h>头文件中,可以用它对一片内存空间逐字节进行初始化;
原型为 :
void *memset(void *s, int v, size_t n);
这里s可以是数组名,也可以是指向某一内在空间的指针;
v为要填充的值;
n为要填充的字节数;
示例1:
1 | struct data |
示例2:
1 | char str[9]; |
示例3:
1 | int num[8]; |
2、 常见的 string 类构造函数有以下几种形式:
strings(num, c) //生成一个字符串,包含num个c字符
LeetCode题目 | 相关题目类型 | 相关链接 |
---|---|---|
1002 | 查找常用字符(简单难度) | 1002. 查找共用字符 - 力扣(LeetCode) (leetcode-cn.com) |
1 | class Solution { |
知识补充:
哈希数据结构:unordered_set
参考链接: C++ STL unordered_set容器完全攻略 (biancheng.net)
注意题目特意说明:输出结果中的每个元素一定是唯一的,也就是说输出的结果是去除了重复的元素, 同时可以不考虑输出结果的顺序
那么用数组来做哈希表也是不错的选择,例如242. 有效的字母异位词
但是要注意,使用数组来做哈希的题目,是因为题目都限制了数值的大小。
而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
此时就要使用另一种结构体了,set ,关于set,C++ 给提供了如下三种可用的数据结构:
- std::set
- std::multiset
- std::unordered_set
std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。
c++ unordered_set容器的成员方法:
find(key): 查找以值为 key 的元素,如果找到,则返回一个指向该元素的正向迭代器;反之,则返回一个指向容器中最后一个元素之后位置的迭代器(如果 end() 方法返回的迭代器)。
思路如图所示:
LeetCode题目 | 相关题目类型 | 相关链接 |
---|---|---|
349 | 两个数组的交集(简单难度) |
1 | class Solution { |
拓展
那有同学可能问了,遇到哈希问题我直接都用set不就得了,用什么数组啊。
直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。
不要小瞧 这个耗时,在数据量大的情况,差距是很明显的。
LeetCode题目 | 相关题目类型 | 相关链接 |
---|---|---|
350 | 两个数组的交集2(简单难度) | 350. 两个数组的交集 II - 力扣(LeetCode) (leetcode-cn.com) |
这道题和349不一样,没有要求 输出不能重复
1 | class Solution { |
LeetCode题目 | 相关题目类型 | 相关链接 |
---|---|---|
202 | 快乐数(简单难度) | 202. 快乐数 - 力扣(LeetCode) (leetcode-cn.com) |
题目中说了会无限循环,即在求和的过程中,sum会重复出现,这一点对解题很重要。
正如:关于哈希表,你该了解这些!中所说,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了。
这道题用哈希法,来判断这个sum是否重复出现,重复则return false, 否则一直找到sum为1为止。
判断sum是否重复出现就可以使用unordered_set。
1 | class Solution { |
LeetCode题目 | 相关题目类型 | 相关链接 |
---|---|---|
1 | (简单难度) | 1. 两数之和 - 力扣(LeetCode) |
则要使用map,那么来看一下使用数组和set来做哈希法的局限。
数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下表。
C++中map,有三种类型:
映射 底层实现 是否有序 数值是否可以重复 能否更改数值 查询效率 增删效率 std::map 红黑树 key有序 key不可重复 key不可修改 O(logn) O(logn) std::multimap 红黑树 key有序 key可重复 key不可修改 O(logn) O(logn) std::unordered_map 哈希表 key无序 key不可重复 key不可修改 O(1) O(1) std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看关于哈希表,你该了解这些!。
这道题目中并不需要key有序,选择std::unordered_map 效率更高!
解题思路:
例如:nums=[2,7,11,15],target=9
遍历数组
1、寻找target-nums[i]是否在map中,
2、没有,9-2=7,7不在map中,把2和对应下标0放进map中
3、有,9-7=2,2在map中,找到了数值2和7相加等于9,返回其下标
遍历结束后,没有返回值,则返回空的数组
参考链接: C++ STL unordered_map容器用法详解 (biancheng.net)
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map <int,int> map;//创建空的umap容器
for(int i = 0; i < nums.size(); i++) {
//查找target - nums[i]是否在map中,
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 {};
}
};
LeetCode题目 | 相关题目类型 | 相关链接 |
---|---|---|
49 | (中等难度) | 49. 字母异位词分组 - 力扣(LeetCode) (leetcode-cn.com) |
参考链接:https://blog.csdn.net/fuqiuai/article/details/83589593
解题思路:
1、对字符串每个词的字母按照字典序进行排序,异位词对字母进行排序后有相同的字母序列。
用排序后的字母序列作为map的key,将所有异位词都保存到字符串数组中,用map建立key和字符串数组之间的映射(不需要结果有序,所以用unordered map)
2、最后把归纳好的字符串数组存入结果res中。
1 | class Solution { |