算法day7|454,383,15,18

今日任务

  • 454.四数相加II

  • 383. 赎金信

  • 15. 三数之和

  • 18. 四数之和

  • 总结

454.四数相加II

class Solution(object):
    def fourSumCount(self, nums1, nums2, nums3, nums4):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :type nums3: List[int]
        :type nums4: List[int]
        :rtype: int
        """
        hashmap = {}
        result = 0
        for a in nums1:
            for b in nums2:
                hashmap[a+b] = hashmap.get(a+b,0)+1
                
        for c in nums3:
            for d in nums4:
                target = 0-(c+d)
                if target in hashmap:
                    #不是直接在result上面加一,因为统计了这个和出现了多次了,所以应该返回map的value的值
                    result += hashmap.get(target)
        return result

重点:

  • 为什么会想到使用hashmap:

因为不用考虑会不会出现重复项。只需要将前面两个的和组合,然后相减,找map里面是否有存在,所以使用hashmap

  • 用什么哈希表的类型:

不能用数组,-2的28次方 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 2的28次方太大了,用数组占的地方太大。

  • map的内部设置:

这里很巧妙,key做两个数组的和,value统计次数。

题目链接/文章讲解/视频讲解:代码随想录

二刷:

未ac

三刷

var fourSumCount = function(nums1, nums2, nums3, nums4) {
    let map = new Map()
    let result = 0
    for(let i = 0;i<nums1.length;i++){
        for(let j = 0;j<nums2.length;j++){
            let sum = nums1[i]+nums2[j]
            map.set(sum, (map.get(sum) || 0) + 1)
        }
    }
    console.log(map)
    for(let num3 of nums3){
        for(let num4 of nums4){
            let sum = num3+num4
            result += (map.get(0-sum) || 0) 
        }
    }
    return result
};

383. 赎金信

class Solution(object):
    def canConstruct(self, ransomNote, magazine):
        """
        :type ransomNote: str
        :type magazine: str
        :rtype: bool
        """
        hashmap = 26*[0]
        for letter in magazine:
            hashmap[ord(letter) - ord("a")] += 1
        
        for letter in ransomNote:
            hashmap[ord(letter) - ord("a")] -= 1
        #如何小于0的,返回false
        for i in hashmap:
            if i < 0:
                return False
        #循环完毕,如果没有就返回True
        return True

题解好像跟我有点出入,可能是我理解的题目意思错误。

应该是random的所有东西都出现在magazine里头,没出现就是false

class Solution(object):
    def canConstruct(self, ransomNote, magazine):
        """
        :type ransomNote: str
        :type magazine: str
        :rtype: bool
        """
        hashmap = 26*[0]
        for letter in magazine:
            hashmap[ord(letter) - ord("a")] += 1
        
        for letter in ransomNote:
            if hashmap[ord(letter) - ord("a")] == 0:
                return False
            else:
                hashmap[ord(letter) - ord("a")] -= 1
        return True

题目链接/文章讲解:代码随想录

二刷:

ac了

三刷:ac

var canConstruct = function(ransomNote, magazine) {
    let arr = new Array(26).fill(0)
    for(let i = 0;i<ransomNote.length;i++){
        arr[ransomNote[i].charCodeAt()-'a'.charCodeAt()] +=1
    }
    for(let i = 0;i<magazine.length;i++){
        arr[magazine[i].charCodeAt()-'a'.charCodeAt()] -= 1
    }
    for(let a of arr){
        if(a > 0){
            return false
        }
    }
    return true
    
};

15. 三数之和

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        #先用hashmap写一下
        result = []
        hashmap = {}
        for i in range(len(nums)):
            hashmap[nums[i]] = i
        
        for l in range(len(nums)):
            for r in range(l+1,len(nums)):
                target = 0 - (nums[l]+nums[r])
                if target in hashmap and hashmap[target] != r and hashmap[target] != l:
                    temp = [nums[l],nums[r],target]
                    temp.sort()
                    result.append(temp)
        return set(list(result))

使用hashmap也算解决了一半,但是.......好多重复值,去不掉

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()
        for i in range(len(nums)):
            left = i+1 
            right = len(nums)-1
            #如果第一个数大于0,直接返回,后面都是更大的
            if nums[i] > 0:
                break
            if i >= 1 and nums[i] == nums[i-1]:
                continue
            #一直在left 和right区间中寻找有无等于0
            while left < right:
                summ = nums[i] + nums[left] + nums[right]
                if summ < 0:
                    left += 1
                elif summ > 0:
                    right -= 1
                else:
                    result.append([nums[i],nums[left],nums[right]])
                    while left != right and nums[left] == nums[left+1]:
                        left += 1
                    while left != right and nums[right] == nums[right-1]:
                        right -= 1
                    left += 1
                    right -= 1
        return result

写错的地方:

if nums[i] > 0:
     break
if i >= 1 and nums[i] == nums[i-1]:
     continue

这里放在while循环里面判断了,应该在一开始就判断一下nums[i]是不是大于0,不是直接跳出循环。这个break,我写成return 了,所以返回没有结果,这里要注意

else:
                    result.append([nums[i],nums[left],nums[right]])
                    while left != right and nums[left] == nums[left+1]:
                        left += 1
                    while left != right and nums[right] == nums[right-1]:
                        right -= 1
                    left += 1
                    right -= 1

这里后面的while循环,写成了nums【left-1】 == nums【left】,导致out of index

 while left != right:也可以写成left < right

忘记加上:  left += 1
                    right -= 1    导致出错

重点:

  • 如何去重
  1. i 的去重

如果i 和前面相同,就去重。为什么不跟后面比较,因为占领了left的位置,left也要去重的。重复操作了,而且是3sum

  1. left,right的去重

位置是sum=0的时候操作,而且是比较后面的元素,如果重复就continue一下

  • 如何使用双指针,怎么操作?

使用i(for),再使用left(left = i + 1)和right指针。。。

题目链接/文章讲解/视频讲解:代码随想录

使用双指针的算法

三刷:

var threeSum = function(nums) {
    let result = []
    nums.sort((a,b)=>a-b)
    // 双指针算法
    for(let i = 0;i<nums.length;i++){
        if(nums[i]>0) return result
        if(i>0 &&nums[i] === nums[i-1]) continue
        let left = i+1
        let right = nums.length -1
        while(left<right){
            let threeSum = nums[i]+nums[right]+nums[left]
            let f = nums[i]
            let s = nums[left]
            let t = nums[right]
            if(threeSum === 0){
                result.push([f,s,t])
                while(left < right && nums[left] === nums[left+1]){
                    left ++
                }
                while(left < right&& nums[right] === nums[right-1]){
                    right--
                }
                left += 1
                right -= 1
            }else if(threeSum<0){
                left += 1
            }else{
                right -= 1
            }
            
        }
    }
    console.log(result)
    return result
    
};

18. 四数之和

class Solution(object):
    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()
        for i in range(len(nums)):
            #剪枝,如果是负数,他就直接break了
            if target > 0 and nums[i] > target and nums[i] > 0:
                break
            #去重
            if i > 0 and (nums[i] == nums[i-1]):
                continue
            for j in range(i+1,len(nums)):
                #剪枝
                if target > 0 and nums[i]+nums[j] > target and nums[j] > 0:
                    break
                #因为要间隔一下,不然的话比较的是i = j嘛
                if j>i+1 and nums[j] == nums[j-1]:
                    continue
                left = j + 1
                right = len(nums)-1
                while left < right:
                    summ = nums[i] + nums[j] +nums[left] + nums[right]
                    if summ == target:
                        result.append([nums[i],nums[j],nums[left],nums[right]])
                        while left < right and nums[left] == nums[left+1]:
                            left += 1
                        while left < right and nums[right] == nums[right-1]:
                            right -= 1
                        right -= 1
                        left += 1
                    elif summ > target:
                        right -= 1
                    else:
                        left += 1             
        return result

与上面一题最大的区别就是多了剪枝环节

题目链接/文章讲解/视频讲解:代码随想录

二刷:

未ac

剪枝操作是因为,我们需要考虑到数组万一都是负数。【-4,-1,0,0】 target = -5,这时候,符合情况的,如果单考虑nums[0] > target,,就会错过结果集。

三刷

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    let result = []
    nums.sort((a,b)=>a-b)
    for(let i = 0;i<nums.length;i++){
        // 一级剪枝
        if(target > 0 && nums[i]>0 && nums[i]>target) break
        // 一级去重
        if(i>0 && nums[i]===nums[i-1]) continue
        for(let j = i+1;j<nums.length;j++){
            // 二级剪枝
            if(target > 0 && nums[i]+nums[j] > 0 && nums[i]+nums[j] > target) break
            // 二级去重
            if(j-1>i && nums[j] === nums[j-1]) continue
            let left = j +1
            let right = nums.length-1
            while(left < right){
                let threeSum = nums[i]+nums[j]+nums[right]+nums[left]
                if(threeSum === target){
                    result.push([nums[i],nums[j],nums[right],nums[left]])
                    while(left < right && nums[left] === nums[left + 1]){
                        left +=1
                    }
                    while(left < right && nums[right] === nums[right-1]){
                        right -= 1
                    }
                    left += 1
                    right-=1
                }
                else if(threeSum > target){
                    right -= 1
                }else{
                    left += 1
                }
            }
            
        }
    }
    return result

    
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值