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

学习笔记--算法(双指针)7

三数之和

链接:

. - 力扣(LeetCode)


题目描述

给你⼀个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满⾜ i != j、i != k 且 j != 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

-10^5 <= nums[i] <= 10^5


解法(排序+双指针)

算法思路

本题与两数之和类似,是⾮常经典的⾯试题。

与两数之和稍微不同的是,题⽬中要求找到所有「不重复」的三元组。那我们可以利⽤在两数之和那⾥⽤的双指针思想,来对我们的暴⼒枚举做优化:

i. 先排序;

ii. 然后固定⼀个数 a ;

iii. 在这个数后⾯的区间内,使⽤「双指针算法」快速找到两个数之和等于 -a 即可。

但是要注意的是,这道题⾥⾯需要有「去重」操作:

i. 找到⼀个结果之后, left 和 right 指针要「跳过重复」的元素;

ii. 当使⽤完⼀次双指针算法之后,固定的 a 也要「跳过重复」的元素。

图解


代码

public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> ret = new ArrayList<>();//用来存放满足条件的所有数组[[nums[i],nums[left],nums[right]],[nums[i],nums[left],nums[right]],[nums[i],nums[left],nums[right]],....]//1.先排序(升序)Arrays.sort(nums);//2.先固定一个数i,再利用“双指针算法”去寻找i后面的区间中的能够相加等于-i的两个数int n = nums.length;//因为i在去重这一步会进行i++,来判断nums[i]是否等于nums[i-1],所以for循环的条件表达式中省略了i++for(int i = 0;i < n;){//判断nums[i]此时的值是否大于零,因为整个数组是事先进行顺序排序的,当nums[i]后面包括nums[i]本身大于零,那么后面进行双指针时,是找不到两数和为负数的情况的,也就没有必要继续找了,这是一个小优化!if(nums[i] > 0){break;}//在nums[i]后⾯的区间内,使⽤「双指针算法」快速找到两个数之和等于 -nums[i] 即可。跟两数之和一样,可以利用单调性来优化算法int left = i + 1;//left必须在固定的值i的左区间中int right = n - 1;int target = -nums[i];while(left < right){int sum = nums[left] + nums[right];//sum小于target,此时的left是最小值,right是最大值,两者和sum都小于target了,那么left和right左边的数相加的和,必定是小于target的,所以让left向右移动一位即可,没必要继续枚举//sum大于target,此时的right是最大值,而left是最小值,所以是因为right太大了,就要将right向左移动一位if(sum < target){left++;}else if(sum > target){right--;}else{ret.add(new ArrayList<Integer>(Arrays.asList(nums[i],nums[left],nums[right])));left++;right--;//缩小区间继续查找//找到⼀个结果之后, left 和 right 指针要「跳过重复」的元素,也就是去重,可以优化算法时间复杂度while(left < right && nums[left] == nums[left - 1]){left++;}while(left < right && nums[right] == nums[right + 1]){right--;}}}//去重:i,i++后也有可能会等于前一个i所指向的下标的值,所以,当nums[i]==nums[i-1]时,可以让i直接向后移动直到nums[i]!=nums[i-1]为止i++;//此处直接让i++,判断nums[i]是否等于nums[i-1]//i也需要确保不能越界while(i < n && nums[i] == nums[i - 1]  ){i++;}}return ret;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 控制反转(IOC)VS 依赖注入(DI)
  • Go 语言常量 6
  • 反射---Java
  • 达梦数据库的系统视图v$sql_stat
  • Element-UI自学实践
  • 【数据库】MySql深度分页SQL查询优化
  • 前端JS总结(下)之DOM
  • LVS原理——详细介绍
  • dos 常用命令整理
  • 微信小程序的广告变现收益怎么样?
  • 如何高效记录并整理编程学习笔记—笔记工具选择?
  • Windows Server 使用Docke部署挂载问题(安装后无限重启崩溃迁移镜像到D盘打包镜像)
  • SSH、FTP、SFTP相关协议详解
  • Android Framework之Pkms详解
  • fatal: The current branch master has no upstream branch.
  • hexo+github搭建个人博客
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • Asm.js的简单介绍
  • Date型的使用
  • eclipse的离线汉化
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • Koa2 之文件上传下载
  • Mac转Windows的拯救指南
  • mongo索引构建
  • Mysql5.6主从复制
  • React+TypeScript入门
  • React16时代,该用什么姿势写 React ?
  • vue总结
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 对超线程几个不同角度的解释
  • 给第三方使用接口的 URL 签名实现
  • 近期前端发展计划
  • 容器服务kubernetes弹性伸缩高级用法
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • FaaS 的简单实践
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #70结构体案例1(导师,学生,成绩)
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (笔记)M1使用hombrew安装qemu
  • (纯JS)图片裁剪
  • (二)学习JVM —— 垃圾回收机制
  • (附源码)php新闻发布平台 毕业设计 141646
  • (十三)Flink SQL
  • (实战篇)如何缓存数据
  • (四)stm32之通信协议
  • (一) springboot详细介绍
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (转)h264中avc和flv数据的解析
  • (转)使用VMware vSphere标准交换机设置网络连接
  • ***详解账号泄露:全球约1亿用户已泄露
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .NET Core 将实体类转换为 SQL(ORM 映射)