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

[优选算法专题一双指针——三数之和]

题目链接

LeetCode三数之和

题目描述

题目解析

一、问题核心与解题思路

问题:给定整数数组 nums,返回所有不重复的三元组 [a, b, c],满足 a + b + c = 0 且 a、b、c 对应数组中不同索引的元素。

核心难点

  1. 找到所有满足条件的三元组;
  2. 避免结果中出现重复的三元组。

解题思路:采用 「排序 + 双指针」 组合策略,具体步骤如下:

  1. 排序数组:为双指针查找和去重提供基础;
  2. 固定第一个元素:通过外层循环遍历每个可能的第一个元素 a = nums[i]
  3. 双指针找剩余两个元素:在内层用左右指针(left = i+1right = n-1)寻找 b 和 c,使得 a + b + c = 0
  4. 去重处理:对三个指针跳过重复元素,避免生成重复三元组。

注意:

在去重的操作中可以使用set去重,但是我们这里推荐,因set 去重虽然能实现功能,但会显著降低效率,且编写代码的复杂程度也会提升,违背了「三数之和」的最优解法思路。因此,更推荐通过排序 + 跳过重复元素的方式去重,既高效又简洁。

这里举例一个已经排过序的新数组:

我们先固定第一个数:

这里就转化成了和为sum的问题,这里的s就是这个固定的数的相反数。也就是:

所以这里解决步骤:

  1. 先对整个数组排序
  2. 固定一个数 a(这里有个小优化,后续介绍)
  3. 在该数后面的区间内,利用双指针算法快速找到两个数的和为 -a 的即可。

注意:这里只是找到了符合条件的情况,并没有去重。

接下来就是处理细节问题了,也就是去重确保所有符合条件的情况不落下。

二、关键逻辑与优化解析
  1. 排序的作用
    排序后:

    • 相同元素相邻,便于跳过重复值(去重核心);
    • 数组有序,可通过双指针高效调整三数之和的大小(左移减小、右移增大)。
  2. 外层循环的优化

    • if (nums[i] > 0) break
      排序后数组递增,若 nums[i] > 0,则 nums[left] 和 nums[right] 均 ≥ nums[i],三数之和必 > 0,无需继续循环。
    • if (i + 2 < n && nums[i] + nums[i+1] + nums[i+2] > 0) break
      对于当前 i,最小的可能组合是 nums[i] + nums[i+1] + nums[i+2](后两个是最小的剩余元素)。若此和 > 0,说明当前 i 及更大的 i 都无有效组合,直接退出。
  3. 双指针的移动逻辑

    • 当 sum == 0:找到有效三元组,加入结果后,需同时移动左右指针并跳过重复值(否则会生成相同三元组);
    • 当 sum < 0:和太小,左指针右移(增大 nums[left]);
    • 当 sum > 0:和太大,右指针左移(减小 nums[right])。
  4. 去重的关键细节

    三、完整代码

    • 对 i 去重:避免因固定元素重复导致的三元组重复(如 [-1, -1, 2] 与 [-1, -1, 2]);
    • 对 left 和 right 去重:找到一组解后,需跳过相邻的相同元素(如 [-2, 0, 0, 2, 2] 中,找到 [-2, 0, 2] 后需跳过重复的 0 和 2)。
四、常见错误点
  1. 越界问题
    需确保 i + 2 < n 再判断 nums[i] + nums[i+1] + nums[i+2],否则可能访问无效索引。

  2. 去重时机错误
    必须在找到有效三元组后再去重(如 sum == 0 时),不能提前去重,否则可能漏掉合法解。

  3. 指针移动遗漏
    找到有效解后,需同时移动 left 和 right(而非只移动一个),否则可能陷入死循环。

五、复杂度分析
  • 时间复杂度

    • 排序:O(n log n)
    • 外层循环 O(n) + 内层双指针 O(n),整体为 O(n²)
      总时间复杂度:O(n²)(排序可忽略)。
  • 空间复杂度

    • 除结果存储外,仅使用常数级额外空间(指针、临时变量),故为 O(1)(若考虑排序的栈空间,最坏为 O(log n))。

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

相关文章:

  • Google再次颠覆自家模型,使用 MoR 模型打破 Transformer 模型壁垒
  • Java选手如何看待Golang
  • webapi项目添加访问IP限制
  • 根据字符出现频率排序
  • 【Bellman负环】Cycle Finding
  • (0️⃣基础)程序控制语句(初学者)(第3天)
  • 调用API接口返回参数缺失是什么原因导致的?
  • [3D数据存储] 对象 | OObject | IObject | 属性 | O<类型>Property | I<类型>Property
  • 安全基础DAY2-等级保护
  • linux-文件系统
  • AD8032ARZ-REEL7 ADI亚德诺 运算放大器 集成电路IC
  • 阿拉伯文识别技术:为连接古老智慧与数字未来铺设了关键道路
  • scratch笔记和练习-第11课:穿越峡谷
  • Cell-cultured meat: The new favorite on the future dining table
  • AR眼镜:能源行业设备维护的“安全守护者”
  • Shell脚本实现自动封禁恶意扫描IP
  • 考研复习-计算机组成原理-第四章-指令系统
  • nvm安装低版本的node失败(The system cannot find the file specified)
  • Mysql 如何使用 binlog 日志回滚操作失误的数据
  • 系统构成与 Shell 核心:从零认识操作系统的心脏与外壳
  • 物联网电能表在企业能耗监测系统中的应用
  • 人工智能与交通:出行方式的革新
  • Android 监听task 栈变化
  • 基于R语言,“上百种机器学习模型”学习教程 | Mime包
  • qt qtablewidget自定义表头
  • ubantu20.04 orin nx 显示器驱动
  • 【C++】类和对象--类中6个默认成员函数(2) --运算符重载
  • 【C#】掌握并发利器:深入理解 .NET 中的 Task.WhenAll
  • Docker容器部署前端Vue服务
  • 复杂路况误报率↓78%!陌讯轻量化模型在车辆违停识别的边缘计算优化​