454.四数相加II
思路
将四数相加问题转换成两个两数相加问题,使用哈希表。
时间复杂度On2
空间复杂度On2
官方题解
首先定义 一个unordered_map,key放a和b两数之和,value 放a和b两数之和出现的次数。
遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。
定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
最后返回统计值 count 就可以了
代码
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
maps:=make(map[int]int)
res:=0
for _,v1:=range nums1{
for _,v2:=range nums2{
maps[v1+v2]++
}
}
for _,v3:=range nums3{
for _,v4:=range nums4{
res+=maps[0-v3-v4]
}
}
return res
}
困难
去重问题
这道题目是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于题目18. 四数之和,题目15.三数之和,还是简单了不少!
如果本题想难度升级:就是给出一个数组(而不是四个数组),在这里找出四个元素相加等于0,答案中不可以包含重复的四元组
383. 赎金信
思路
哈希表存入第二个字符串(即需要被抄的字符串)中的字符出现次数。然后遍历第一个字符串,将对应次数-1,最后遍历哈希表,如果次数>0,那么返回true。
时间复杂度Om+n
空间复杂度O1
代码
func canConstruct(ransomNote string, magazine string) bool {
maps:=make([]int,26)
for _,v:=range magazine{
maps[v-'a']++
}
for _,v:=range ransomNote{
maps[v-'a']--
}
for _,v:=range maps{
if v<0{
return false
}
}
return true
}
困难
题目要求英文小写字母,只有26个,所以可以用数组模拟华西表。
15. 三数之和
思路
同一个数组要考虑去重问题。
哈希表去重困难,选择双指针法。
时间复杂度:O(n^2)。
官方题解
其实这道题目使用哈希法并不十分合适,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。
而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是O(n^2),也是可以在leetcode上通过,但是程序的执行时间依然比较长 。
接下来我来介绍另一个解法:双指针法,这道题目使用双指针法 要比哈希法高效一些,那么来讲解一下具体实现的思路。
拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
时间复杂度:O(n^2)。
代码
func threeSum(nums []int) [][]int {
sort.Ints(nums)
res:=make([][]int,0)
for i:=0;i<len(nums)-2;i++{
if i>0&&nums[i]==nums[i-1]{
continue
}
left:=i+1
right:=len(nums)-1
for left<right{
if left>i+1&&nums[left]==nums[left-1]{
left++
continue
}
if right<len(nums)-1&&nums[right]==nums[right+1]{
right--
continue
}
if nums[i]+nums[left]+nums[right]<0{
left++
}else if nums[i]+nums[left]+nums[right]>0{
right--
}else{
res=append(res,[]int{nums[i],nums[left],nums[right]})
left++
right--
}
}
}
return res
}
困难
去重的逻辑,去重应该是和前一次循环的数相比。
18. 四数之和
思路
在三数之和基础上再套一层for循环。
时间复杂度On3
代码
func fourSum(nums []int, target int) [][]int {
sort.Ints(nums)
res:=make([][]int,0)
for i:=0;i<len(nums)-3;i++{
if i>0&&nums[i]==nums[i-1]{
continue
}
for j:=i+1;j<len(nums)-2;j++{
if j>i+1&&nums[j]==nums[j-1]{
continue
}
left:=j+1
right:=len(nums)-1
for left<right{
if nums[i]+nums[j]+nums[left]+nums[right]<target{
left++
}else if nums[i]+nums[j]+nums[left]+nums[right]>target{
right--
}else{
res=append(res,[]int{nums[i],nums[j],nums[left],nums[right]})
for left<right&&nums[left+1]==nums[left]{
left++
}
for left<right&&nums[right-1]==nums[right]{
right--
}
left++
right--
}
}
}
}
return res
}
困难
也可以在取得结果后再去重,注意边界条件
for left<right&&nums[right-1]==nums[right]{
right--
}
今日收获
哈希表对于去重的问题不是很适用。
如果问题需要去重 ,比如从同一个数组中选取元组,可以考虑排序后双指针,然后在循环的时候或者移动指针的时候去重。