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

力扣hot100----15.三数之和(java版)

1、题目描述

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

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]输出:[[-1,-1,2],[-1,0,1]]解释: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:

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

示例 3:

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

2、思路

方法一:暴力破解

class Solution{public List<List<Integer>> threeSum(int[] nums){Set<List<Integer>> resultSet = new HashSet<>();//使用Set自动去重//三重循环:i,j,k互不相同for(int i = 0;i<nums.length -2;i++){for(int j = i+1;j<nums.length-1;j++){for(int k = j+ 1;k<nums.length;k++){if(nums[i]+nums[j]+nums[k] == 0){//创建一个三元组,排序后加入Set(避免[0,-1,1]和[-1,0,-1]被认为不同)List<Integer> triplet = Arrays.asList(nums[i],nums[j],nums[k]);Collections.sort(triplet);//排序保证顺序一致resultSet.add(triplet);}}}}//转成List返回return new ArrayList<>(resultSet);}
}

/**Arrays.asList(...)
将一组元素(或一个数组)快速包装成一个固定大小的 List,便于临时使用或传递给需要集合参数的方法。
*/

注意:这种暴力破解的方法是可以的,但是在算法或者数据规模较大的情况下,会超出时间限制,因为它的时间复杂度是O(n^3),空间复杂度为O(1)

方法二:排序 + 双指针(Two Pointers)

时间复杂度:O(n²)

空间复杂度:O(1)(不考虑结果存储空间)

 步骤 1:排序 —— 为什么?
 Arrays.sort(nums);

为什么排序?

  • 排序后,我们可以使用双指针技巧来高效查找两个数之和等于目标值。

  • 排序后,可以轻松跳过重复元素,避免重复三元组。

  • 排序是双指针法的前提。

 时间复杂度:O(n log n)

 步骤 2:外层循环固定第一个数 —— 为什么?
for (int i = 0; i < nums.length - 2; i++) {if (i > 0 && nums[i] == nums[i - 1]) continue; // 跳过重复...}

为什么从 0 到 len-2?

  • 因为要留出两个位置给 left right 指针。

  • 边界:i < nums.length - 2

 为什么跳过重复?

  • 如果 nums[i] == nums[i-1],说明以这个数开头的组合我们已经处理过了,跳过避免重复三元组。

步骤 3:双指针找另外两个数 —— 为什么?
 int left = i + 1, right = nums.length - 1;while (left < right) {int sum = nums[i] + nums[left] + nums[right];if (sum < 0) left++;else if (sum > 0) right--;else {// 找到一组解list.add(Arrays.asList(nums[i], nums[left], nums[right]));// 跳过重复while (left < right && nums[left] == nums[left + 1]) left++;while (left < right && nums[right] == nums[right - 1]) right--;left++;right--;}}

 为什么双指针能工作?

  • 数组已排序,所以:

    • 如果当前三数之和 太小 → 需要更大的数 → left++

    • 如果当前三数之和 太大 → 需要更小的数 → right--

    • 如果 等于 0 → 找到答案,记录并移动双指针继续找

为什么移动指针时还要跳过重复?

  • 例如:[-2, 0, 0, 2, 2],当 i=0left=1, right=4,找到 [-2,0,2]

  • 如果不跳过,left=2, right=3 又会找到相同的 [-2,0,2] → 重复!

 边界条件(Corner Cases)

输入说明处理方式
nums.length < 3不足三个数直接返回空列表
全是 0:[0,0,0,0]只能有一个 [0,0,0]通过跳过重复处理
全是正数或负数不可能和为 0双指针自然不会找到解
有重复元素[-1,-1,0,1]通过 i > 0 && nums[i]==nums[i-1] 跳过
 Java 数据结构与算法知识点详解
(1) List<List<Integer>>
 List<List<Integer>> result = new ArrayList<>();

知识点:

  • Java 泛型:List<List<Integer>> 表示“列表的列表”,每个元素是一个 List<Integer>

  • ArrayList List 接口的常用实现,支持动态扩容。

  • 添加元素:result.add(Arrays.asList(a, b, c)) —— 快速创建不可变列表。

(2)Arrays.sort(int[])
 Arrays.sort(nums);

知识点

  • 对原数组进行升序排序

  • 底层使用双轴快排(Dual-Pivot QuickSort),平均 O(n log n),不稳定排序。

  • 原地排序,不额外占用空间。

(3) Arrays.asList(...)
list.add(Arrays.asList(nums[i], nums[left], nums[right]));

知识点

  • 快速将多个元素包装成 List

  • 返回的是固定大小的列表(基于数组),不能 add/remove,但这里只是用于添加到外层列表,没问题。

  • 如果需要可变列表,可以用 new ArrayList<>(Arrays.asList(...)),但没必要。

(4)双指针(Two Pointers)算法

核心思想:

  • 有序数组中,用两个指针从两端向中间逼近,根据条件移动指针。

  • 常用于:

    • 两数之和

    • 三数之和

    • 最接近的三数之和

    • 盛最多水的容器

优势:

  • 时间复杂度从 O(n²) 降到 O(n)(在固定第一个数的前提下)

  • 空间复杂度 O(1)

完整代码
class Solution {public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> result = new ArrayList<>();//边界:不足三个数if (nums == null || nums.length < 3) {return result;}//1、排序Arrays.sort(nums);//2、遍历一个数for (int i = 0; i < nums.length - 2; i++) {//跳过重复的第一个数if (i > 0 && nums[i] == nums[i - 1]) {continue;}int left = i + 1;int right = nums.length - 1;//3、双指针找另外两个数while (left < right) {int sum = nums[i] + nums[left] + nums[right];if (sum < 0) {left++;//和太小,左指针右移} else if (sum > 0) {right--;//和太小,右指针左移} else {//找到一组解result.add(Arrays.asList(nums[i], nums[left], nums[right]));//跳过重复的left和rightwhile (left < right && nums[left] == nums[left + 1])left++;while (left < right && nums[right] == nums[right - 1])right--;//移动指针继续查找left++;right--;}}}return result;}
}
http://www.dtcms.com/a/557065.html

相关文章:

  • 网站建设最重要的是什么什么是网站的主页
  • 影视传媒网站源码成华区建设局网站
  • 快速搭建网站 开源网络营销推广的目的是什么
  • 超越传统:大型语言模型在文本分类中的突破与代价
  • 【洛谷算题】顺序,分支,循环结构部分题目分享
  • Jmeter吞吐量控制器详解
  • 最全网站源码分享哈尔滨建设发展集团有限责任公司
  • 机器学习-KNN算法示例
  • 【随机访问介质访问控制-1】为什么纯 ALOHA 效率不到 20%?3 大随机访问 MAC 协议拆解
  • 有关电子商务网站建设与维护的书籍具有价值的响应式网站
  • C++笔记(面向对象)定义虚函数规则 运行时多态原理
  • 自然语言处理(NLP)之文本预处理:词元化——以《时间机器》文本数据集为例
  • Java-165 Neo4j 图论详解 欧拉路径与欧拉回路 10 分钟跑通:Python NetworkX 判定实战
  • WindowsRE文件夹不显示
  • 【PID】非标准PID控制是否影响控制目标 chapter1(补充)思考
  • 数码和easy
  • 做网站跟app的区别做网站的要求
  • 酷维网站模版wordpress 分类页id
  • MySQL查询一行数据为何变慢?深度排查与优化指南
  • Crashpad介绍
  • 博兴县建设局网站襄阳云平台网站建设
  • 若依分离版前端部署在tomcat刷新404的问题解决方法
  • qcustomplot 显示坐标轴
  • Java Web 项目打包部署全解析:从 IDEA 配置到 Tomcat 运行
  • 如何让网站收录公司名免费网络空间搜索引擎
  • 上海门户网站建设方案河源网络公司
  • WebSocket实战:构建Spring Boot实时聊天应用
  • Go高并发在企业级项目中的实战应用:数据库访问与GIN+GORM深度实践
  • 在网站写小说怎么做封面产品宣传册设计与制作
  • AI学习和研究——环境部署