Day8 哈希表Part3
哈希表部分的最后一节,三数之和比较难,而且用哈希表实现效率不高,使用双指针可以获得较好的时间效率。
1 赎金信
1.1 题目描述
给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)
注意:
你可以假设两个字符串均只含有小写字母。
canConstruct(“a”, “b”) -> false
canConstruct(“aa”, “ab”) -> false
canConstruct(“aa”, “aab”) -> true
1.2 思路解析
通过构造字符和出现次数的unordered_map
即可。
1 | bool canConstruct(string ransomNote, string magazine) { |
2 三数之和
2.1 题目描述
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
2.2 思路解析
这个题首先得知道怎么找全三个和为0的数,首先可以用减治的思想化为定值 + 两数之和的问题,之前我们已经用时间复杂度为O($n$)的方法求解过两数之和的问题了,加上固定指针的最外层循环,就可以实现时间复杂度为$O(n^2)$的算法了。
但是问题来了,找出全部的三元组之后要如何去重呢?
我想到的方法是利用set的模版以及自动去重的特性。
1 | vector<vector<int>> twoSum(int target, const vector<int>& nums, int k){ |
这种方法虽然正确,但是提交的时候却超时了,我们加上这一句剪枝操作试试。
1 | if (i > 0 && nums[i] == nums[i - 1]) continue; |
加上之后就可以顺利通过测试了,但是所用的时间和空间还是太大。分析其中的原因,虽然算法整体的时间复杂度是$O(n^2)$,但是建立哈希表的时间太慢,而这是使用哈希方法不可避免的问题。
那有没有比哈希表更高效的方法呢?
有的,答案就是使用双指针!
哈希表实现中,确定好第一个指针之后,每移动一次第二个指针都会调用哈希函数在红黑树中建立一个节点。这在时间和空间上都会有加大的开销。于是我们想,如果能够用两个指针相反方向遍历剩余数组,找到合适的target值,这样时间和空间开销就会小很多了!
于是我们构造两个指针,left和right指针来进行遍历。如果三个数加起来是负数的话,left指针就往右移;如果加起来是正数的话,right指针就往左移。然后再加上去重操作
1 | vector<vector<int>> threeSum(vector<int>& nums) { |
用这种方法,时间复杂度就很小,算法性能提升了很多。