当前位置: 首页 > news >正文

【Python刷力扣hot100】15. 3Sum

问题

给定一个整数数组 nums,返回所有满足条件的三元组[nums[i], nums[j], nums[k]],使得i != ji != kj != k,且nums[i] + nums[j] + nums[k] == 0

注:解集不能包含重复的三元组。

例1:

Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
Explanation:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0.
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0.
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0.
不同的三元组为 [-1,0,1] 和 [-1,-1,2]。
需注意,输出结果的顺序以及三元组内部元素的顺序均不影响最终结果。

例2:

Input: nums = [0,1,1]
Output: []
Explanation: 唯一可能的三元组,其和不等于 0。

例3:

Input: nums = [0,0,0]
Output: [[0,0,0]]
Explanation: 唯一可能的三元组,其和等于 0。

约束:

  • 3 <= nums.length <= 3000
  • 105 <= nums[i] <= 105

解1:暴力

记数组长度为n

写3层for循环,遍历。时间复杂度为O(n3)O(n^3)O(n3),空间复杂度为O(1)O(1)O(1)

for i in range(n):for j in range(n):for k in range(n):if nums[i]+nums[j]+nums[k]==0):res.append([nums[i],nums[j],nums[k]])

优化遍历次数,变为n(n−1)(n−2)6\frac{n(n-1)(n-2)}{6}6n(n1)(n2),但时间复杂度仍为O(n3)O(n^3)O(n3),空间复杂度为O(1)O(1)O(1)

n = len(nums)
res = []
for i in range(n):for j in range(i + 1, n):  # j 从 i+1 开始,保证 j > ifor k in range(j + 1, n):  # k 从 j+1 开始,保证 k > jif nums[i] + nums[j] + nums[k] == 0:res.append([nums[i], nums[j], nums[k]])

解2:排序+双指针

先将数组进行排序,之后使用双指针,将后两层for循环的复杂度从O(n2)O(n^2)O(n2)优化到O(n)O(n)O(n)。对后两层循环,原先是遍历2次数组的时间复杂度,现在是遍历一次数组的时间复杂度。

i表示第一层循环下标,对每个i,初始状态下,左指针l指向i+1,右指针r指向n-1

算法流程如下:

  1. 先对数组进行排序(从小到大)
  2. 遍历数组,核心改进在后两层遍历
    • 若第一个数nums[i]>0,则说明后面的nums[l]>0 , nums[r]>0,之后的nums[i]+nums[j]+nums[k]都大于0,所以此时就跳出循环。
    • 跳过重复元素,避免重复解。i,l,r在移动时都要跳过重复元素
    • 对每层i,判断nums[i]+nums[l]+nums[r]
      • nums[i]+nums[l]+nums[r]==0,则产生一个解,加入list,之后移动lr
      • nums[i]+nums[l]+nums[r]>0,则说明当前值过大,移动r,从而减小数值
      • nums[i]+nums[l]+nums[r]<0,则说明当前值过小,移动l,从而增大数值

对边界情况要加特殊判断,如数组为空或者数组元素数量小于3的时候,显然是无解的。但这里题目中给了3 <= nums.length <= 3000,所以这里不用特判。

时间复杂度O(n2)O(n^2)O(n2):排序O(nlogn)O(nlogn)O(nlogn),用i遍历数组O(n)O(n)O(n),用lr遍历数组O(n)O(n)O(n),所以遍历数组为O(n2)O(n^2)O(n2),总时间复杂度为O(nlogn+n2)=O(n2)O(nlogn+n^2)=O(n^2)O(nlogn+n2)=O(n2)
空间复杂度O(1)O(1)O(1):随着问题规模(数组长度n)的增大,所需要的额外空间是不变的,只存储有限数量的变量即可。

class Solution:def threeSum(self, nums: List[int]) -> List[List[int]]:ans = []n = len(nums)nums.sort() # 排序for i in range(n):if nums[i] > 0: # 特判return ansif i > 0 and nums[i] == nums[i - 1]: # 特判continuel = i + 1r = n - 1while l < r: # 双指针if nums[i] + nums[l] + nums[r] == 0:ans.append([nums[i], nums[l], nums[r]])while l < r and nums[l] == nums[l + 1]:l = l + 1while l < r and nums[r] == nums[r - 1]:r = r - 1l = l + 1r = r - 1elif nums[i] + nums[l] + nums[r] > 0:r = r - 1else:l = l + 1return ans

思考

while l < r and nums[l] == nums[l + 1]:l = l + 1
while l < r and nums[r] == nums[r - 1]:r = r - 1

为什么这里的while要加个l<r?不加的话会出什么问题?举个反例

这个问题中,双指针去重时不加 l < r 会导致数组越界,结合题目背景(存在重复元素、三元组和为0的场景),可以举以下典型反例。

反例场景:数组包含连续重复的“0”(如 nums = [0,0,0,0]

  1. 排序后数组[0,0,0,0]n=4
  2. 第一次循环(i=0)
    • nums[i] = 0(不大于0,不触发提前返回),且 i=0 无前置元素,进入双指针逻辑。
    • 初始 l=1r=3,三数和 0+0+0=0,满足条件,将 [0,0,0] 加入结果集。
  3. 进入第一个去重循环(不加 l < r
    • 代码变为 while nums[l] == nums[l + 1],此时 l=1
      • 第一次判断:nums[1] == nums[2](0==0),成立,l 变为 2。
      • 第二次判断:nums[2] == nums[3](0==0),成立,l 变为 3。
      • 第三次判断:nums[3] == nums[4],但数组最大索引为 3(长度为4),nums[4] 超出范围,直接抛出 IndexError: list index out of range

参考

https://leetcode.cn/problems/3sum

http://www.dtcms.com/a/532542.html

相关文章:

  • MacOS平台Keil代替方案
  • 建设项目技术服务网站笋岗网站建设
  • 【AI原生架构:数据架构】10、从主数据治理到价值落地
  • jQuery JSONP详解
  • GitHub等平台形成的开源文化正在重塑和解
  • 网站首页包含的内容wordpress扩展class名称
  • MCoT在医疗AI工程化编程的实践手册(上)
  • 济南网站建设淄博外贸网站哪家好
  • 阮一峰《TypeScript 教程》学习笔记——类型工具
  • 怎样做钓鱼网站网站建设电话营销话术
  • 51c大模型~合集32
  • 生物化学Learning Track(14)酶催化机制
  • 力扣2:两数相加
  • 构建通用并发下载工具:用Golang重构wget脚本的实践分享
  • 多国语言 网站源码邦邻营销型网站建设
  • 深圳网站制作服杭州专业网站
  • (N_156)基于springboot,vue小区物业管理系统
  • 短信验证码
  • mysql的 in 用法
  • 《考研408数据结构》第六章(5.1+5.2+5.3树、二叉树、线索二叉树)复习笔记
  • Python如何做语义分析
  • apipost如何设置mock接口
  • 网站流量显示openresty wordpress
  • Python装饰器解包装技术详解:从原理到高级应用
  • Spring事务自调用失效问题:Spring 默认使用代理(proxy)来实现事务拦截:只有通过代理对象的调用才会触发事务增强
  • 兰州网站seo收费标准张槎网站建设
  • Vue Pinia 状态管理实战指南
  • 向量内积可看作 1 行 ×1 列的矩阵乘法,矩阵乘法则可拆成 多个向量内积的集合
  • 做社区网站怎么做巫山做网站哪家强
  • RabbitMQ -- 保障消息可靠性