【中等】15-三数之和 3Sum

题目

Given an array nums of n integers, are there elements a, b, c in nums 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.

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

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

Example:

Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum

解法

方法一:双指针

解题思路

对有序数组求两数之和为某数的算法我们知道,即双指针,从两端取数,和不够就小的一侧指针向中间移,超出则大的一侧向中间移。对于三数也是一样,先对数组进行排序,若三数中包含了第n位数字t,那么只需要对剩下的数字计算两数之和为-t的情况即可,即n从0开始递增,对剩下的n+1位开始的数组求和位-t。

为了剪枝,我们可以判断如果这个第n个数已经大于0,后面的数一定也大于0,和不可能小于0,就直接返回。

代码

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        int size = nums.size();
        if(size < 3){
            return res;
        }
        sort(nums.begin(), nums.end());
        for(int first = 0; first < size-2; ++first){
            if(nums[first] > 0){
                return res;
            }
            if(first == 0 || nums[first] != nums[first-1]){
                int left = first+1, right = size-1;
                while(left < right){
                    if(nums[left] + nums[right] + nums[first] > 0)
                        do --right; while(right>left && nums[right] == nums[right+1]);
                    else if(nums[left] + nums[right] + nums[first] < 0)
                        do ++left; while(left < right && nums[left] == nums[left-1]);
                    else{
                        vector<int> newinsert = {nums[first], nums[left], nums[right]};
                        res.push_back(newinsert);
                        do --right; while(right>0 && nums[right] == nums[right+1]);
                        do ++left; while(left < size && nums[left] == nums[left-1]);
                    }
                }
            }
        }
        return res;
    }
};

方法二:map

解法

用一个map记录每一个数字出现的次数,仍旧是双指针从两端开始,我们已知两个指针的和,只需要判断是否这个和的相反数是否在数组中仍然出现即可。

代码

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        if(nums.size() < 3) return result;
        map<int,int> num_map;
        map<int,int>::iterator start, end, target;
        for (int i=0;i<nums.size();i++) {
            if (num_map.find(nums[i]) == num_map.end()) num_map[nums[i]] = 1;
            else num_map[nums[i]] += 1;
        }    
        for(start = num_map.begin(); start != num_map.end() && start->first <= 0; ++start) {
            start->second--; 
            end = num_map.end();
            end--;
            int t = 0- start->first-end->first;
            while (start->first<=end->first&&t<=end->first) {
                // cout << start->first << " " << t <<  " " << end->first << endl;
                end->second--;
                target = num_map.find(t);
                if (target!=num_map.end() && target->second>0) {
                    result.push_back(vector<int>{ start->first, t, end->first});
                    // cout << "found!" << endl;
                }
                end->second++;
                if(start==end) break;
                end--;
                t = 0-start->first-end->first;
            }
            start->second = 0;
        }
        return result;  
    }
};

总结

leetcode有个快速解法是对数字进行计数排序,相当于快速查找某个值是否在数组中

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        if (nums.size() < 3) {
            return res;
        }

        int maxNum = 0;
        int minNum = 0;
        for (const int d : nums) {
            maxNum = maxNum < d ? d : maxNum;
            minNum = minNum > d ? d : minNum;
        }

        const int tb_size = maxNum - minNum + 1;
        vector<char> rec(tb_size, 0);
        for (const int d : nums) {
            if (++rec[d - minNum] > 3) {
                rec[d - minNum] = 3;
            }
        }
        if (rec[-minNum] == 2) {
            rec[-minNum] = 1;
        }

        vector<int> v;
        for (int i = 0; i < tb_size; ++i)
            if (rec[i] > 0) {
                v.push_back(i + minNum);
            }
        const int vsz = v.size();
        for (int i = 0; i < vsz; ++i) {
            const int vmin = v[i];
            if (vmin > 0) {
                break;
            }
            for (int j = i; j < vsz; ++j) {
                const int vmid = v[j];
                const int target = -vmin - vmid;
                if (target < vmid) {
                    break;
                }
                if (target > maxNum || rec[target - minNum] == 0) {
                    continue;
                }
                if (rec[vmid - minNum] > 1 || vmin < vmid && vmid < target) {
                    res.push_back({vmin, vmid, target});
                }
            }
        }
        return res;
    }
};

static int _IO_ = []() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    //cout.tie(nullptr);
    return 0;
}();
posted @ 2020-04-23 10:53  陌良  阅读(132)  评论(0编辑  收藏  举报