哈希表part2 | 454.四数相加II ● 383. 赎金信 ● 15. 三数之和 ● 18. 四数之和


454.四数相加II

力扣454

思路

将四数相加问题转换成两个两数相加问题,使用哈希表。
时间复杂度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. 赎金信

力扣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. 三数之和

力扣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. 四数之和

力扣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--
                    }

今日收获

哈希表对于去重的问题不是很适用。
如果问题需要去重 ,比如从同一个数组中选取元组,可以考虑排序后双指针,然后在循环的时候或者移动指针的时候去重。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值