【LeetCode】一起探究三数之和的奥秘

news2024/11/24 7:06:14

在这里插入图片描述

Problem: 15. 三数之和

文章目录

  • 题目解析
  • 算法原理分析
    • 排序 + 暴力枚举 + set去重
    • 排序 + 单调性 + 双指针划分思想
  • 复杂度
  • Code

题目解析

首先我们来分析一下本题的思路

  • 题目说到要我们在一个整数数组中去寻找三元组,而且呢这三个数字所相加的和为0,而且呢这三个数的位置还要不一样
  • 我们以这个示例1为例来看看,我列出了3种可能性,分别是[-1, 0, 1][-1, 2, -1][0, 1, -1],不过呢我们仔细看这个题意中的概念,又可以知道这些三元组还不可以重复,那么第一个和第三个我们就需要考虑到去重

1.jpg

💬 但是要如何去求解本题呢,怎么去找出这些三元组呢?找出之后又该如何去做一个去重的操作呢?我们马上进行算法原理分析

算法原理分析

接下去我将通过分析算法原理来讲解本题该如何去进行求解

排序 + 暴力枚举 + set去重

首先第一种,我们能想到的一定是暴力枚举

  • 不过在这之前呢,我们首先要对这些整型数据去做一个排序的操作,在排完序之后我们通过暴力枚举的方式在找出两个三元组,可以看出这两个三元组的是重复的,所以我们要去做一个去重的操作
  • 对于这步操作的话如果读者有学习过C++中的unordered_set和Java中的Hashset的话就可以知道如果我们将重复的数据扔到集合中的话就会去做一个自动去重的操作

2.jpg

💬 对于上面这种解法的代码就不给出了,读者可以自己去写写看

排序 + 单调性 + 双指针划分思想

我们主要的话是来讲这种解法

  • 对于比较优的解法,我们首先应该要考虑到的就是【双指针】,如果有做过 和为s的两个数字,那么就可以知道使用双指针来进行求解的话会事半而功倍
  • 之前我们是利用双指针去求解两个的和,那现在要求解三个数的和呢?其实也是一样的,我们看到下面的这张图,首先的话我们将第一个数去做一个固定,然后呢让left指针指向i + 1的位置,right指针指向nums.size() - 1的位置,然后通过这两个指针的遍历去寻找不同的三元组即可

3.jpg

💬 那有同学说,这在遍历的过程中要怎么去进行判断呢?

  • 其实的话这很简单,因为我们首先固定了一个数a,因为【三数之和需要为0】,那么接下去在遍历后面的这两个数的时只需要判断它们相加之和为-a即可,也就是这个数a的相反数

4.jpg

所以我们来梳理一下这个逻辑

5.jpg

  • 再来模拟一下这个过程,具体的双指针判断逻辑就不解释了,读者可以去看看上面的那道题,然后再通过看下面的代码来理解这个匹配的过程

6.jpg

  • 最后呢我们可以看到这三个数被找到了,那此时就可以将其放入结果集中去

所以接下去呢我们还需要去处理一些【细节】方面的问题

  1. 首先第一个的话就是让这个leftright指针还需要继续去做移动的操作,因为我们是要找到这组数据中所有的三元组
left++;right--;
  1. 第二个的话就是我们在上面所说到过的话需要考虑到的去重问题,那要怎么去重呢?
  • 其实的话这很简单,当在执行完left++之后我们需要去判断当前所指的数字和上一个数字是否相同,如果出现了相同情况的话,就让left++,跳过这个数。对于上面的这段逻辑我们需要放在【while】循环中执行,因为可能出现很多连续相同的数据
while(nums[left - 1] == nums[left]) left++;
  • 那对于右侧的这个数也是一样,不过是当前的这个数和后一个数去进行比较
while(nums[right] == nums[right + 1]) right--;
  • 除了这两块的去重逻辑,其实还有一个地方也需要考虑到去重的问题,那就是外部读这个数a,读者可以思考一下,第一次我们已经对这个【-4】做了一轮的判断,那此时如果再去做一轮的判断的话就是多次一举了

7.jpg

  • 所以呢我们还需要对这个i做去重的逻辑,和left是一致的
// i的去重【防止越界】
i++;
while(nums[i] == nums[i - 1])  i++;

你认为这么就完了吗,那也太小瞧这道题了,毕竟也是力扣中的困难题

  • 我们来看一下下面的这种情况,所有的数字都是0,那么在第一次遍历的时候就天然满足了,于是就进入继续查找的逻辑,但是呢因为ilr所指的数据都是一样的,所以在进行【while】循环的时候会一直地进行移动,此时就会造成l超过r或者r超过l的情况,不仅如此,i在遍历的时候会碰到类似的情况

8.jpg

所以我们应该把去重的逻辑做一个修改,可以起到防止越界的效果✔

left++;right--;
// left与right的去重【防止越界】
while(left < right && nums[left - 1] == nums[left]) left++;
while(left < right && nums[right] == nums[right + 1]) right--;
// i的去重【防止越界】
i++;
while(i < n && nums[i] == nums[i - 1])  i++;

💬 以上就是【双指针】算法原理的分析,读者可以在阅读完之后试着自己写写看代码,很多细节的处理对代码能力的提升很有帮助

复杂度

  • 时间复杂度:

因为我们在遍历的时候, 每次去固定一个数a,然后下标为i,接着从[i + 1, n - 1]的地方进行遍历,复杂度为: O ( n 2 ) O(n^2) O(n2)

  • 空间复杂度:

因为没有去堆区申请任何空间,所以空间复杂度为 O ( 1 ) O(1) O(1)

Code

以下是整体的代码展示

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        // 1.排序
        sort(nums.begin(), nums.end());

        // 2.利用双指针解决问题
        vector<vector<int>> result;
        int n = nums.size();
        int i = 0;
        while(i < n)    // 固定a
        {
            // 若发现a > 0的话,则后面的nums[left] + nums[right]不会 < 0
            if(nums[i] > 0) break;  
            int left = i + 1, right = nums.size() - 1, target = -nums[i];
            while(left < right)
            {
                int sum = nums[left] + nums[right];
                if(sum < target){
                    left++;
                }else if(sum > target){
                    right--;
                }else{
                    // 找到了,放入结果集
                    result.push_back({nums[i], nums[left], nums[right]});
                    left++;right--;
                    
                    // left与right的去重【防止越界】
                    while(left < right && nums[left - 1] == nums[left]) left++;
                    while(left < right && nums[right] == nums[right + 1]) right--;
                }
            }
            // i的去重【防止越界】
            i++;
            while(i < n && nums[i] == nums[i - 1])  i++;
        }
        return result;
    }
};

在这里插入图片描述

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

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

相关文章

数据复制:构建大规模分布式系统的关键组成部分

数据复制对于构建可靠的大规模分布式系统至关重要。在本期中&#xff0c;我们将探讨常见的复制策略以及选择合适策略的关键因素。 在本期中&#xff0c;我们将以数据库为例进行讨论。请注意&#xff0c;复制不仅适用于数据库&#xff0c;还适用于缓存服务器&#xff08;如Redis…

探索Apache Hive:融合专业性、趣味性和吸引力的数据库操作奇幻之旅

文章目录 版权声明一 数据库操作二 Hive数据表操作2.1 表操作语法和数据类型2.2 Hive表分类2.3 内部表Vs外部表2.4 内部表操作2.4.1 创建内部表2.4.2 其他创建内部表的形式2.4.3 数据分隔符2.4.4 自定义分隔符2.4.5 删除内部表 2.5 外部表操作2.5.1 创建外部表2.5.2 操作演示2.…

BMS电池管理系统——电芯需求数据(三)

BMS电池管理系统 文章目录 BMS电池管理系统前言一、有什么基础数据二、基础数据分析1.充放电的截至电压2.SOC-OCV关系表3.充放电电流限制表4.充放电容量特性5.自放电率 总结 前言 在新能源产业中电芯的开发也占有很大部分&#xff0c;下面我们就来看一下电芯的需求数据有哪些 …

知名农业企业-九三食品选择泛微京桥通构建全程数字化采购管理

九三食品股份有限公司是北大荒集团旗下九三粮油工业集团有限公司的控股子公司&#xff0c;作为九三集团大豆精深加工生产及研发基地&#xff0c;九三食品公司可生产非转基因“九三”牌大豆油等植物油、食品添加剂、胶囊保健品、精油类产品、食用豆粕、豆饼粉和其它产品等七大类…

yo!这里是进程控制

目录 前言 进程创建 fork()函数 写时拷贝 进程终止 退出场景 退出方法 进程等待 等待原因 等待方法 1.wait函数 2.waitpid函数 等待结果&#xff08;status介绍&#xff09; 进程替换 替换原理 替换函数 进程替换例子 shell简易实现 后记 前言 学习完操作…

【更新至2022年】2000-2022年全国31省市以2000年为基期的实际GDP、名义GDP、GDP平减指数数据(含原始数据+计算过程+计算结果)

2000-2022年31省市名义GDP 实际GDP GDP平减指数 1、时间&#xff1a;2000-2022 2、范围&#xff1a;31省市 3、来源&#xff1a;GJ统计J和统计NJ 4、指标&#xff1a;名义GDP、地区生产总值指数&#xff08;上年100&#xff09;、实际GDP&#xff08;以2000年为基期&#x…

【光伏系统】将电流从直流转换为交流电的太阳能逆变器、太阳能跟踪系统来提高系统的整体性能及集成电池解决方案(Simulink仿真)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

(高阶)Redis 7 第10讲 单线程 与 多线程 入门篇

面试题 1.Redis 是单线程还是多线程 最早的版本3.x是单线程。 版本4.x,严格意义不是单线程。负责处理客户端请求的线程是单线程,开始加入异步删除。 6.0.x版本后明确使用全新的多线程来解决问题 2.说说IO多路复用3.Redis 为什么快IO多路复用+epoll函…

基于SSM的乡镇自来水收费系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【leetcode 力扣刷题】回文串相关题目(KMP、动态规划)

回文串相关题目 5. 最长回文子串动态规划中心扩展算法 214. 最短回文串336. 回文对 5. 最长回文子串 题目链接&#xff1a;5. 最长回文子串 题目内容&#xff1a; 题目就是要我们找s中的回文子串&#xff0c;还要是最长的。其实想想&#xff0c;暴力求解也行……就是遍历所有的…

系统移植MakefileREADME文件分析

Makefile # 指定交叉编译工具链前缀变量 CROSS_COMPILE arm-linux-gnueabihf- #指定文件名字变量 NAME interface ## #-g:编译时添加gdb调试信息 -marm: 将程序编译生成arm指令集 -Wall:编译时显示所有警告信息 #-O0:编译时添加优化等级 -O0:不优化 -O1:一级优化 #-f…

zabbix配置钉钉告警、和故障自愈、监控java

文章目录 1.配置钉钉告警server 配置web界面创建媒介给用户添加媒介测试告警 实现故障自愈功能监控Javazabbix server 安装java gateway配置 Zabbix Server 支持 Java gateway使用系统内置模板监控 tomcat 主机 1.配置钉钉告警 server 配置 钉钉告警python脚本 脚本1 cd /…

实相融、云启未来,智慧公厕让城市生活更美好

现代社会&#xff0c;随着科技的不断发展&#xff0c;人们对于城市生活的要求也在不断提升。在这个过程中&#xff0c;智慧公厕作为城市基础设施中的重要组成部分&#xff0c;正在发挥着越来越重要的作用。通过数字化、云管理、人工智能等未来的科技方式&#xff0c;智慧公厕为…

2024字节跳动校招面试真题汇总及其解答(一)

1. 【算法题】重排链表 给定一个单链表 L 的头节点 head ,单链表 L 表示为: L0 → L1 → … → Ln - 1 → Ln请将其重新排列后变为: L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例 1: 输入:hea…

嵌入式Linux驱动开发(同步与互斥专题)(二)

一、自旋锁spinlock的实现 自旋锁&#xff0c;顾名思义&#xff1a;自己在原地打转&#xff0c;等待资源可用&#xff0c;一旦可用就上锁霸占它。 ① 原地打转的是CPU x&#xff0c;以后CPU y会解锁&#xff1a;这涉及多个CPU&#xff0c;适用于SMP系统&#xff1b; ② 对于单…

【Vue】Router路由无法跳转问题整理

问题集 整理了部分Vue Router路由无法跳转问题&#xff1a; 顶层router-view只能被顶层路由配置内容使用&#xff1a;此问题异常表现在路由跳转但页面不变子路由跳转必需父路由对应的组件中存在router-view&#xff1a;此问题异常表现在路由跳转但页面不变 子路由配置路径会自…

uniapp项目实践总结(十三)封装文件操作方法

导语&#xff1a;在日常 APP 开发过程中&#xff0c;经常要进行文件的保存、读取列表以及查看和删除文件等操作&#xff0c;接下来就看一下具体的方法。 目录 原理分析方法实现实战演练案例展示 原理分析 主要是以下 API。 uni.saveFile&#xff1a;保存文件到本地缓存列表…

处理流程设计-系统设计-人机界面设计

处理流程设计-系统设计-人机界面设计 流程表示工具&#xff08;重点&#xff09; 流程表示工具&#xff08;重点&#xff09; 数据流图也是一种 IPO 图 NS图和PAD图

为什么说电子元器件采购如果不够专业就容易上当受骗

电子元器件采购如果不够专业就容易上当受骗的原因有多方面&#xff0c;主要包括以下几点&#xff1a; 众多供应商和产品&#xff1a;电子元器件市场涉及众多供应商和各种不同的产品。如果采购人员不具备足够的专业知识和经验&#xff0c;可能会难以识别哪些供应商和产品是可信赖…

flink 写入数据到 kafka 后,数据过一段时间自动删除

版本 flink 1.16.0kafka 2.3 流程描述&#xff1a; flink利用KafkaSource&#xff0c;读取kafka的数据&#xff0c;然后经过一系列的处理&#xff0c;通过KafkaSink&#xff0c;采用 EXACTLY_ONCE 的模式&#xff0c;将处理后的数据再写入到新的topic中。 问题描述&#xff1…