[LeetCode] Three Sum题解

Three Sum:

Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: The solution set must not contain duplicate triplets.

这是一道LeetCode中标记为Medium的题。由于时间限制,对算法的复杂度有要求,最后的解法确实有点巧妙。
给一个有n个数字的数组S,找出所有满足a + b + c = 0的组合,结果排除重复元组。

最初的错误尝试

一开始的想法是,先将数组排序,通过三次遍历找出所有a、b、c的组合,再排除相同的元组。这个算法的复杂度在元组很少时是O(n^3),元组很多的时更大,在输入很长的时候就超时了。

接下来还是想在这个的基础上改良——将n缩小。
在某些时候——例如-3,-2,0,2,6,8,由于6和8比最小的两个数加起来还大,所以可以舍去不考虑。同理,在例如-5,0,1,2的数列中-5也可以不考虑。这样就减少了计算量。但是最后依然超时。

在复杂度至少是O(n^3)的算法上的改良尝试失败。我在过去做题遇到超时时,曾经做过很多次这种尝试,但是每次都失败了。改进算法还是要做到在O层面降低复杂度才行。
要找到一种复杂度Ω(n^3)的算法才有机会通过。

解决方案

在参考了LeetCode的Discussion之后,有了最后的解决方案。
这种解决方案参考了题目Two Sum的解法,复杂度只有O(n^2)

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {

        vector<vector<int>> re;
        sort(nums.begin(), nums.end());
        //当nums中元素个数不足3个时,返回一个空的re就可以了
        if(nums.size() < 3){
            return re;
        }
        for(int i = 0 ; i < nums.size() - 1 ; i++){       
            int target = nums[i];
            int font = i + 1;
            int back = nums.size()-1;            
            while(back > font){

                //由于nums已经被排序,所以
                //如果三个数之和小于0,那么让font++,下一次检测三个数之和就会增大(也可能不变)
                //如果三个数之和大于0,那么让back--,下一次检测三个数之和就会减小(也可能不变)
                if(target + nums[font] + nums[back] < 0){
                    font ++;
                }
                else if(target + nums[font] + nums[back] > 0){
                    back --;
                }

                //等于0的时候添加到re中
                else if(target + nums[font] + nums[back]  == 0 && font <back){
                    vector<int> temp;
                    temp.push_back(-target);
                    temp.push_back(nums[font]);
                    temp.push_back(nums[back]);
                    re.push_back(temp);

                    //此时要让font和back分别自增、自减到下一个不同的数字
                    while(nums[font] == temp[1] && font <back){
                        font ++;
                    }      
                    while(nums[back] == temp[2] && font <back){
                        back --;
                    }
                }                
            }

            //让target自增到下一个不同的数字
            while(i<nums.size()-1 && nums[i] == nums[i+1]){
                i++;
            }
        }
        return re;
    }
};

在这个算法里,最重要的是只用了两个循环就检测出所有abc元组。
其次,通过两次让font和back、target自增(自减)到下一个不同的数字,就排除了重复元组。

posted @ 2017-09-07 13:50  liangf27  阅读(631)  评论(0编辑  收藏  举报