Leetcode第六题:用 C++ 解决三数之和

在算法学习的道路上,三数之和算得上是一个必刷的经典题目。它不仅考察了对数组操作的基本功,更是对双指针思想和去重逻辑的一次全面检验。今天,我使用 C++ 来完成这个题目,并记录下整个思考过程。


1. 问题描述

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 abc ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。答案中不可以包含重复的三元组。

示例 :

输入: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. 解题思路

第一眼看到这道题,最容易想到的办法就是三重for循环暴力解决,如下:

cpp 复制代码
for (int i = 0; i < n; i++) 
{
    for (int j = i + 1; j < n; j++) 
    {
        for (int k = j + 1; k < n; k++) 
        {
            if (nums[i] + nums[j] + nums[k] == 0) 
            {
                //找到一组解
            }
        }
    }
}

但是实际上这并不是一个好办法,时间复杂度 O(n³)不说,还需要额外处理去重问题。

事实上,由于题目要求结果不能包含重复的三元组,我们能够比较容易地想到对原数组进行排序,因为排序之后能够更容易地处理重复的情况。

此外,由于我们需要寻找三个数,对于我们已经排序好的数组来说,一种常用的方法就是固定一个数,用双指针去寻找其余两个数,这道题,我们固定最前面的元素,让双指针去遍历剩下的元素。

我们还要注意一点,就是题目要求的不能含有重复的三元组,对于按元素大小排好序的数组来说,这并不是一件难事。

思路大体就是这样,下一章我们编写代码,还会附上代码详解。


3. 完整代码

我先贴出完整代码,再详细分析:

cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;//存放最终结果
        int n = nums.size();//先拿到原数组大小
        if(n < 3) return result;//如果原数组中元素小于3我们可以直接返回

        sort(nums.begin(),nums.end());//对原数组的元素重新按照从小到大的顺序进行排序得到排序好的数组
        for(int i=0; i<n-2; i++)//为什么i<n-2后面会讲
        {
            if(nums[i] > 0) return result;//因为数组元素是从小到大排列的,如果最前面的数都大于零,那就可以直接结束了
            if(i > 0 && nums[i] == nums[i-1]) continue;//固定元素的去重
            int l = i + 1;//左指针指向固定元素的下一个
            int r = n - 1;//右指针指向数组最后一个元素
            while(l < r)//要确保左指针不能超过右指针
            {
                int sum = nums[i] + nums[l] + nums[r];//三个数求和,判断sum的值
                if(sum > 0)//如果大于零,我们把右指针左移,sum就会变小
                {
                    r--;
                }
                else if(sum < 0)//如果小于零,就把左指针右移,sum变大
                {
                    l++;
                }
                else//sum刚好等于零
                {
                    result.push_back({nums[i],nums[l],nums[r]});//先把这个三元组存入result
                    //双指针的去重,这里篇幅可能比较长,我们下面讲解
                    while(l < r && nums[l] == nums[l+1]) l++;
                    while(l < r && nums[r] == nums[r-1]) r--;
                    l++;
                    r--;
                }
            }
        }
        return result;
    }
};

下面讲一下代码中的一些细节问题:

  • 首先要讲的就是为什么要把 for 循环的边界设为n-2。请大家回顾一下前面的解题思路,这个i作为固定元素的下标,lr始终都在i的后面。试想一下,假如i能指向倒数第二位或者最后一个元素,那么lr是不是就越界了?这直接就导致了严重的后果。
  • 其次要讲的是双指针去重 。要知道,else不仅在while循环中,它还在for循环中,也就是说对于一个固定的i,双指针lr要多次移动,直到找出全部与这个i指向元素组合后能满足题目要求的三元组。我们已经知道i是固定的,如果l或者r任一个指向的元素和上一位元素相同,那么另一个也会相同,从而出现重复的三元组。
  • 最后要讲的是固定元素去重 。我们已经提到过了,双指针会找出对于一个固定i所有满足条件的三元组,因此,再遇到相同的元素时,就可以直接跳过,不将它作为固定元素了。

4. 写在最后

三数之和这道题,我刷了不下五遍。第一遍一头雾水,第二遍懵懵懂懂,第三遍似懂非懂,第四遍豁然开朗,第五遍------我开始享受这个过程。我的大脑中好像有了ilr他们三个真实的写照,它们就在那不停的动来动去,我看到了每个指针他们是怎样跳过重复元素的,又是怎样在sum不满足条件时恰到好处的移动,从而最终找到合适元素的。

回想做这道题的整个过程,其实算法学习就是这样,没有捷径可走。每一道题都是一次修行,每一个 bug 都是一次磨练,享受这种从不懂到懂的过程,我想如果有捷径的话,这就是捷径。

今天我把这道题写进博客,不是为了展示我有多厉害,毕竟这题大把人都能写出来,而是记录下自己思考的过程,给自己一个交代。

最后放上一句真理名言:路漫漫其修远兮,吾将上下而求索。


相关推荐
进击的小头2 小时前
第4篇:二阶系统的时域响应分析
python·算法
wengqidaifeng2 小时前
备战蓝桥杯----C/C++组 (一)所需C++基础知识(上)
c语言·数据结构·c++·蓝桥杯
tankeven2 小时前
HJ126 小红的正整数计数
c++·算法
0 0 02 小时前
CCF-CSP 37-2 机器人饲养指南(apple)【C++】考点:完全背包问题
开发语言·c++·算法
xiaoye-duck2 小时前
《算法题讲解指南:优选算法-分治-归并》--49.计算右侧小于当前元素的个数,50.翻转对
c++·算法
_Twink1e2 小时前
[算法竞赛]八、排序、排列
数据结构·c++·笔记·算法·排序算法
im_AMBER2 小时前
Leetcode 137 组合 | 电话号码的字母组合
开发语言·算法·leetcode·深度优先·剪枝
Alex艾力的IT数字空间2 小时前
OCR 原理:从像素到文本的智能转换
数据结构·人工智能·python·神经网络·算法·cnn·ocr
仟濹2 小时前
【算法打卡day19(2026-03-11 周三)算法:打家劫舍-DP,双指针,二分查找,滑动窗口,方向控制,前缀和 】8个题
算法·leetcode·二分查找·动态规划