leetcode-三数之和

 题目来自LeetCode,链接:3sum。具体描述为:给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。需要注意答案中不可以包含重复的三元组。

 示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

 与前一篇博客的两数之和相似,一个很容易想到的思路就是暴力法,遍历所有可能的三个数的组合,这样的时间复杂度为 O ( n 3 ) O(n^{3}) O(n3)。然后也可以用到两数之和的想法,就是对于当前遍历到的数,以(0-当前数)作为target,以删去当前数的数组作为待处理数组,问题转变为求两数之和等于target,当然结果可能不止一组,这样做的时间复杂度减为 O ( n 2 ) O(n^{2}) O(n2),但还是会超时,原因在于题目要求不可重复,所以在判断重复上会耗费较多时间。

 另一种可行的方法是利用双指针的方法。首先对数组排序,然后对于当前的数,其右边的数组成一个子数组(而且是升序的),只需要在右边搜索两个数之和等于(0-当前数)即可。给定左指针为子数组最左,右指针为子数组最右,左指针对应左数,右指针对应右数,则:

  • 如果左数+右数=0-当前数,则[当前数,左数,右数]是一组符合条件的三元组,将左指针右移至不与当前左数相等,右指针左移至不与当前右数相等,这么做是为了避免重复;
  • 如果左数+右数<0-当前数,说明需要增大左数,所以左指针右移;
  • 否则即左数+右数>0-当前数,说明需要减少右数,所以右指针左移。

最后当前面的流程进行到左指针遇到/超过右指针就结束了,此时要把当前数右移到不与当前数相同位置,同样是为了避免重复。

 可以看到这种方法的时间复杂度同样为 O ( n 2 ) O(n^{2}) O(n2),但由于通过避免重复而免去判断重复的过程,从而可以AC。另外,空间复杂度为 O ( 1 ) O(1) O(1),其实就是指针占用空间罢了。

 JAVA版代码如下:

class Solution {

    public List<List<Integer>> threeSum(int[] nums) {
        int numsLen = nums.length;
        Arrays.sort(nums);
        int i = 0;
        List<List<Integer>> result = new ArrayList<>();
        while (i < numsLen - 2) {
            if (nums[i] > 0) {
                return result;
            }
            int left = i + 1;
            int right = numsLen - 1;
            int nowNum = nums[i];
            while (left < right) {
                int leftNum = nums[left];
                int rightNum = nums[right];
                int sumLR = leftNum + rightNum;
                if (sumLR == -nowNum) {
                    result.add(Arrays.asList(nowNum, leftNum, rightNum));
                    ++left;
                    while (left < right && nums[left] == leftNum) {
                        ++left;
                    }
                    --right;
                    while (right > left && nums[right] == rightNum) {
                        --right;
                    }
                }
                else if (sumLR < -nowNum) {
                    ++left;
                }
                else {
                    --right;
                }
            }
            ++i;
            while (i < numsLen -2 && nums[i] == nowNum) {
                ++i;
            }
        }
        
        return result;
    }
}

 提交结果如下:


 Python版代码如下:

class Solution:

    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()                                         #先升序排序
        length = len(nums)
        result = []
        i = 0
        while i < length - 2:                               #只遍历到倒数第3个数
            if nums[i] > 0:                                 #第一个数已经大于零了,后面不可能再有符合的了
                return result
            left = i + 1                  
            right = length - 1
            now_num = nums[i]
            while left < right:                             #当左指针和右指针相遇就结束一轮循环
                left_num = nums[left]
                right_num = nums[right]
                sumLR = left_num + right_num
                if sumLR == -now_num:                       #三个数的和为0
                    result.append([now_num,left_num,right_num])
                    left += 1                               #这个时候左指针右移直到所在位置的数不跟其左边的一样
                    while left < right and left_num == nums[left]:
                        left += 1
                    right -= 1                              #这个时候右指针左移直到所在位置的数不跟其右边的一样
                    while right > left and right_num == nums[right]:
                        right -= 1
                elif sumLR < -now_num:                      #当三个数的和小于零时,左指针右移
                    left += 1
                else:                                       #当三个数的和大于零时,右指针左移
                    right -= 1
            i += 1
            while i < length - 2 and nums[i] == now_num:    #第一个数移动到不与之前一样为止
                i += 1
        return result

 提交结果如下:


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值