leetcode15 三数之和

1.哈希法

为了避免重复

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> temple;//使用 set 来存储符合条件的三元组,避免重复
        vector<vector<int>> out;//存放最终输出结果

        sort(nums.begin(),nums.end());//先进行数组排序

        for(int i = 0; i < nums.size(); i++){
            if(i > 0 && nums[i] == nums[i-1]) continue;//如果当前数值与上一轮循环所得数值相同、后续找到的三元组将相同,没必要再找,退出本轮
            unordered_set<int> hash;//定义哈希表存放待查找内容
            for(int j = i+1; j < nums.size(); j++){                
                if(hash.find(-(nums[i] + nums[j])) != hash.end()){
                    temple.insert({nums[i], nums[j], -(nums[i] + nums[j])});//哈希表中存在与当前nums[i]\nums[j]匹配的值 -(nums[i] + nums[j]),即找到了符合条件的三元组将其存入                 
                }
                hash.insert(nums[j]);//将nums[j]存入,可能成为后续寻找的-(nums[i] + nums[j])值
            }    
        }
        for(const auto& t: temple){
            out.push_back(t);
        }//遍历三元组将他们存入最终输出的变量中
        return out;
    }
};

在解决 三数之和(3Sum)问题时,先对数组进行排序是一个非常重要的步骤。排序不仅简化了问题的解决过程,还能帮助我们高效地处理重复元素和优化查找逻辑。以下是详细解释:

1. 排序的作用

(1)方便去重
  • 排序后,相同的元素会相邻排列。这样,我们可以通过简单的条件判断(如 nums[i] == nums[i - 1])来跳过重复的元素,避免重复解。

  • 例如,数组 [-1, -1, 0, 1, 2] 中,两个 -1 相邻,可以通过判断 nums[i] == nums[i - 1] 来跳过第二个 -1

(2)简化查找逻辑
  • 排序后,数组是有序的,可以利用 双指针法 来高效查找满足条件的三元组。

  • 双指针法的时间复杂度是 O(n)O(n),而哈希法的时间复杂度是 O(n2)O(n2),因此排序后使用双指针法可以显著提高性能。

(3)确保结果有序
  • 排序后,找到的三元组 (nums[i], nums[j], nums[k]) 也是有序的,这样可以避免重复解(如 [-1, 0, 1] 和 [0, -1, 1] 被视为不同的解)。


内层循环中,哈希表的作用是存储已经遍历过的 nums[j]。每次遍历到一个新的 nums[j] 时:

  • 先检查哈希表中是否存在 target = -nums[i] - nums[j]

    • 如果存在,说明 nums[i] + nums[j] + target = 0,即找到一个三元组。

  • 然后将当前的 nums[j] 加入哈希表,以便后续的 nums[j] 可以使用它来查找目标值。

nums[i] 是外层循环的固定值,它不会被用作目标值(target),因为 target 的定义是 -nums[i] - nums[j]

去重逻辑
  • 在外层循环中,通过 if (i > 0 && nums[i] == nums[i - 1]) continue; 跳过重复的 nums[i]

  • 在内层循环中,哈希表会自动处理重复的 nums[j],因为哈希表的特性是存储唯一的键。

  • const auto& triplet

    • auto自动推导变量类型。在这里,triplet 的类型是 const vector<int>&

    • const:表示 triplet 是只读的,不能修改。

    • &:表示引用,避免拷贝 vector<int>,提高效率。

2.双指针法

先排序,有一层for循环,定义指针i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

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

问题分析

  1. sum 的计算位置

    • 你在外层循环中计算了 sum = nums[i] + nums[left] + nums[right],但在内层循环中 left 和 right 会变化,sum 的值却没有更新,导致逻辑错误。

  2. 去重逻辑

    • 你在找到满足条件的三元组后,没有跳过重复的 nums[left] 和 nums[right],这可能导致重复解。(

      多加了 while (left < right && nums[right] == nums[right + 1]) right--; 这一行代码,其实就是把 需要执行的逻辑提前执行了,但并没有减少 判断的逻辑。

      最直白的思考过程,就是right还是一个数一个数的减下去的,所以在哪里减的都是一样的。

      所以这种去重 是可以不加的。 仅仅是 把去重的逻辑提前了而已。

      )

  3. set 的使用

    • 你使用 set<vector<int>> 来存储结果,虽然可以避免重复解,但效率较低,因为 set 的插入操作是 O(log⁡n)O(logn) 的。

  4. 边界条件

    • 你没有处理数组长度小于 3 的情况。

class Solution{
public:
    vector<vector<int>> threeSum(vector<int>& nums){
        sort(nums.begin(), nums.end());
        vector<vector<int>> out;
        if (nums.size() < 3) {
            return out;
        }
        for(int i = 0; i < nums.size(); i++){
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            int left = i + 1, right = nums.size() - 1;
            while(left<right){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum > 0){
                    right --;
                }
                if(sum < 0){
                    left ++;
                }
                if(sum == 0){
                    out.push_back({nums[i], nums[left], nums[right]});
                    right --;
                    left ++;
                }
            } 
        }
        return out;

    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux 系统内核查询

1. 使用uname命令 uname命令可以用来显示系统信息&#xff0c;包括内核版本。 查看完整的内核版本信息&#xff1a;uname -a [rootlocalhost ~]# uname -a Linux localhost.localdomain 4.18.0-448.el8.x86_64 #1 SMP Wed Jan 18 15:02:46 UTC 2023 x86_64 x86_64 x86_64 GN…

静态时序分析:报告命令report_timing详解

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 目录 指定时序路径起点 指定时序路径经过点 指定时序路径终点 指定不报告的路径 指定路径类型 指定延迟类型 指定每个终点报告的最大时序路径数 指定每个时序组的…

DeepSeek 助力 Vue3 开发:打造丝滑的弹性布局(Flexbox)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

qt 播放pcm音频

一、获取PCM音频 ffmpeg -i input.mp3 -acodec pcm_s16le -ar 44100 -ac 2 -f s16le output.pcm -acodec pcm_s16le&#xff1a;指定16位小端PCM编码格式&#xff08;兼容性最佳&#xff09;-ar 44100&#xff1a;设置采样率为CD标准44.1kHz&#xff08;可替换为16000/8000等&a…

OpenCV计算摄影学(15)无缝克隆(Seamless Cloning)调整图像颜色的函数colorChange()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::colorChange 是 OpenCV 中用于调整图像颜色的函数。它允许你通过乘以不同的系数来独立地改变输入图像中红色、绿色和蓝色通道的强度&#xf…

WPS AI+office-ai的安装、使用

** 说明&#xff1a;WPS AI和OfficeAI是两个独立的AI助手&#xff0c;下面分别简单讲下如何使用 ** WPS AI WPS AI是WPS自带AI工具 打开新版WPS&#xff0c;新建文档后就可以看到菜单栏多了一个“WPS AI”菜单&#xff0c;点击该菜单&#xff0c;发现下方出现很多菜单&#xf…

绝美焦糖暖色调复古风景画面Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 通过 Lr 软件丰富的工具和功能&#xff0c;对风景照片在色彩、影调等方面进行调整。例如利用基本参数调整选项&#xff0c;精准控制照片亮度、对比度、色温、色调等基础要素&#xff1b;运用 HSL 面板可对不同色彩的色相、饱和度以及明亮度进行单独调节&#xff1b;利…

【Manus资料合集】激活码内测渠道+《Manus Al:Agent应用的ChatGPT时刻》(附资源)

DeepSeek 之后&#xff0c;又一个AI沸腾&#xff0c;冲击的不仅仅是通用大模型。 ——全球首款通用AI Agent的破圈启示录 2025年3月6日凌晨&#xff0c;全球AI圈被一款名为Manus的产品彻底点燃。由Monica团队&#xff08;隶属中国夜莺科技&#xff09;推出的“全球首款通用AI…

团队学习—系统思考

3月的团队学习将继续深入&#xff0c;与海外顾问共同探讨系统思考和心智模式的核心内容。回顾过去&#xff0c;这些每月一次的学习不仅成为我们审视思维框架的常规途径&#xff0c;更成为揭示潜在问题与盲点的高效工具。尤其是那些“我不知道我不知道”的领域&#xff0c;外部顾…

使用LVGL驱动三色墨水屏,Arduino

一、基本情况 本文代码基于以下软件版本&#xff1a; Arduino2.X LVGL8.3.11 GXEPD1.6.2 epdpaint---微雪EPD驱动的一部分&#xff0c;你可以在微雪的官网下载到 硬件&#xff1a; MCU&#xff1a;ESP32-S3-N16R8 屏幕&#xff1a;GDEY042Z98黑白红三色墨水屏&#xff0c;某…

DeepSeek未来发展趋势:开创智能时代的新风口

DeepSeek未来发展趋势&#xff1a;开创智能时代的新风口 随着人工智能&#xff08;AI&#xff09;、深度学习&#xff08;DL&#xff09;和大数据的飞速发展&#xff0c;众多创新型技术已经逐渐走向成熟&#xff0c;而DeepSeek作为这一领域的新兴力量&#xff0c;正逐步吸引越…

Go红队开发—编解码工具

文章目录 开启一个项目编解码工具开发Dongle包Base64编解码摩斯密码URL加解密AES加解密 MD5碰撞工具开发 开启一个项目 这作为补充内容&#xff0c;可忽略直接看下面的编解码&#xff1a; 一开始用就按照下面的步骤即可 1.创建一个文件夹&#xff0c;你自己定义名字(建议只用…

算法·搜索

搜索问题 搜索问题本质也是暴力枚举&#xff0c;一般想到暴力也要想到利用回溯枚举。 排序和组合问题 回溯法 去重问题&#xff1a;定义全局变量visited还是局部变量visited实现去重&#xff1f; 回溯问题 图论中的搜索问题 与一般的搜索问题一致&#xff0c;只不过要多…

常见的限流算法有哪些?

好的&#xff0c;关于这个问题&#xff0c;我会从几个方面来回答。 首先&#xff0c;限流算法是一种系统保护策略&#xff0c;主要是避免在流量高峰导致系统被压垮&#xff0c;造成系统不可用的问题。 常见的限流算法有 5 种。 1. &#xff08;如图&#xff09;计数器限流&a…

BetaFlight源码解读01

1.打开main.c init();run(); void systemInit(void) {int ret;clock_gettime(CLOCK_MONOTONIC, &start_time);printf("[system]Init...\n");SystemCoreClock 500 * 1e6; // virtual 500MHzif (pthread_mutex_init(&updateLock, NULL) ! 0) {printf("Cr…

FPGA-按键消抖

按键消抖 1. 原理 2. 关键程序实现 always (posedge clk or negedge rst) beginif(!rst)begincnt_wait < 26d0;end else if(flag_nege || flag_pose)begincnt_wait < 26d1;end else if(cnt_wait MAX_CNT) begincnt_wait < 26d0;end else if(cnt_wait > 26d0 &am…

Qt 进度条与多线程应用、基于 Qt 的文件复制工具开发

练习1&#xff1a;Qt 进度条与多线程应用 题目描述 开发一个基于 Qt 的应用程序&#xff0c;该应用程序包含一个水平进度条&#xff08;QSlider&#xff09;&#xff0c;并且需要通过多线程来更新进度条的值。请根据以下要求完成代码&#xff1a; 界面设计&#xff1a; 使用 QS…

C语言100天练习题【记录本】

C语言经典100题&#xff08;手把手 编程&#xff09; 可以在哔哩哔哩找到 已解决的天数&#xff1a;一&#xff0c;二&#xff0c;五&#xff0c;六 下面的都是模模糊糊的 可以学学这些算法&#xff0c;我是算法白痴&#xff0c;但是我不是白痴&#xff0c;可以学&#xff…

L1G5000XTuner 微调个人小助手认知

环境配置与数据准备 本节中&#xff0c;我们将演示如何安装 XTuner。 推荐使用 Python-3.10 的 conda 虚拟环境安装 XTuner。 步骤 0. 使用 conda 先构建一个 Python-3.10 的虚拟环境 cd ~ #git clone 本repo git clone https://github.com/InternLM/Tutorial.git -b camp4 mk…

【JavaEE】阻塞队列

【JavaEE】阻塞队列 一、什么是阻塞队列&#xff1f;二、阻塞队列的特点三、阻塞队列的常用方法3.1 抛出异常3.2 有返回结果&#xff0c;不会抛出异常3.3 阻塞 四、常见的阻塞队列4.1 ArrayBlockingQueue4.2 LinkedBlockingQueue4.3 SynchronousQueue4.4 PriorityBlockingQueue…