第五天周日休息啦(没有day5)
哈希表理论基础
文章链接:代码随想录
哈希表定义:哈希表是根据关键码的值(key)而直接进行访问的数据结构。
可能这样的定义有点笼统,简单来说就是一种快捷的便于查找的数据结构,比方说在数组里找一个元素,要遍历的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
本文主要围绕哈希表怎么使用展开,关于哈希表的原理就不解释了。
解题中主要用到的三种哈希结构(本文的下面力扣题会包含三种结构使用场景的题目):
- 数组 (对应题号242)
- set(集合) (对应题号349,202)
- map(映射) (对应题号1)
数组就没什么可分类的了,主要是c++中set和map各有不同的三个结构可用:
集合 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
std::set | 红黑树 | 有序 | 否 | 否 | O(log n) | O(log n) |
std::multiset | 红黑树 | 有序 | 是 | 否 | O(logn) | O(logn) |
std::unordered_set | 哈希表 | 无序 | 否 | 否 | O(1) | O(1) |
std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
映射 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
std::map | 红黑树 | key有序 | key不可重复 | key不可修改 | O(logn) | O(logn) |
std::multimap | 红黑树 | key有序 | key可重复 | key不可修改 | O(log n) | O(log n) |
std::unordered_map | 哈希表 | key无序 | key不可重复 | key不可修改 | O(1) | O(1) |
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。
需要注意的点:
1.当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。(选择三种映射结构的map也是一个道理,看是否有序和重复选择)
2.当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
3.使用哈希表就牺牲了空间换取了时间。
力扣题部分
242.有效的字母异位词
题目链接:. - 力扣(LeetCode)
思路(数组哈希表):
s 和 t 仅包含小写字母,所以用数组开辟26个空间就行了,开辟后遍历字符串s,将每个元素的信息与0-25对应(a 对应 0, b 对应 1...)数组一开始都是0,每遍历一个元素就让对应元素在数组中+1,然后遍历字符串t,流程和遍历s差不多,把+1改成-1,都遍历后如果哈希表和原来一样,那就说明是异位词。
代码实现:
349. 两个数组的交集
题目链接:. - 力扣(LeetCode)
思路(set哈希表):
创建nums1的哈希表nums和空哈希表result,遍历nums2中的各个元素,如果元素在nums里有,也就代表在nums1里有相同值的元素。将其放进result里,遍历完后输出result。
代码实现:
202. 快乐数
题目链接:. - 力扣(LeetCode)
思路(set哈希表):
先将计算交给另一个函数cal,然后将每一次的结果记录到set中,如果结果是1好说,直接return true了,如果不是就要记录在哈希表set中,每次都要将结果查找一遍,如果有一样的就说明进入循环了,就可以 return false结束了。
代码实现:
思路(快慢指针):
没想到吧,快慢指针他又来了!
先和上面的思路差不多,构造计算的函数cal,然后就是判断是否循环了,和环形链表那道题一个逻辑,如果真的无限次到达不了1的话,一定会重复一样的数值,进入循环。如果一进入循环,也就能用快慢指针来探测是否为环状结构。具体操作的流程理解可以移步day4的环形链表II。(相信理解完这两道题你会感受到不一样的惊喜) 代码随想录算法训练营第四天| 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II,链表总结-CSDN博客
和环形链表题目不同的是,我们不需要知道具体在哪个点进入环,只用判断是否为环结构就可以了
代码实现:
1. 两数之和
题目链接:. - 力扣(LeetCode)
思路(暴力):
像冒泡排序一样用双重for循环将所有的两两组合遍历一遍判断之和是否等于target。
代码实现:
思路(map哈希表);
用map的键代表元素值,用map的值代表元素下标,遍历nums,如果target - nums[i]在map中就直接return{iter->second,i} 。前者是map在target - nums[i]的值(也就是总和为target的对应nums[i]的另一个元素的下标),后者是i(当前遍历的元素下标)。如果没有对应值,那就把i 和 nums[i]记录到map中。遍历完后都没有找到,就return{}了。
代码实现: