关于代码的一切尽在「代码随想录」
接下来我们要明确 哈希法可以用来解决什么问题, 「当我们需要判断一个元素是否出现过的时候,就要考虑哈希表。」
哈希法通常使用如下三种数据结构 :
接下来我们分别看一下三个容器在哈希法中的应用。
这道题题意很清晰,就是用判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点1。
因为题目只有小写字母,那我们可以采用空间换取时间的哈希策略, 用一个长度为26的数组还记录magazine里字母出现的次数,然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。
题解:https://github.com/youngyangyang04/leetcode/blob/master/problems/0383.赎金信.md
糖果的种类是可妹妹先来,所以思路先求出一共有多少种类型的糖果。
需要注意:数组中数字的大小也就是糖果的种类取值范围在[负十万和 正十万之间], 依然可以定义一个数组,通过哈希法求出有多少类型的糖果。
那么糖果种类可以是负数 怎么办呢,可以把 定一个 20万大小的数组 ,就可以把糖果的全部类型映射到数组的下表了。
通过哈希法,可以求出了糖果的类型数量,如果糖果种类大于糖果总数的一半了,返回 糖果数量的一半就好,因为妹妹已经得到种类最多的糖果了,否则,就是返回 糖果的种类。
题解:https://github.com/youngyangyang04/leetcode/blob/master/problems/0575.分糖果.md
这道题目,主要要学会使用一种哈希数据结构,unordered_set,这个数据结构可以解决很多类似的问题。
注意题目特意说明:输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序。
这道题用暴力的解法时间复杂度是O(n^2),这种解法面试官一定不会满意,那我们看看使用哈希法进一步优化。
那么可以发现,貌似用数组做哈希表可以解决这道题目,把nums1的元素,映射到哈希数组的下表上,然后在遍历nums2的时候,判断是否出现过就可以了。
但是要注意,使用数据来做哈希的题目,都限制了数值的大小,例如只有小写字母,或者数值大小在[0- 10000] 之内等等。而这道题目没有限制数值的大小,就无法使用数组来做哈希表了。
例如说:如果我的输入样例是这样的, 难道要定义一个2亿大小的数组来做哈希表么, 不同的语言对数组定义的大小都是有限制的, 即使有的语言可以定义这么大的数组,那也是对内存空间造成了非常大的浪费。
此时我们就要使用另一种结构体了,set ,关于set,C++ 给我们提供了如下三种可用的数据结构。
std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,我们并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。
题解:https://github.com/youngyangyang04/leetcode/blob/master/problems/0349.两个数组的交集.md
这道题目重点是,题目中说了会 「无限循环」, 那么也就是说 求和的过程中,sum会重复出现,这对我们解题很重要,这样我们就可以使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。
还有就是求和的过程,如果对取数值各个位上的单数操作不熟悉的话,做这道题也会比较艰难。
题解:https://github.com/youngyangyang04/leetcode/blob/master/problems/0202.快乐数.md
很明显暴力的解法是两层for循环查找,时间复杂度是O(n^2) 。我们来看一下使用数组和set来做哈希法的局限。
此时我们就要选择另一种数据结构 map ,map是一种key value的存储结构,我们可以用key保存数值,用value在保存数值所在的下表。这道题目是map在哈希法中的经典应用。
题解:https://github.com/youngyangyang04/leetcode/blob/master/problems/0001.两数之和.md
本题使用哈希表映射的方法。
那么为什么18. 四数之和,0015.三数之和不适用哈希表映射的方法呢,感觉上 这道题目都是四个数之和都可以用哈希,三数之和怎么就用不了哈希呢。
因为题目15.三数之和和18. 四数之和,使用哈希的方法在不超时的情况下做到对结果去重很困难。
而这道题目 相当于说 不用考虑重复元素,是四个独立的数组,所以相对于题目18. 四数之和,0015.三数之和,还是简单了不少。
题解:https://github.com/youngyangyang04/leetcode/blob/master/problems/0454.四数相加II.md
三数之和 四数之和的问题,用哈希法也可以解决,但是最好使用双指针法,因为使用哈希法的话去重逻辑有很多细节需要注意,很难快速写出没有bug的代码。
所以几数之和的问题,我将单独写一篇文章来介绍。
「哈希法的本质是空间换时间,通过数组,set和map将数据已经预处理,从而通过O(1)的时间复杂度快速判断出元素是否出现某个集合中。」
数组的限制是数组的大小,当我们的哈希值超过了数组大小的时候,就无法映射到数据下表上。
进而采用set,set的限制是只能存放单一key,不能记录更多的数据。
进而考虑map,map通过<key, value>
的存储结构,允许我们更加灵活的存储哈希值和对应的数值。
以上六道题目都非常具有代表性,大家可以按循序做一下,相信会对哈希法有所感悟。
如有疑问欢迎留言!