数据结构【双指针、数组】| leetcode 15. 三数之和(中等)

代码链接:https://leetcode.cn/problems/3sum/solution/3sumpai-xu-shuang-zhi-zhen-yi-dong-by-jyd/

解题思路:三指针

暴力法搜索为 O ( N 3 ) O(N^3) O(N3)时间复杂度,可通过双指针动态消去无效解来优化效率。

双指针法铺垫: 先将给定 nums 排序,复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)

双指针法思路: 固定 3 个指针中最左(最小)数字的指针 k k k,双指针 i , j i,j ij 分设在数组索引 (k+1, len(nums)-1) \text{(k+1, len(nums)-1)} (k+1, len(nums)-1) 两端,通过双指针交替向中间移动,记录对于每个固定指针 k k k 的所有满足 n u m s [ k ] + n u m s [ i ] + n u m s [ j ] = = 0 nums[k] + nums[i] + nums[j] == 0 nums[k]+nums[i]+nums[j]==0 i , j i, j i,j 组合:

  • n u m s [ k ] > 0 nums[k] > 0 nums[k]>0 时直接break跳出:因为 n u m s [ j ] ≥ n u m s [ i ] ≥ n u m s [ k ] > 0 nums[j] \geq {nums[i]} \geq {nums[k] > 0} nums[j]nums[i]nums[k]>0,即 3 个数字都大于 0,在此固定指针 k k k 之后不可能再找到结果了。
  • k > 0 k > 0 k>0 n u m s [ k ] = = n u m s [ k − 1 ] nums[k] == nums[k - 1] nums[k]==nums[k1]时即跳过此元素 n u m s [ k ] nums[k] nums[k]:因为已经将 n u m s [ k − 1 ] nums[k - 1] nums[k1] 的所有组合加入到结果中,本次双指针搜索只会得到重复组合。

i , j i, j i,j分设在数组索引 ( k + 1 , l e n ( n u m s ) − 1 ) (k+1, len(nums)-1) (k+1,len(nums)1) 两端,当 i < j i < j i<j 时循环计算 t o t a l = n u m s [ k ] + n u m s [ i ] + n u m s [ j ] total = nums[k] + nums[i] + nums[j] total=nums[k]+nums[i]+nums[j],并按照以下规则执行双指针移动:

  • t o t a l < 0 total < 0 total<0时, i + = 1 i += 1 i+=1;(不需要跳过重复数字,下一轮循环条件不会满足,自动跳出)
  • t o t a l > 0 total > 0 total>0时, j − = 1 j -= 1 j=1
  • t o t a l = = 0 total == 0 total==0时,记录组合 [ k , i , j ] [k, i, j] [k,i,j] r e s res res,执行 i + = 1 i += 1 i+=1 j − = 1 j -= 1 j=1 并跳过所有重复的 n u m s [ i ] nums[i] nums[i] n u m s [ j ] nums[j] nums[j],防止记录到重复组合。

复杂度分析:

  • 时间复杂度 O ( N 2 ) O(N^2) O(N2):其中固定指针 k k k 循环复杂度 O ( N ) O(N) O(N),双指针 i , j i, j i,j 复杂度 O ( N ) O(N) O(N)
  • 空间复杂度 O ( 1 ) O(1) O(1):指针使用常数大小的额外空间。
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()   # 对数组进行排序
        res = []
        for k in range(len(nums)-2):  # 固定一个数
            # nums[j]≥nums[i]≥nums[k]>0,即3个数字都大于0
            if nums[k] > 0:
                break
            # 跳过此元素nums[k]:因为已经将nums[k - 1]的所有组合加入到结果中
            if k > 0 and nums[k] == nums[k-1]:
                continue

            # k每遍历一次数组,i和j从k+1 -> len(nums)-1移动一次
            i, j = k+1, len(nums)-1
            while i < j:
                total = nums[k] + nums[i] + nums[j] # 求三个数的和
                if total > 0:   # 值大于0, j前移
                    j -= 1
                elif total < 0: # 值小于0, i后移
                    i += 1
                else:   # 值=0
                    res.append([nums[k], nums[i], nums[j]])
                    # 如果临近数值相同,则i指针后移
                    while i < j and nums[i] == nums[i+1]:
                        i += 1
                    # 如果临近数值相同,则j指针前移
                    while i < j and nums[j] == nums[j-1]:
                        j -= 1
                    i += 1  # 移动指针,查找其它可能结果
                    j -= 1
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值