15. 三数之和(实际是双指针类型的题目)

news2024/9/19 7:50:23

15. 三数之和

15. 三数之和

给你一个整数数组 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 < = n u m s . l e n g t h < = 3000 3 <= nums.length <= 3000 3<=nums.length<=3000
  • − 1 0 5 < = n u m s [ i ] < = 1 0 5 -10^5 <= nums[i] <= 10^5 105<=nums[i]<=105

思路:

哈希解法
两层for循环就可以确定ab 的数值了,可以使用哈希法来确定0-(a+b)是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。

把符合条件的三元组放进[][]int切片中,然后再去重,这样是非常费时的,很容易超时。

去重的过程不好处理,有很多小细节,如果在面试中很难想到位。

时间复杂度可以做到 O ( n 2 ) O(n^2) O(n2),但还是比较费时的,因为不好做剪枝操作。

双指针

其实这道题目使用哈希法并不十分合适,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。

接下来我来介绍另一个解法:双指针法,这道题目使用双指针法 要比哈希法高效一些,那么来讲解一下具体实现的思路。

拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]

接下来如何移动left right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

如果nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到leftright相遇为止。

时间复杂度: O ( n 2 ) O(n^2) O(n2)

去重逻辑的思考

a的去重

说到去重,其实主要考虑三个数的去重。 a, b ,c, 对应的就是 nums[i],nums[left],nums[right]

a 如果重复了怎么办,anums里遍历的元素,那么应该直接跳过去。

但这里有一个问题,是判断 nums[i] 与 nums[i + 1]是否相同,还是判断 nums[i] nums[i-1] 是否相同。

有同学可能想,这不都一样吗。

其实不一样!

都是和 nums[i]进行比较,是比较它的前一个,还是比较它的后一个。

如果我们的写法是 这样:

if nums[i] == nums[i + 1] { // 去重操作
    continue
}

那我们就把 三元组中出现重复元素的情况直接pass掉了。 例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据{-1, -1 ,2}pass了,但实际上{-1, -1 ,2}是符合和为0的一个三元组。

我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!

所以这里是有两个重复的维度。

那么应该这么写:

if i > 0 && nums[i] == nums[i - 1] {
    continue
}

这么写就是当前使用 nums[i],我们判断前一位是不是一样的元素,在看 {-1, -1 ,2} 这组数据,当遍历到 第一个 -1 的时候,只要前一位没有-1,那么{-1, -1 ,2}这组数据一样可以收录到 结果集里。

这是一个非常细节的思考过程。

b与c的去重

bc也要去除重复的组合 例如【-1,0,1,1,2】,那么【-1,0,1】就是一个结果,而第二个1得到的还是【-1,0,1】,故让left++, 让b跳过第二个1

for left < right {
    if base + nums[left] + nums[right] == 0 {
        res = append(res,[]int{base,nums[left],nums[right]})
        // 上面找到一个组合后,以nums[i]作为基数的可能还有其他组合,此时移动left和right
        //同样,left和right也要去除重复的组合  例如【-1,0,1,1,2】,那么【-1,0,1】就是一个结果,而第二个1得到的还是【-1,0,1】,故让left++,跳过第二个1
        for left < right && nums[left + 1] == nums[left] { left++ }
        for left < right && nums[right - 1] == nums[right] { right-- }
        //当上面两个for结束时,说明num[left+1] != nums[left],nums[right-1]!=nums[right],此时我们要取的就是left+1和right-1的那两个数,即如下
        left++
        right--
    } else if base + nums[left] + nums[right] < 0 {
        left++
    } else {
        right--
    }
}

当然也可以用回溯法,找出所有符合条件的三元组,但是回溯法和暴力法直接三层遍历一样,时间复杂度是 O ( n 3 ) O(n^3) O(n3)

Go代码

func threeSum(nums []int) [][]int {
	/*对切片先进行排序,方便去除重复的结果,然后固定一个数,移动另外两个数*/
    if nums == nil { return nil}
    var res [][]int
    sort.Ints(nums)

	//由于是三个数之和,而另外的两个数都是取i之后的,因为取i之前的情况的三个数的组合已经包含在i还较小时作为固定数的循环中了
	// 从头至尾以当前数字作为固定的数字,至少需要三个数字,所以遍历到len(nums)-2即可
	for i := 0;i < len(nums) - 2;i++ {
        if i > 0 && nums[i] == nums[i - 1] {continue} // 对基数去除重复的情况
        base := nums[i] // 固定一个基数
        left ,right := i + 1, len(nums) - 1 // 双指针的两个数
        for left < right {
            if base + nums[left] + nums[right] == 0 {
                res = append(res,[]int{base,nums[left],nums[right]})
                // 上面找到一个组合后,以nums[i]作为基数的可能还有其他组合,此时移动left和right
                //同样,left和right也要去除重复的组合  例如【-1,0,1,1,2】,那么【-1,0,1】就是一个结果,而第二个1得到的还是【-1,0,1】,故让left++,跳过第二个1
                for left < right && nums[left + 1] == nums[left] { left++ }
                for left < right && nums[right - 1] == nums[right] { right-- }
                //当上面两个for结束时,说明num[left+1] != nums[left],nums[right-1]!=nums[right],此时我们要取的就是left+1和right-1的那两个数,即如下
                left++
                right--
            } else if base + nums[left] + nums[right] < 0 {
                left++
            } else {
                right--
            }
        }
    }
	return res   
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2140760.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Uniapp的alertDialog返回值+async/await处理确定/取消问题

今天在使用uniui的alertDialog时&#xff0c;想添加一个确定/取消的警告框时 发现alertDialog和下面的处理同步进行了&#xff0c;没有等待alaertDialog处理完才进行 查询后发现问题在于 await 关键字虽然被用来等待 alertDialog.value.open() 的完成&#xff0c;但是 alertDi…

Android中的冷启动,热启动和温启动

在App启动方式中分为三种&#xff1a;冷启动&#xff08;cold start&#xff09;、热启动&#xff08;hot start&#xff09;、温启动&#xff08;warm start&#xff09; 冷启动&#xff1a; 系统不存在App进程&#xff08;App首次启动或者App被完全杀死&#xff09;时启动A…

使用 GaLore 预训练LLaMA-7B

项目代码&#xff1a; https://github.com/jiaweizzhao/galorehttps://github.com/jiaweizzhao/galore 参考博客&#xff1a; https://zhuanlan.zhihu.com/p/686686751 创建环境 基础环境配置如下&#xff1a; 操作系统: CentOS 7CPUs: 单个节点具有 1TB 内存的 Intel CP…

F12抓包11:UI自动化 - Recoder(记录器)

课程大纲 使用场景&#xff08;导入和导出&#xff09;: ① 测试的重复性工作&#xff0c;本浏览器录制并进行replay&#xff1b; ② 导入/导出录制脚本&#xff0c;移植后replay&#xff1b; ③ 导出给开发进行replay复现bug&#xff1b; ④ 进行前端性能分析。 1、录制脚…

kubernetes 学习 尚硅谷

出自 https://www.bilibili.com/video/BV13Q4y1C7hS 相关命令 kubeadm init &#xff1a;将当前节点创建为主节点 kubectl get nodes&#xff1a;获取集群所有节点 kubectl apply -f xxx.yaml&#xff1a;根据配置文件&#xff0c;给集群创建资源 kubectl delete -f xx.yaml&…

【C++】模板进阶:深入解析模板特化

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与Queue 本章将…

判断关系模式的无损连接(表格法)

目录 前言 一、什么是无损连接&#xff1f; 二、如何判断无损连接&#xff1f; 1.表格法 2.示例题 D选项构造初始的判断表如下&#xff1a; 总结 前言 在数据库设计中&#xff0c;确保数据的完整性和有效性是至关重要的。在关系数据库中&#xff0c;函数依赖和无损连接是…

docker|Oracle数据库|docker快速部署Oracle11g和数据库的持久化(可用于生产环境)

一、 容器数据持久化的概念 docker做为容器化的领先技术&#xff0c;现在广泛应用于各个平台中&#xff0c;但不知道什么时候有一个说法是docker并不适用容器化数据库&#xff0c;说容器化的数据库性能不稳定&#xff0c;其实&#xff0c;这个说法主要是因为对docker的数据持…

零基础5分钟上手亚马逊云科技-利用API网关管理API

简介 欢迎来到小李哥全新亚马逊云科技AWS云计算知识学习系列&#xff0c;适用于任何无云计算或者亚马逊云科技技术背景的开发者&#xff0c;通过这篇文章大家零基础5分钟就能完全学会亚马逊云科技一个经典的服务开发架构方案。 我会每天介绍一个基于亚马逊云科技AWS云计算平台…

C语言 ——— 编写代码,将一个长整数用逗号隔开,每3位一个逗号,并输出打印

目录 题目要求 代码实现 题目要求 对于一个较大的整数 N (1 < N < 2,000,000,000) &#xff0c;将 N 每个 3 位加上一个逗号&#xff0c;并且最后输出打印 举例说明&#xff1a; 输入&#xff1a;1980364535 输出&#xff1a;1,980,364,535 代码实现 代码演示&#…

详解JUC

Java并发工具包&#xff08;Java Util Concurrent&#xff0c; 简称JUC&#xff09;是Java提供的一组用于简化多线程编程的类和接口&#xff0c;它包含了用于线程同步、并发数据结构、线程池、锁、原子操作以及其他并发实用工具的丰富集合。 1. 线程池 线程池是 Java 并发编程…

【Go】Go语言中的数组基本语法与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

GUI编程13:JDialog弹窗

视频链接&#xff1a;15、JDialog弹窗_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1DJ411B75F?p15&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 package com.yundait.lesson04;import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; im…

为你的 Github 仓库引入自动构建的Github Pages静态页面

1. 设置config文件 在Github仓库根目录下创建_config.yml文件。其中的内容为&#xff1a; plugins:- jekyll-relative-links relative_links:enabled: truecollections: true include:- CONTRIBUTING.md- README.md- LICENSE.md- COPYING.md- CODE_OF_CONDUCT.md- CONTRIBUTI…

通过解预测和机器学习促进蚁群优化

文章目录 Abstract1. Introduction2. Background and related work2.1 定向越野问题2.2 ACO优化Abstract ML - ACO 算法的第一阶段,使用一组已知最优解的小定向越野问题实例训练一个 ML 模型。具体来说,使用分类模型根据问题特定的特征和统计度量来判断一条边是否属于最优路…

SSH工具 MobaXterm的使用与快捷配置

软件下载/安装与链接服务器/本地虚拟机 文章目录 软件下载/安装与链接服务器/本地虚拟机软件下载软件安装使用软件链接非本地机器并设置用户密码我不想有确定密码的弹窗 其余便捷配置配置右键粘贴SSH链接设置 软件下载 如果你访问不了这个网址&#xff0c;可以评论区找博主或者…

黑神话悟空黑风山攻略

在黑神话悟空中&#xff0c;玩家一开始来到黑风山的地图就会站在前山土地庙。 下面小编将会根据黑风山地图的地标来进行路线攻略推荐&#xff0c; 玩家可以一边查看游戏地图一边了解这些路线的动线是怎样的。 在苍狼林前山捡到药材老山参&#xff0c;跟随金色光线找到附近的土…

【机器学习】:解锁数据背后的智慧宝藏——深度探索与未来展望

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 引言一、深入机器学习的内在机制二、最新进展与趋势三、对未来社会的深远影响结语 引言 在上一篇博客中&#xff0c;我们初步探讨了机器学习如何成为解锁数据背后智慧的关键工具。现在&#xff0c;让…

网盘隐私照片泄露?教你如何保护自己的隐私照片!

网盘内的隐私照片 好兄弟最近遇到了一个困难&#xff1a;“我之前一直都是把照片存在网盘里面的&#xff0c;但是最近听说了某网盘的照片泄露了&#xff0c;自己的生活照啊&#xff0c;私密照啊都被人看光了&#xff0c;这太可怕了&#xff01;我现在也很担心自己的网盘上照片…

Android MediaPlayer + GLSurfaceView 播放视频

Android使用OpenGL 播放视频 概述TextureView的优缺点OpenGL的优缺点 实现复杂图形效果的场景参考 概述 在Android开发中&#xff0c;使用OpenGL ES来渲染视频是一种常见的需求&#xff0c;尤其是在需要实现自定义的视频播放界面或者视频特效时。结合MediaPlayer&#xff0c;我…