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

三数之和[中等]

优质博文:IT-BLOG-CN

一、题目

给你一个整数数组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

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

二、代码

排序 + 双指针: 题目中要求找到所有「不重复」且和为0的三元组,这个「不重复」的要求使得我们无法简单地使用三重循环枚举所有的三元组。这是因为在最坏的情况下,数组中的元素全部为0,即[0,0,0,0,0]任意一个三元组的和都为0。如果我们直接使用三重循环枚举三元组,会得到O(N3)个满足题目要求的三元组(其中N是数组的长度)时间复杂度至少为O(N3)。在这之后,我们还需要使用哈希表进行去重操作,得到不包含重复三元组的最终答案,又消耗了大量的空间。这个做法的时间复杂度和空间复杂度都很高,因此我们要换一种思路来考虑这个问题。

「不重复」的本质是什么?我们保持三重循环的大框架不变,只需要保证:第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。

也就是说,我们枚举的三元组(a,b,c)满足a≤b≤ca,保证了只有(a,b,c)这个顺序会被枚举到,而(b,a,c)(c,b,a)等等这些不会,这样就减少了重复。要实现这一点,我们可以将数组中的元素从小到大进行排序,随后使用普通的三重循环就可以满足上面的要求。同时,对于每一重循环而言,相邻两次枚举的元素不能相同,否则也会造成重复。举个例子,如果排完序的数组为[]1,2,2,2,4]

nums.sort()
for first = 0 .. n-1// 只有和上一次枚举的元素不相同,我们才会进行枚举if first == 0 or nums[first] != nums[first-1] thenfor second = first+1 .. n-1if second == first+1 or nums[second] != nums[second-1] thenfor third = second+1 .. n-1if third == second+1 or nums[third] != nums[third-1] then// 判断是否有 a+b+c==0check(first, second, third)

这种方法的时间复杂度仍然为O(N3),毕竟我们还是没有跳出三重循环的大框架。然而它是很容易继续优化的,可以发现,如果我们固定了前两重循环枚举到的元素ab,那么只有唯一的c满足a+b+c=0。当第二重循环往后枚举一个元素b时,由于b′>b,那么满足a+b′+c′=0c′一定有c′<c,即c′在数组中一定出现在c的左侧。也就是说,我们可以从小到大枚举b,同时从大到小枚举c,即第二重循环和第三重循环实际上是并列的关系。

有了这样的发现,我们就可以保持第二重循环不变,而将第三重循环变成一个从数组最右端开始向左移动的指针,从而得到下面的伪代码:

nums.sort()
for first = 0 .. n-1if first == 0 or nums[first] != nums[first-1] then// 第三重循环对应的指针third = n-1for second = first+1 .. n-1if second == first+1 or nums[second] != nums[second-1] then// 向左移动指针,直到 a+b+c 不大于 0while nums[first]+nums[second]+nums[third] > 0third = third-1// 判断是否有 a+b+c==0check(first, second, third)

这个方法就是我们常说的「双指针」,当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从O(N2)减少至O(N)。为什么是O(N)呢?这是因为在枚举的过程每一步中,「左指针」会向右移动一个位置(也就是题目中的b),而「右指针」会向左移动若干个位置,这个与数组的元素有关,但我们知道它一共会移动的位置数为O(N),均摊下来,每次也向左移动一个位置,因此时间复杂度为O(N)

注意到我们的伪代码中还有第一重循环,时间复杂度为O(N),因此枚举的总时间复杂度为O(N2)。由于排序的时间复杂度为O(Nlog⁡N),在渐进意义下小于前者,因此算法的总时间复杂度为O(N2)

上述的伪代码中还有一些细节需要补充,例如我们需要保持左指针一直在右指针的左侧(即满足b≤c),具体可以参考下面的代码,均给出了详细的注释。

class Solution {public List<List<Integer>> threeSum(int[] nums) {//思想:1、先对 nums 进行排序// 2、先确定第一层循环,通过 0 - nums[x] 得到第二层和第三层的和// 3、将第二层和第三层汇总为一层,left = i + 1; right = nums.length - 1; 进行双指针移动,计算和,如果相等,加入队列,并继续移动指针,直到不满足 left < rightint left = 0, right = 0, size = 0;List<List<Integer>> res = new ArrayList();Arrays.sort(nums);for(int i = 0; i < nums.length; i++) {if (i > 0 && i < nums.length && nums[i] == nums[i-1]) {continue;}// i 发生变化之后,left 和 right 指针都需要发生变化。 第一次将right定义再外部,导致bugleft = i + 1;right = nums.length - 1;int tar = -nums[i];while(left < right) {if (nums[left] + nums[right] == tar) {List<Integer> temp = Arrays.asList(nums[i], nums[left], nums[right]);res.add(temp);// 数据去重while(left < right && nums[left] == nums[left + 1]) {++left;}while(right > left && nums[right] == nums[right - 1]) {--right;}++left;--right;} else if(nums[left] + nums[right] < tar){++left;} else {--right;}}}return res;}
}

时间复杂度: O(N2)其中N是数组nums的长度。
时间复杂度: O(N2),其中N是数组nums的长度。

相关文章:

三数之和[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你一个整数数组nums&#xff0c;判断是否存在三元组[nums[i], nums[j], nums[k]]满足i ! j、i ! k且j ! k&#xff0c;同时还满足nums[i] nums[j] nums[k] 0。请你返回所有和为0且不重复的三元组。 注意&#xff1a;答案中不可以…...

基于天牛须优化的BP神经网络(分类应用) - 附代码

基于天牛须优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于天牛须优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.天牛须优化BP神经网络3.1 BP神经网络参数设置3.2 天牛须算法应用 4.测试结果&#x…...

渗透波菜网站

免责声明 本文发布的工具和脚本&#xff0c;仅用作测试和学习研究&#xff0c;禁止用于商业用途&#xff0c;不能保证其合法性&#xff0c;准确性&#xff0c;完整性和有效性&#xff0c;请根据情况自行判断。如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利&#xff0c…...

Spring Boot:Dao层-实例介绍

目录 Dao层的作用Dao层的特点与 Service 层和 Controller 层的关系实例介绍MenuDaoOperatorLogDaoRoleDaoUserDao四个文件的共同点引用的包使用Repository注解继承JpaRepository接口接口的实体类的主键类型使用 Query()注解 Dao层的作用 负责与数据库进行交互&#xff0c;主要…...

接口测试入门:深入理解接口测试!

很多人会谈论接口测试。到底什么是接口测试&#xff1f;如何进行接口测试&#xff1f;这篇文章会帮到你。 一、前端和后端 在谈论接口测试之前&#xff0c;让我们先明确前端和后端这两个概念。 前端是我们在网页或移动应用程序中看到的页面&#xff0c;它由 HTML 和 CSS 编写…...

Redis微服务架构

Redis微服务架构 缓存设计 缓存穿透 缓存穿透是指查询一个根本不存在的数据&#xff0c;缓存层和存储层都不会命中&#xff0c;通常出于容错的考虑&#xff0c;如果从存储层查不到数据则不写入缓层。 缓存穿透将导致不存在的数据每次请求都要到存储层去查询&#xff0c;失去…...

【C++】 局部对象,引用返回

1、new 关键字 会在堆内申请空间&#xff0c;如果仅仅是普通调用构造函数&#xff0c;不会在堆内开辟空间。 2、函数调用会形成栈帧&#xff0c;进行压栈操作&#xff0c;函数调用结束&#xff0c;会进行弹栈。 函数内的局部对象&#xff0c;会随着弹栈&#xff0c;而被销毁(…...

线性代数中涉及到的matlab命令-第二章:矩阵及其运算

目录 1&#xff0c;矩阵定义 2&#xff0c;矩阵的运算 3&#xff0c;方阵的行列式和伴随矩阵 4&#xff0c;矩阵的逆 5&#xff0c;克莱默法则 6&#xff0c;矩阵分块 1&#xff0c;矩阵定义 矩阵与行列式的区别&#xff1a; &#xff08;1&#xff09;形式上行列式…...

计算机毕业设计选什么题目好?springboot 美食推荐系统

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…...

爆肝整理,Jmeter接口性能测试-跨线程调用变量实操(超详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、Jmeter中线程运…...

Maven导入程序包jakarta.servlet,但显示不存在

使用前提&#xff1a;&#xff08;Tomcat10版本&#xff09;已知tomcat10版本之后&#xff0c;使用jakart.servlet。而tomcat9以及之前使用javax.servlet。 问题描述&#xff1a;在maven仓库有导入了Jakarta程序包&#xff0c;但是界面仍然显示是javax。&#xff08;下图&…...

es6(二)——常用es6说明

ES6的系列文章目录 es6&#xff08;一&#xff09;——var和let和const的区别 文章目录 ES6的系列文章目录一、变量的结构赋值1.数组的结构赋值2.对象的结构赋值 二、模板字符串三、扩展运算符1.字符串的使用2.数组的使用 四、箭头函数1.普通函数的定义2.箭头函数的定义3.箭头…...

经典垃圾回收器

1.各垃圾回收器之间的配合使用关系 2.垃圾回收器的种类 2.1 Serial收集器&#xff08;默认新生代收集器&#xff09; Serial收集器是历史最悠久的收集器&#xff0c;曾经是新生代收集器的唯一选择&#xff0c;它是一个单线程工作的收集器&#xff0c;其“单线程”的意义不仅仅…...

台达DOP-B07S410触摸屏出现HMI no response无法上传的解决办法

台达DOP-B07S410触摸屏出现HMI no response无法上传的解决办法 台达触摸屏(B07S410)在上载程序时(显示No response from HMI)我以前的电脑是WIN7的,从来没出现过这样的问题,现在换成win10的,怎么都不行,(USB显示是一个大容量存储)换一台电脑(win10)有些行,有些不行…...

[资源推荐] 复旦大学张奇老师科研分享

刷B站的时候首页给我推了这个&#xff1a;【直播回放】复旦大学张奇教授亲授&#xff1a;人工智能领域顶会论文的发表指南先前也散漫地读了些许论文&#xff0c;但没有在一些宏观的方法论下去训练&#xff0c;读的时候能感觉出一些科研的套路&#xff0c;论文写作的套路&#x…...

C++数位动态规划算法:统计整数数目

题目 给你两个数字字符串 num1 和 num2 &#xff0c;以及两个整数 max_sum 和 min_sum 。如果一个整数 x 满足以下条件&#xff0c;我们称它是一个好整数&#xff1a; num1 < x < num2 min_sum < digit_sum(x) < max_sum. 请你返回好整数的数目。答案可能很大&…...

ip 网段设置 --chatGPT

问&#xff1a;host all all 127.0.0.1/32 scram-sha-256 里的 127.0.0.1/32 是什么含义 &#xff0c;要指定某个呢 gpt: 在 PostgreSQL 的 pg_hba.conf 文件中&#xff0c;127.0.0.1/32 是一个用于定义访问控制规则的CIDR&#xff08;无类域间路由&#xff09;标记&#xff0…...

使用JMeter进行接口测试教程

安装 使用JMeter的前提需要安装JDK&#xff0c;需要JDK1.7以上版本目前在用的是JMeter5.2版本&#xff0c;大家可自行下载解压使用 运行 进入解压路径如E: \apache-jmeter-5.2\bin&#xff0c;双击jmeter.bat启动运行 启动后默认为英文版本&#xff0c;可通过Options – Cho…...

文本生成解码策略

解码策略 1. sample实现了怎样的功能 不是直接选择概率最大的token&#xff0c;而是根据多项式分布进行采样获得下一个token 这里的概率通过设置一些策略&#xff0c;进行处理。例如&#xff0c;解码最小长度&#xff08;当长度小于该值的时候&#xff0c;eos的采样概率为0&am…...

华为数通方向HCIP-DataCom H12-831题库(单选题:221-240)

第221题 以下关于IS-IS的LSP分片功能的描述,正确的是哪一项? A、IS-IS的分片扩展功能的Mode-1模式,虚拟系统是需要参与路由SPF计算的 B、IS-IS的LSP分片功能,是用于让收到LSP分片报文的设备老化相关路由信息 C、IS-IS的分片扩展功能,是通过LSP报文中的LSPID实现的 D、IS-…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...

ThreadLocal 源码

ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物&#xff0c;因为每个访问一个线程局部变量的线程&#xff08;通过其 get 或 set 方法&#xff09;都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段&#xff0c;这些类希望将…...

字符串哈希+KMP

P10468 兔子与兔子 #include<bits/stdc.h> using namespace std; typedef unsigned long long ull; const int N 1000010; ull a[N], pw[N]; int n; ull gethash(int l, int r){return a[r] - a[l - 1] * pw[r - l 1]; } signed main(){ios::sync_with_stdio(false), …...