双指针法(N数之和)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


N数之和

提示:类似的一些列题解法,有两数之和,三数之和,四数之和等,这一次记录的是各种适合使用指针来做题接的方法,。博主之前不死心,秉着万物皆可递归的想法,把自己搞的欲生欲四,虽然递归加DFS的方法真的很爽,但是剪枝这个方法还需要多修炼。


1.两数之和

题目描述:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:给定 nums = [2, 7, 11, 15], target = 9。因为 nums[0] + nums[1] = 2 + 7 = 9。所以返回 [0, 1]

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        l=len(nums)
        if not l:
            return []
        nums,ind=zip(*sorted(zip(nums,list(range(l)))))
        # 使用zip函数,设置辅助队列ind,队列里的值是nums对应的下标
        i=0
        j=l-1
        while i<j:
            if nums[i]+nums[j]==target:
                return [ind[i],ind[j]]
            elif nums[i]+nums[j]>target:
                j-=1
            else:
                i+=1
        return []

2.三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[[-1, 0, 1], [-1, -1, 2]]

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:

        # 思路解析
        # 对于传入的数组 nums, 三数之和
        # 1,只要数组内的数小于3 或等于0,则返回false
        # 2, 对数组进行初步排序
        # 3,排序后判断第一个数的大小, 是否小于0,否则后面将不会有小于0的值,则直接返回false
        # 4,对于重复的数, 直接跳过
        # 5,设置左指针和右指针, L = i+1, R = N-1
        # 6,遍历排序后的数组,一旦找到 s = nums[i] + nums[L+1] + nums[N-1] = 0,将这和组合加入到res队列中去
        # 再判断左右边界是否出现重复的情况。
        # 7,最后判断 s 小于0,则值小了,左进一位,L=L+1, 若大于0 ,则是大了,右减一位

        n = len(nums)
        res = []
        if (not nums or n<3):
            return []
        nums.sort()
        for i in range(n):
            if (nums[i]>0): 
            # 判断排序后的第一个数的大小,因为target是0,若都一个大于0,就不可能找到三数之和为0
                return res
            if (i>0 and nums[i] == nums[i-1]): #去重,为了避免出现重复的三元组。同一层下避免同一个序列出现多次
                continue
            L = i+1
            R = n-1
            while(L<R):
                if(nums[i]+nums[L]+nums[R] == 0):
                # 设置返回条件,一旦找到三数之和为0的情况,返回序列
                    res.append([nums[i],nums[L],nums[R]])
                    while(L<R and nums[L] == nums[L+1]):
                    #当重复元素出现时,跳过当前的L,指向L+1
                        L=L+1
                    while(L<R and nums[R] == nums[R-1]):
                    #当重复元素出现时,跳过当前的R,指向R-1
                        R=R-1
                    L= L+1
                    R= R-1
                elif(nums[i]+nums[L]+nums[R]>0):
                # 相加大于0,数大了, R-1
                    R=R-1
                else:
                 # 相加小于0,数小了, L+1
                    L=L+1
        return res

第二种解法:泛用于N数之和,使用回溯剪枝的思路。
关键点是:

  1. 先对nums排序;
  2. 递归处理时的剪枝处理(参考注释)
    这种写法参考了国际站上的文章,我觉得是最好的一种解法了。
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if not nums: return []
        # 先排序,关键!
        nums.sort()     
        ans = set()
        N, target = 3, 0
        self._find_sum(nums, 0, N, target, [], ans)
        return list(ans)

    def _find_sum(self, nums, start, N, target, path, ans):
        # terminator
        if len(nums) < N or N < 2: return
        # process
        if N == 2:
            # 两数求和
            d = set()
            for j in range(start, len(nums)):
                if target - nums[j] in d:
                    ans.add(tuple(path + [target - nums[j], nums[j]]))
                else:
                    d.add(nums[j])
        else:
            for i in range(start, len(nums)):
                # 剪枝1: target比剩余数字能组成的最小值还要小 或 比能组成的最大值还要大,就可以停止循环了
                if target < nums[i] * N or target > nums[-1] * N: break
                # 剪枝2: 去重
                if i > start and nums[i] == nums[i - 1]: continue
                # drill down
                self._find_sum(nums, i + 1, N - 1, target - nums[i], path + [nums[i]], ans)
        return

3.四数之和


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值