【leetcode】15. 三数之和(排序+左右双指针)

目录

题目

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

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

示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

思路

leetcode清晰思路
leetcode代码交流

时间复杂度O(N^2), 空间复杂度O(1)

1. 先对数组排序,便于判断重复元素O(NlogN)

2. 一层遍历O(N):对于每一个nums[i],在[i+1,n-1]范围内找出包含自己的不重复的三元组。

  • 具体来说,就是双指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],计算三个数的和 sum 判断是否满足为 0,满足则添加进结果集。
  • 如果不满足,就根据sum比0大还是比0小,来判断左指针右移还是右指针左移。这个过程是O(N)。
  • 所以总的来说时间复杂度O(N^2)。

3. 遍历过程中为了保证三元组的唯一性,在找到一组合适的三元组后,对于重复值,要加快指针的移动:

  • (1)nums[i]如果和nums[i-1]相等,则忽略;
  • (2)sum为0时,如果nums[L]==nums[L+1],则后面的都要跳过;
  • (3)sum为0时,如果nums[R]==nums[R-1],则后面的都要跳过。

4. 为了优化遍历,当 nums[i]大于 0,则三数之和必然大于 0,可以直接结束循环

class Solution {
public:
/*
错误超时思路:两层循环遍历数组,将两两组合放入哈希表中,key为两数之和,value为二维数组,里面放的都是和为key的序号对;再来一次遍历数组,寻求刚好满足的第三者。需要注意的是,把符合条件的三元组放进vector中,然后在去去重,这样是非常费时的,很容易超时。
时间复杂度O(N^2),空间复杂度O(N)。
*/
/*
换一种思路。
1、先对数组排序,便于判断重复元素O(NlogN)
2、一层遍历O(N):对于每一个nums[i],在[i+1,n-1]范围内找出包含自己的不重复的三元组。
具体来说,就是双指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],计算三个数的和 sum 判断是否满足为 0,满足则添加进结果集。如果不满足,就根据sum比0大还是比0小,来判断左指针右移还是右指针左移。这个过程是O(N)。所以总的来说时间复杂度O(N^2)。
3、遍历过程中为了保证三元组的唯一性,在找到一组合适的三元组后,对于重复值,要加快指针的移动:(1)nums[i]如果和nums[i-1]相等,则忽略;(2)sum为0时,如果nums[L]==nums[L+1],则后面的都要跳过;(3)sum为0时,如果nums[R]==nums[R-1],则后面的都要跳过。
4、为了优化遍历,当 nums[i]大于 0,则三数之和必然大于 0,可以直接结束循环

*/
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int> > anws;
        if( nums.size() <= 2 ){
            return anws;
        }
        //先对数组排序,便于判断重复元素O(NlogN)
        sort( nums.begin(), nums.end() );
        if( nums[0] > 0 ){ //全是正数肯定无解
            return anws;
        }
        //对于每一个nums[i],在[i+1,n-1]范围内找出包含自己的不重复的三元组
        int l,r,sum;
        for(int i=0; i<nums.size(); i++){
            //优化遍历,可直接结束
            if( nums[i] > 0 ){
                break;
            }
            //左右双指针遍历[i+1,n-1]
            l = i+1, r = nums.size()-1, sum;
            while( l < r ){
                sum = nums[i] + nums[l] + nums[r];
                if( sum == 0 ){
                    anws.push_back({nums[i], nums[l], nums[r]});
                    //三元组不能重复,保留最后一次符合的情况。注意l,r相邻的情况不用跳。
                    while( l<r && nums[l]==nums[l+1] ){
                        l++;
                    }
                    if( l<r && nums[r]==nums[r-1] ){
                        r--;
                    }
                    //这里不能丢啊!前面那是去重操作,这个是一定要有的正常指针移动操作
                    l++; 
                    r--;
                }else if( sum < 0 ){//左指针右移
                    l++;
                }else{ // >0则右指针左移动
                    r--;
                }
            }
            //三元组不能重复。避免nums[i]作为第一个数重复出现
            while(i+1<nums.size() && nums[i] == nums[i+1])
                i++;
        }
        return anws;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值