python刷题--N数之和问题(双指针+剪枝)

1.两数之和(双指针)

这题前面已经做过,当时是用哈希表做的,时间复杂度为N

但如果换一种思路,用今天学的双指针来做,虽然在时间复杂度上不降反增(因为排序的复杂度为NlogN)但理解起来十分简单清晰。(注:对于三数四数N数之和问题来说,双指针算法相当于将最内部的两层循环优化成一层。)

思路:即先排序,得到一个有序的列表,一个指针指向列表开头,一个指向末尾,对于每一个状态而言,如果大于目标值则让右指针左移,小于目标值则让左指针右移。该方法适用于求无重复的解。

个人题解:

# 注释1
class Solution:
    def twoSum(self, nums: List[int], target: int):
        res = []
        i, j = 0, len(nums)-1
        nums.sort()
        for i in range(len(nums)):
            if i > 0 and nums[i-1] == nums[i]:
                continue
            while i < j and nums[i] + nums[j] > target:
                j -= 1
            if i == j:  # 注释2,3
                break
            if nums[i] + nums[j] == target:
                res.append([nums[i], nums[j]])
        return res

注释1:# 注意这不是题目:两数之和的解,这代码的条件是跟后面的三数之和四数之和相同时才能成立(必须要求只返回目标值而不是返回索引)

注释2:# 能导致i=j的只有两种情况。一:由内层循环得到,那么当前状态的前一个状态nums[i]+nums[j] 是大于target的,此时期待减少两个数的和,但是控制减小的指针j已经无法向左移,所以循环结束二:由外层循环得到,那么当前状态的前一个状态nums[i]+nums[j]是小于target的,此时期待增大两个数的和,但是控制增大的指针i已经无法向右移,循环结束

注释3:在上述情况发生时(期待增大的两个数之和,但是控制增大的指针i无法向右移)。那么可不可以让j往右移动(回头)呢?

答:是不行的。看上去j向右移动,和这个状态的i是一组全新的i,j组合,是没有遍历过的,那为什么不能这样呢?

我们来假设这样的情况成立。。假设在移动之前i和j的位置分别为i0和j0那么j0向右移动后移到某个j1的位置,此时nums[i0]+nums[j1]的值是大于等于0(或者目标值的),假设i在最开始的位置是i1,那么当j第一次运动到j1的位置时,i在i1的位置,这个时候的nums[i1]+nums[j1]是小于nums[i0]+nums[j1]的,而j1运动到j0是为了使整体值减小,那么第一次的nums[i1]+nums[j1]一定就是大于0,所以对比两个状态,i1和j1这个状态下已经大于0,那么就不会再让i1增大得到i0,所以与我们一开始设定好的值大就减小,值小就增大的思想不符,假设不成立。

2.三数之和

题目:https://leetcode-cn.com/problems/3sum/

官方题解:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        nums.sort()
        ans = list()
        
        # 枚举 a
        for first in range(n):
            # 需要和上一次枚举的数不相同
            if first > 0 and nums[first] == nums[first - 1]:
                continue
            # c 对应的指针初始指向数组的最右端
            third = n - 1
            target = -nums[first]
            # 枚举 b
            for second in range(first + 1, n):
                # 需要和上一次枚举的数不相同
                if second > first + 1 and nums[second] == nums[second - 1]:
                    continue
                # 需要保证 b 的指针在 c 的指针的左侧
                while second < third and nums[second] + nums[third] > target:
                    third -= 1
                # 如果指针重合,随着 b 后续的增加
                # 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if second == third:
                    break
                if nums[second] + nums[third] == target:
                    ans.append([nums[first], nums[second], nums[third]])
        
        return ans

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/3sum/solution/san-shu-zhi-he-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思想一样,就是在外层再套一个循环。

3.四数之和

题目:https://leetcode-cn.com/problems/4sum/

整体思路一样,题解可以看看,多了个剪枝操作(通过判断情况筛选掉不必要的循环)

官方题解:

https://leetcode-cn.com/problems/4sum/solution/si-shu-zhi-he-by-leetcode-solution/python刷题--N数之和问题(双指针+剪枝)https://leetcode-cn.com/problems/4sum/solution/si-shu-zhi-he-by-leetcode-solution/

4.N数之和 

道理一样,就是在外层套循环。然后将最内的两层循环改为双指针操作。

上一篇:0字符串中等 LeetCode784. 字母大小写全排列


下一篇:LibreOJ 109 并查集