leetcode-三数之和

分析问题

这道题目非常的奇怪 出现的位置更是奇怪

这道题目在代码随想录的哈希章节看到的,但是这道题目基本不会用哈希方法去解决

就当是复习快慢指针了吧

题解

给你一个数组,从其中任意取出3个数(不可重复取出) 这三个数如果相加==0,则满足要求加入到二维数组中去。但是很重要的一点就是,这三个数取出后a,b,c 如果我后面又找到了一样的a,b,c 此时我们要去重 。此题一个是想到如何去做非常的困难,而且去重部分的逻辑也是很精妙,稍有不慎就是错误.

小结

题目的意思很明确,取出三个数字相加==0,则放入数组,但是这三个数的组合已经不能再次出现,否则重复了,需要去重。

重点

规律,如果只要数字,不要下标,而且不能重复。那么我们可以尝试先排序。

这样可以方便我们去重.

-暴力

基本代码

暴力算法三个循环嵌套。那么我们便可以取出所有的组合

vector<vector<int>> threeSum(vector<int>& nums) {
    vector<vector<int>> ans;
    int size = nums.size();
    for(int a = 0;a<size-2;a++){
        int n1=nums[a];
        //b循环
        for(int b = a+1; b<size-1;b++){
            int n2=nums[b];
            //c循环
            for(int c = b+1;c<size;c++){
                int n3=nums[c];
                if(n1+n2+n3==0){
                   ans.push_back(vector<int>{n1,n2,n3});
                   break;
                }
            }
            //c循环
        }
        //b循环
    }
    return ans;
}

这是第一部分,我们完成了基本的逻辑 此时输出 我们发现有重复的部分

接下来就是关键的去重操作

去重

我们想到去重的话,记录元素有没有出现过,那么最好的是unordered_set,这道题目理论上也是可以的,但是我不知道怎么去搞,有点混乱复杂,所以我们需要一点技巧。

在讲解代码前,有一个题目需要讲一下。两数之和,leetcode上面有一套类似的,不过他要求出下标,而我只需要数值便可以,其他要求类似,不重复。

例如:

我给出{-1,3,2,2,1,-1,-1,1,-2}我要求给出所有两个数相加==0的组合,且不能重复。

讲解

这道题目很显然双指针,一个slow放在开头,fast则是++ ,如果双指针指向的数字相加==0符合就退出。

但是如何辨别slow已经指向过-1呢

我们说到

但是这样会有多余的hash计算和空间的占用,即使非常的渺小。

排序

我发现过一个规律,如果只要数字,不要下标,而且不能重复。那么我们可以尝试先排序。

如果slow的指针下标不为0,而且nums[slow--]==nums[slow]。那么我们完全可以跳过这个数字,因为第一次我们已经把所有符合条件的全部遍历过了

vector<vector<int>> threeSum(vector<int>& nums) {
    sort(nums.begin(),nums.end());
    for(int i:nums)cout<<i<<" ";
    cout<<endl;
    vector<vector<int>> ans;
    int size = nums.size();
    for(int a = 0;a<size-2;a++){
        int n1=nums[a];
        if(a!=0&&n1==nums[a-1])continue;
        //b循环
        for(int b = a+1; b<size-1;b++){
            int n2=nums[b];
            if(b!=a+1&&n2==nums[b-1])continue;
            //c循环
//            for(int c = b+1;c<size;c++){
//                int n3=nums[c];
//                if(c!=0&&n3==nums[c-1])continue;
//                if(n1+n2+n3==0){
//                   ans.push_back(vector<int>{n1,n2,n3});
//                   break;
//                }
//            }
            //c循环
            if(n1+n2==0){
                   ans.push_back(vector<int>{n1,n2});
                   break;
                }
        }
        //b循环
    }
    return ans;
}

这样就可以非常的帅气去除重复的答案,如果你理解了 那么 c循环里面应该干什么你就知道怎么写了。

  vector<vector<int>> threeSum(vector<int>& nums) {
    sort(nums.begin(),nums.end());
    for(int i:nums)cout<<i<<" ";
    cout<<endl;
    vector<vector<int>> ans;
    int size = nums.size();
    for(int a = 0;a<size-2;a++){
        int n1=nums[a];
        if(a!=0&&n1==nums[a-1])continue;
        //b循环
        for(int b = a+1; b<size-1;b++){
            int n2=nums[b];
            if(b!=a+1&&n2==nums[b-1])continue;
            //c循环
            for(int c = b+1;c<size;c++){
                int n3=nums[c];
                if(c!=b+1&&n3==nums[c-1])continue;
                if(n1+n2+n3==0){
                   ans.push_back(vector<int>{n1,n2,n3});
                   break;
                }
            }
            //c循环
        }
        //b循环
    }
    return ans;
}

请注意 嵌套中的b,c判断不再是0,而是上一层的+1.

-双指针(三指针)

分析

看到上面这种解法,也许你会有所启发。

但是苦于这题三数,难道有新的方法叫做三指针。

其实没有,但是这题可以拆解为一个指针和双指针的问题

讲解

第一个循环a是要留下的,毕竟有1才有2.我们需要知道从哪里开始。

我们主要优化的是2,3循环。

怎么优化呢?如果我说给你数字a,现在请你遍历数组给出b,c。那么问题变成了什么?

双指针。我们在nums[a]~nums.back()中 需要得出==-a的结果

动手

我们保留a循环,剔除b,c循环;

在数组中快速的求出和为-a的数字组合。

如果你做过这道题目,那么你就会知道。

我们可以定义int left = a+1,right = nums.size()-1;

如果他们相加>a那么right--,反left++.这里有二分法的思想.

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

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值