力扣--15. 三数之和(中等题)

本文详细解析了力扣15.三数之和问题,通过多种尝试与思考,最终实现了有效的解决方案,并对代码进行了优化。特别讨论了如何避免重复解和超时问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


今天晚上没啥事,就挑战了一题中等题,结果果然没写出来,思路应该是没有问题,因为是卡在了一个大输入,超时了,查阅了官方的答案,感觉是还有些重复的地方没有考虑周全。

原题传送门

【题目描述】

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

【示例】

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

【解题过程】

首先,我认为应该是要枚举,但是对于三个数的和,用三层循环的话,显然会超时(虽然自己写的两层都超时了),因此,使用两层循环。
首先,解决特殊情况:
当数组中的数据不足三个,答案肯定是空。
而后,进行两层循环的遍历。在遍历时,考虑到会有重复,因此,添加了一些判定条件,如:三个数的和为0,必然是两种情况:

  1. 0+0+0
  2. 三个数中至少有一个负数

因此,不妨将第一层循环中的数字设定为非正数,则可以减少近一半的重复情况。为了方便遍历,先对数组进行了排序。

此外,第二层应该在第一层的右边,若在左边,也会造成重复。
基于上述思路,我写了如下代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        if(nums.size()==0 || nums.size()==1 || nums.size()==2)
        {
            return ans;
        }
        sort(nums.begin(),nums.end());//排序
        // int a=0,b=0,c=0;
        for(vector<int>::iterator i=nums.begin();(i<nums.end()-1) &&(*i<=0);i++){ 
            for(vector<int>::iterator j=nums.end()-1;(j>i)&&(*j>=0);j--){
                //两个数字不应该是同一个,且只需要比较i<j时的情况,当i>j,重复
                    vector<int> temp;
                    int f=0-(*i+*j);
                    vector<int>::iterator ind=find(nums.begin(),nums.end(),f);
                    if((ind==nums.end())||(ind==i)||(ind==j)){//未找到或与i/j重复
                        continue;
                    }
                    else{
                    temp.push_back(*i);
                        temp.push_back(*j);
                        temp.push_back(f);
                        sort(temp.begin(),temp.end());//排序
                    vector<vector<int>>::iterator t=find(ans.begin(),ans.end(),temp);
                    if(t==ans.end()){
                        ans.push_back(temp);//不会重复,添加
                    }
                }
            }
        }
        sort(ans.begin(),ans.end());
        return ans;
    }
};

然而结果是超时:
在这里插入图片描述

【参考代码】

想了很久都没有想出来要怎么优化,因此,看了官方的参考代码:
官方解决方案

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    ans.push_back({nums[first], nums[second], nums[third]});
                }
            }
        }
        return ans;
    }
};

经过比对,发现主要是忽略了这种重复的情况:
在这里插入图片描述
因此,我决定再对我的代码进行修改。

【艰难修改】

然后,我将代码改成了如下形式:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        if(nums.size()==0 || nums.size()==1 || nums.size()==2)
        {
            return ans;
        }
        if(nums.size()==3){
            if(nums[0]+nums[1]+nums[2]==0){
                ans.push_back(nums);
            }
            return ans;
        }
        sort(nums.begin(),nums.end());//排序
        for(int i=0;i<nums.size();++i){          
            if((i>0 && nums[i]==nums[i-1])||(nums[i]>0))
            {
                continue;
            }
            for(int j=i+1;j<nums.size();++j){
                //两个数字不应该是同一个,且只需要比较i<j时的情况,当i>j,重复
                    vector<int> temp;
                    int f=0-(nums[i]+nums[j]);
                    vector<int>::iterator it=find(nums.begin(),nums.end(),f);
                    int ind=&*it - &nums[0];//将迭代器转换为下标
                    if((it==nums.end())||(ind==i)||(ind==j)){//未找到或与i/j重复
                        continue;
                    }
                    else{
                    temp.push_back(nums[i]);
                    temp.push_back(nums[j]);
                    temp.push_back(*it);
                    sort(temp.begin(),temp.end());//排序
                    vector<vector<int>>::iterator t=find(ans.begin(),ans.end(),temp);
                    if(t==ans.end()){
                        ans.push_back(temp);//不会重复,添加
                    }
                }
            }
        }
        sort(ans.begin(),ans.end());
        return ans;
    }
};

结果还是错误,显示为:
在这里插入图片描述
想了很久很久,终于,我意识到,应该是用的find函数有问题,因为输入四个都是0,而我要使用find找的也是0,很有可能,它返回的位置,是第一个0,这样转换为下标后,我得到的ind==i,因此,它执行了continue语句,我无法得到正确答案。
为了验证这个想法,我在控制台输出,果然。。
在这里插入图片描述

因此,只能改变思路。。。抛弃find方案。
将代码改成了三层for循环,

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        if(nums.size()==0 || nums.size()==1 || nums.size()==2)
        {
            return ans;
        }
        if(nums.size()==3){
            if(nums[0]+nums[1]+nums[2]==0){
                ans.push_back(nums);
            }
            return ans;
        }
        sort(nums.begin(),nums.end());//排序
        for(int i=0;i<nums.size();++i){          
            if((i>0 && nums[i]==nums[i-1])||(nums[i]>0))
            {
                continue;
            }
            for(int j=i+1;j<nums.size();++j){
                //两个数字不应该是同一个,且只需要比较i<j时的情况,当i>j,重复
                if(j>i+1 && nums[j]==nums[j-1])
                {
                    continue;
                }
                for(int k=nums.size()-1;k>j;k--)
                {
                    vector<int> temp;
                    if(nums[i]+nums[j]+nums[k]<0){
                        break;
                    }
                    if(nums[i]+nums[j]+nums[k]==0){
                        temp.push_back(nums[i]);
                        temp.push_back(nums[j]);
                        temp.push_back(nums[k]);
                        sort(temp.begin(),temp.end());//排序
                        vector<vector<int>>::iterator t=find(ans.begin(),ans.end(),temp);
                        if(t==ans.end()){
                            ans.push_back(temp);//不会重复,添加
                    }
                    }
                    
                
                }
                    
            }
        }
        sort(ans.begin(),ans.end());
        return ans;
    }
};

结果,果然在第315个测试点超时了。。。。。。。。。感觉今天改不出来了,图书馆也要闭馆了,明天继续肝吧。【果然还是太菜,一个中等题做了三个小时,看了答案还改不出来】

------------------3.3更新------------------------------------------------------------
改了半天,还是过不去,最后改成了参考答案的同款,结果依然超时,细看了很久,发现我用的是k–,而答案用的是–k,抱着试一试的心态,结果就过了?????????????
在这里插入图片描述
对应的代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        if(nums.size()==0 || nums.size()==1 || nums.size()==2)
        {
            return ans;
        }
        if(nums.size()==3){
            if(nums[0]+nums[1]+nums[2]==0){
                ans.push_back(nums);
            }
            return ans;
        }
        sort(nums.begin(),nums.end());//排序
        for(int i=0;i<nums.size();++i){          
            if((i>0 && nums[i]==nums[i-1])||(nums[i]>0))
            {
                continue;
            }
            int k=nums.size()-1;
            for(int j=i+1;j<nums.size();++j){
                //两个数字不应该是同一个,且只需要比较i<j时的情况,当i>j,重复
                if(j>i+1 && nums[j]==nums[j-1])
                {
                    continue;
                }
                while ((k>j) && (nums[i]+nums[j]+nums[k]>0))
                {
                    --k; //这里变成k--就会超时。。。。。。。。。。。
                }
                if(j==k){
                    break;
                }
                    if(nums[i]+nums[j]+nums[k]==0)
                        ans.push_back({nums[i],nums[j],nums[k]});
                    } 
                }
        sort(ans.begin(),ans.end());
        return ans;
    }
};

【总结】

这题写下来,明显感觉到自己代码能力有多差劲,做为大三的计院学子,属实不应该,这样下去面试要咋整诶!!!!!!!总觉得自己的思路和参考答案是一样的,但写出来和跑起来的结果相差就是很大。还是要多练习,找找解题规律吧,希望能坚持下去。
此外,第一次意识到了–k和k–的不同之处,以前老师讲过,也知道一个是先减后用,另一个是先用后减,但一直觉得没啥区别,这次是真实感受到了这种区别了。。。。看来只是还是要用才能理解,之前都只是停留在课本上了。可惜啊可惜。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗小芋圆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值