LeetCode 15. 三数之和

题目描述

15. 三数之和

这里写图片描述


思路

思路1

比较容易想到的就是,求三数之和等于0,可以等价于求两个数的和,然后看这个和的相反数是否在nums里面。
但是 T_T这样的话复杂度太高了,会超时,捂脸,最后三个case,怎么改都超时……

bool find(vector<int> nums, int nums_sz, int target, int m, int n){
    for (int i = 0; i < nums_sz; i++){
        if (i == m || i == n)
            continue;

        if (nums[i] == target)
            return true;
    }

    return false;
}


vector<vector<int>> threeSum(vector<int>& nums) {
    int nums_sz = nums.size();

    vector<vector<int>> res;
    set<vector<int>> s;

    sort(nums.begin(), nums.end());

    for (int i = 0; i < nums_sz - 1; i++){
        int start = nums[i];

        for (int j = i + 1; j < nums_sz; j++){
            int end = nums[j];
            int opposite_number = -(start + end);
            if (find(nums, nums_sz, opposite_number, i, j)){
                vector<int> tmp(NUM);

                tmp[0] = start;
                tmp[1] = end;
                tmp[2] = opposite_number;

                sort(tmp.begin(), tmp.end());

                s.insert(tmp);

            }
        }
    }

    int s_sz = s.size();
    for (set<vector<int>>::iterator it = s.begin(); it != s.end(); it++){
        res.push_back(*it);
    }

    return res;
}

思路2

最后没办法去网上找了下人家的sol,还有这篇(没有优化),发现是我之前的思路不够好,而且没有优化到很好的地步。

总体思路是先排序(从小到大),然后以每一个数为起始,在它的右边不断缩小范围看是否有符合条件的数。

  1. 首先需要将原数组排序,这里可以用c++的sort接口就行:sort(nums.begin(), nums.end());
    这么排序我觉得个好处是当找到满足条件的三元组时,不需要判断结果的二维vector中是否已经包含了当前要放进去的子数组;

  2. 然后对于当前的数nums[i],在他的右边用下标leftright进行缩小范围的遍历;

  3. 如果sum = nums[i] + nums[left] + nums[right]; > 0 ,那么说明nums[right]大了,因此right--;如果sum<0,那么说明nums[left]小了,因此left++
  4. 1-3为基本的想法,然后在此之上还要有优化,跳过一些不必要的循环,还有提前退出(见代码)

这里写图片描述


代码

vector<vector<int>> threeSum(vector<int>& nums) {
    int nums_sz = nums.size();
    vector<vector<int>> res;

    sort(nums.begin(), nums.end()); // 排序

    int left, right, sum;
    vector<int> tmp(NUM);

    for (int i = 0; i < nums_sz - 2; i++){ // 这里只需要到nums_sz-3的位置即可,后面还有 nums_sz-2, nums_sz-1
        left = i + 1;
        right = nums_sz - 1;

        // 优化1: 当nums[i] == nums[i - 1]时,nums[i-1]与nums[i+1]--nums[nums_sz-1]的组合情况  和  nums[i]的情况一致,
        //         因此可以跳过nums[i]的循环,i>0保证i-1不会访问越界,同时也保证了输入为[0,0,0]时的正确性
        if (i > 0 && nums[i] == nums[i - 1]) { 
            continue;  
        }

        // 优化2: 当nums[i] > 0 ,那么nums[i]加上后面的比它大的两个数肯定也大于0,甚至是后面的数([i+1], [i+2]……)作为起始数时肯定也大于0(数组已经排过序),
        //         因此可以跳过;
        //        当 nums[i] + nums[left] + nums[left+1]>0,说明nums[i]与后面的范围中任取两个数并且这两个数是最小值,他们的和都大于0,后面的数([i+1], [i+2]……)作为起始数时sum也大于0,
        //         因此也可以跳过;
        if (nums[i] > 0 || (nums[i] + nums[left] + nums[left+1])>0){
            break;
        }

        // 优化3: 当nums[i] + nums[right] + nums[right - 1] < 0时,说明nums[i]与后面的范围中任取两个数并且这两个数是最大值,得到的sum < 0,说明nums[i]这个起始数选小了
        //         因此跳过进入下一层循环;
        if (nums[i] + nums[right] + nums[right - 1] < 0) {
            continue;
        }

        while (left < right){
            sum = nums[i] + nums[left] + nums[right];
            if (sum == 0){
                tmp[0] = nums[i];
                tmp[1] = nums[left];
                tmp[2] = nums[right];

                res.push_back(tmp);

                left++;
                right--;

                // 优化4: 如果nums[left] == nums[left - 1](left变化之前),那么此时nums[left]--nums[right]这个范围得到的结果还是和nums[left-1]--nums[right]得到的一样
                //        下面同理;
                while (left < right && nums[left] == nums[left - 1]){
                    left++;
                }

                while (left < right && nums[right] == nums[right + 1]){
                    right--;
                }

                tmp.clear();
                tmp.resize(NUM);
            }
            else if (sum < 0){
                left++;
                while (left < right && nums[left] == nums[left - 1]){
                    left++;
                }
            }
            else if (sum > 0){
                right--;
                while (left < right && nums[right] == nums[right + 1]){
                    right--;
                }
            }
        }
    }

    return res;
}

以后做题一定要多想想有没有更好的办法,还得多注意下能不能优化的。切记切记!

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:C马雯娟 返回首页