LeetCode 15 三数之和(双指针)

15. 三数之和

给定一个包含 n 个整数的数组 S,是否存在属于 S 的三个元素 a,b,c 使得 a + b + c = 0 ?找出所有不重复的三个元素组合使三个数的和为零。

注意:结果不能包括重复的三个数的组合。

例如, 给定数组 S = [-1, 0, 1, 2, -1, -4],

一个结果集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

ps:这题可以转换target的值,从而将题目变为两数之和的做法(两数之和题目戳我阅读)。下面是我一开始的做法: 
步骤1:先判断nums数组的大小是否小于3。
步骤2:对nums进行稳定排序(我这种做法和他们的下标有联系)
步骤3:将排序后的数组保存到num结构体数组中。
步骤4:找出nums数组里所有数的相反数,用set去重,同样保存他们的下标(目的是最后答案数组的去重)。
步骤5:有了相反数后,遍历所有的相反数,令target = opposize[i],这样在结构体数组中用双指针找出两数之和等于target,注意两个数的下标不能等于target的下标。
步骤6:将符合的数组保存,这里我用map去掉重复的数组。

static const auto __ = []()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	return nullptr;
}();
struct OPPOSIZE
{//相反数结构体数组
    int value;
    int index;
    OPPOSIZE(int value, int index): value(value), index(index) {}
    friend bool operator < (const OPPOSIZE& p1, const OPPOSIZE& p2)
    {
        return p1.value < p2.value;
    }
};
struct NUM
{
    int value;
    int index;
};
bool cmp(const NUM& n1, const NUM& n2)
{
    return n1.value < n2.value;
}
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //1
        vector<vector<int> >ans;
        if(nums.size() < 3)return ans;
        //2
        int nums_size = nums.size();
        stable_sort(nums.begin(), nums.end());
        //3
        vector<NUM> num;
        NUM temp1;
        for (int i = 0; i < nums_size; i++)
        {
            temp1.value = nums[i];
            temp1.index = i;
            num.push_back(temp1);
        }
        //4保存相反数
        set<OPPOSIZE> opposize;
        set<int> _opp;
        int opp_size = _opp.size();
        for (int i = 0; i < nums_size; ++i)
        {
            _opp.insert(-nums[i]);
            if (_opp.size() > opp_size)
            {
                OPPOSIZE temp(-nums[i], i);
                opposize.insert(temp);
                opp_size = _opp.size();
            }
        }
        map<vector<int>, int> m_ans;
        //5
        for (set<OPPOSIZE>::iterator it = opposize.begin(); it != opposize.end(); ++it)
        {
            int target = (*it).value;
            int t_index = (*it).index;
            int top = 0;
            int tail = nums_size - 1;

            while (top < tail)
            {
                if (top == t_index)
                {
                    ++top;
                    continue;
                }
                else if (tail == t_index)
                {
                    --tail;
                    continue;
                }
                if (num[top].value + num[tail].value == target)
                {
                    vector<int>t_ans;
                    if (t_index < num[top].index && t_index < num[tail].index)
                    {
                        t_ans.push_back(-(*it).value);
                        if (num[top].index < num[tail].index)
                        {
                            t_ans.push_back((num[top].value));
                            t_ans.push_back((num[tail].value));
                        }
                        else
                        {
                            t_ans.push_back((num[tail].value));
                            t_ans.push_back((num[top].value));
                        }
                    }
                    else if (num[top].index < t_index && num[top].index < num[tail].index)
                    {
                        t_ans.push_back(num[top].value);
                        if (t_index < num[tail].index)
                        {
                            t_ans.push_back(-(*it).value);
                            t_ans.push_back(num[tail].value);
                        }
                        else
                        {
                            t_ans.push_back(num[tail].value);
                            t_ans.push_back(-(*it).value);
                        }
                    }
                    else
                    {
                        t_ans.push_back(num[tail].value);
                        if (t_index < num[top].index)
                        {
                            t_ans.push_back(-(*it).value);
                            t_ans.push_back(num[top].value);
                        }
                        else
                        {
                            t_ans.push_back(num[top].value);
                            t_ans.push_back(-(*it).value);
                        }
                    }
                    //6
                    m_ans.insert(map<vector<int>, int>::value_type(t_ans, 1));
                    ++top;
                    --tail;
                }
                else if (num[top].value + num[tail].value > target)
                {
                    --tail;
                }
                else
                {
                    ++top;
                }
            }
        }
        for (map<vector<int>, int >::iterator it = m_ans.begin(); it != m_ans.end(); it++)
        {
            ans.push_back(it -> first);
        }
        return ans;
    }
};

    后来上交过了,我去看了一下别人的代码(好简洁啊!),原来可以进一步优化,做法大概是: 
    1.先对原数组进行排序
    2.遍历该数组,先确定相反数(他们的去重很巧妙)。如果该数大于0,则循环结束(这与去重做法有关),如果这个数与前面的数相同,则跳过。
    3.下来是精髓部分,我们找到一个相反数后去哪找另外的两个数呢。查找范围就是
(相反数的下标+1, nums.size() - 1)。
为什么是这样呢? 3数之和的目标是target(这道题是0)。首先如果选出的数是第一数,那就不用解释。如果选出的数不是第一个数呢?假设这个数是nums[i](0 < i < nums.size() - 2),如果我们要查找的范围是(0,i-1)和(i+1,nums.size()-1),假如我们要的数有一个数k在(0,i-1)中,那么另外的一个数假设是m。即存在
k + m = target - nums[i], m + nums[i] = target - k(注意这里之前我们已经求过target - k),
因此为了去重,可以把(0, i - 1)的范围去掉,接下来目标就是位于
(i + 1, nums.size() - 1)
的两数之和问题了。

class Solution
{
public:
    vector<vector<int>> threeSum(vector<int>& nums)
    {
        vector<vector<int> >ans;
        if (nums.size() < 3) return ans;
        
        sort(nums.begin(), nums.end());
        int new_size = nums.size() - 2;
        for (int i = 0; i < new_size; ++i)
        {
            if (nums[i] > 0) break;
            if (nums[i] == nums[i - 1] && i) continue;//去重
            int target = -nums[i];
            int top = i + 1, tail = new_size + 1;
            while (top < tail)
            {
                if (nums[top] + nums[tail] == target)
                {
                    ans.push_back({nums[i], nums[top], nums[tail]});
                    while ((top + 1) < tail && nums[top] == nums[top + 1])++top;//去重
                    while ((tail - 1) > i && nums[tail] == nums[tail - 1])--tail;//去重
                    ++top;
                    --tail;
                }
                else if (nums[top] + nums[tail] > target) --tail;
                else top++;
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值