LeetCode_15_中等_三数之和


1. 题目

给你一个整数数组 n u m s nums nums ,判断是否存在三元组 [ n u m s [ i ] , n u m s [ j ] , n u m s [ k ] ] [nums[i], nums[j], nums[k]] [nums[i],nums[j],nums[k]] 满足 i ≠ j 、 i ≠ k i \neq j、i \neq k i=ji=k j ≠ k j \neq k j=k ,同时还满足 n u m s [ i ] + n u m s [ j ] + n u m s [ k ] = 0 nums[i] + nums[j] + nums[k] = 0 nums[i]+nums[j]+nums[k]=0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入: n u m s = [ − 1 , 0 , 1 , 2 , − 1 , − 4 ] nums = [-1,0,1,2,-1,-4] nums=[1,0,1,2,1,4]
输出: [ [ − 1 , − 1 , 2 ] , [ − 1 , 0 , 1 ] ] [[-1,-1,2],[-1,0,1]] [[1,1,2],[1,0,1]]
解释:
n u m s [ 0 ] + n u m s [ 1 ] + n u m s [ 2 ] = ( − 1 ) + 0 + 1 = 0 nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 nums[0]+nums[1]+nums[2]=(1)+0+1=0
n u m s [ 1 ] + n u m s [ 2 ] + n u m s [ 4 ] = 0 + 1 + ( − 1 ) = 0 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 nums[1]+nums[2]+nums[4]=0+1+(1)=0
n u m s [ 0 ] + n u m s [ 3 ] + n u m s [ 4 ] = ( − 1 ) + 2 + ( − 1 ) = 0 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 nums[0]+nums[3]+nums[4]=(1)+2+(1)=0
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入: n u m s = [ 0 , 1 , 1 ] nums = [0,1,1] nums=[0,1,1]
输出: [ ] [ ] []
解释:唯一可能的三元组和不为 0 。

示例 3:

输入: n u m s = [ 0 , 0 , 0 ] nums = [0,0,0] nums=[0,0,0]
输出: [ [ 0 , 0 , 0 ] ] [[0,0,0]] [[0,0,0]]
解释:唯一可能的三元组和为 0 。


提示

  • 3 < = n u m s . l e n g t h < = 3000 3 <= nums.length <= 3000 3<=nums.length<=3000
  • − 1 0 5 < = n u m s [ i ] ≤ 1 0 5 -10^5 <= nums[i] \leq 10^5 105<=nums[i]105

2. 思路及代码实现(Python)

2.1 排序 + 双指针

这道题目有点类似于《LeetCode_1_简单_两数之和》,当时题目当中是,如果确定了一个值,另一个值也能相应确定,因此可以用哈希表的形式存储值对,将时间复杂度降低为 O ( N ) O(N) O(N)

而三数之和也类似的,我们可以视为第一个数确定后,后面两个数的搜索就变成了 “两数之和” 问题。对于两个数之和是固定值的问题,有一个特点是,当一个数增大,另一个加和数会减小,才能保证求和结果一致。因此这个特点非常适合在有序序列当中用双指针的方法,对此,基本思路确定,就是遍历一次数组,作为元组第一个数,另外两个数则用双指针的方式确定,在这个思路下,就需要对数组先进行排序。此外,题目中还提到找到的三元组不应重复,因此在固定第一个值前提下,移动变更第二个值时,若第二个值不变,则跳过。同理,在移动第一个值时,若第一个值移动后和移动前的值相等,则跳过。

此代码思路下,算法的复杂度为 O ( N 2 ) O(N^2) O(N2) 以及排序的复杂度 O ( N l o g N ) O(Nlog N) O(NlogN),加和结果也为 O ( N 2 ) O(N^2) O(N2)。空间复杂度为对数组的排序副本 O ( N ) O(N) O(N)

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

执行用时:589 ms
消耗内存:19.36 MB

参考来源:力扣官方题解

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值