LeetCode算法练习之三数之和

32 篇文章 0 订阅

和之前的两数之和问题一样,这个问题也可以直接三重循环直接莽穿,但是这算法时间复杂度太大了,O(n^3)的时间复杂度一旦遇到1000位的数组就要循环10亿次,几万位的话时间够泡一杯java了。

这个问题有两种解法,时间复杂度都是O(n^2),包括散列表查找法和双指针法。 

散列表法当然就是之前搞两数之和算法的最优解法,然而幸亏我提前看了讨论区说明这种方法会超时,就没有试验,不然就会写老半天程序最后答案还超时心态爆炸。其实,散列表的查询虽然是线性时间,但是散列表对空间占用非常大,而且碰撞处理也会拖慢它的查找时间使得查找的效率变慢。

然后我就使用了双指针法,先对整个数组进行排序,然后具体原理看两数之和就可以了。之前还有一篇专门写双指针的文章。就是一个指针移动使结果变大,一个使得结果变小。

但是!再次读题,会发现题目要求答案不能包含重复的组。就算结果在程序中算出了两次,最后返回的数组中也只能有一个。刚刚发现这个问题的时候,我认为这个问题很好解决,只要在最后再添加一个小程序来判断解是否在数组中就可以了。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        if(nums.size()<3) return {};
        sort(nums.begin(), nums.end());
        int lptr=0;
        int rptr=0;
        int target=0;
        vector<vector<int>> res;
        for(int i=0;i<nums.size()-2;i++){
            target=-nums[i];
            lptr=i+1;
            rptr=nums.size()-1;
            while(rptr>lptr){
                if(nums[lptr]+nums[rptr]>target){
                    rptr--;
                }else if(nums[lptr]+nums[rptr]<target){
                    lptr++;
                }else{
                    bool isbreak=false;
                    for(int a=0;a<res.size();a++){
                        if(res[a][0]==nums[i]&&res[a][1]==nums[lptr]&&res[a][2]==nums[rptr]) isbreak=true;
                    }
                    if(!isbreak) 
                    res.push_back({nums[i],nums[lptr],nums[rptr]});
                    lptr++;
                    rptr--;
                }
            }
        }
        return res;
    }
};

题目确实可以这样解出来,但是这样做,最终的时间评分只有5%具体原因,我估计就是最后一小段程序拖慢了时间。

假设有一个输入是[0,0,0,0,0,0,0,0,0.....,0](上千个0),这时判断是否有重复元素的程序循环的次数可想而知非常多,可以把时间复杂度拖到O(n^3)。

所以就要对程序进行再优化。我们想可不可以在循环的时候就判断出结果是否重复,去掉最后判断结果是否重复的程序?考虑一个现象,数组既然已经排序好了,那么同样的元素必定相邻。那么如果这次循环的元素和上次一样,这次循环就包括了上次循环的所有可能情况,这次循环就不能执行,必须马上跳出以保证结果中同样的答案只出现一次。改进代码如下:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        if(nums.size()<3) return {};
        sort(nums.begin(), nums.end());
        int lptr=0;
        int rptr=0;
        int target=0;
        vector<vector<int>> res;
        for(int i=0;i<nums.size()-2;i++){
            target=-nums[i];
            if(nums[i]>0) break;
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            lptr=i+1;
            rptr=nums.size()-1;
            while(rptr>lptr){
                if(nums[lptr]+nums[rptr]>target){
                    rptr--;
                    while(lptr<rptr&&nums[rptr]==nums[rptr+1]) rptr--;
                }else if(nums[lptr]+nums[rptr]<target){
                    lptr++;
                    while(lptr<rptr&&nums[lptr]==nums[lptr-1]) lptr++;
                }else{/*
                    bool isbreak=false;
                    for(int a=0;a<res.size();a++){
                        if(res[a][0]==nums[i]&&res[a][1]==nums[lptr]&&res[a][2]==nums[rptr]) isbreak=true;
                    }
                    if(!isbreak) 
                    */
                    res.push_back({nums[i],nums[lptr],nums[rptr]});
                    lptr++;
                    while(lptr<rptr&&nums[lptr]==nums[lptr-1]) lptr++;
                    rptr--;
                    while(lptr<rptr&&nums[rptr]==nums[rptr+1]) rptr--;
                }
            }
        }
        return res;
    }
};

 

结果达到了81%,说明算法还有优化空间,但是做题的时候心态爆炸,没有想出来,试一下解答区大佬的优化技巧(++i的使用一看就知道是C++编程老手)

 

貌似编译器会对这样的程序进行优化以达到更加的效率,这点我就不懂了。。。不过既然时间复杂度是一样的,差距也没多少,这些内容的研究还是次要的。掌握主要的算法知识才是重点。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值