LeetCode-三数之和(双指针)

前言

本周分享一道双指针相关的算法题。作者水平有限,有任何问题欢迎在文章下方留言交流!
关键词:双指针。

题目

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

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

示例
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

力扣原题:https://leetcode-cn.com/problems/3sum

解法1-暴力破解

从头开始遍历,先确定第1个元素,然后遍历尝试与第2个元素组合,然后再对每一个组合遍历尝试第3个元素。

每找到一组符合条件的组合便与已找到的组合逐个做比较,若重复则去掉。

此法时间复杂度当然是相当之高的。

时间复杂度:O(n^3)

解法2-排序+双指针

上述暴力破解法显然存在巨大优化空间。我们可以使用双指针的方法来解决此问题。

算法具体流程:

  • 特判:如果原数组长度len小于3,则直接返回

  • 对数组进行排序

  • 从头开始遍历数组,遍历的条件是i<len-2,且nums[i]小于等于0

    • 若当前元素nums[i]与前一个元素nums[i-1]相同,则跳过,防止得到重复结果

    • 取左指针Li+1,右指针Rlen-1,当左边界小于右边界时执行循环:

      • nums[i]+nums[L]+nums[R] < 0,则L=L+1,如果nums[L]==nums[L-1],为防止结果重复,L继续往右移
      • nums[i]+nums[L]+nums[R] > 0,则R=R-1,如果nums[R]==nums[R+1],为防止结果重复,R继续往左移
      • nums[i]+nums[L]+nums[R] == 0,则加入结果集,然后L=L+1R=R-1,同时也需防止结果重复

代码实现:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        // result:结果集
        vector<vector<int>> result;
        // 获取原数组长度
        int len = nums.size();
        // 特判:如果原数组长度小于3,则直接返回
        if(len<3) return result;
        // 先对原数组进行排序
        sort(nums.begin(), nums.end());
        // 从头开始遍历数组,遍历的条件是i<len-2,且nums[i]<=0
        for(int i=0;i<len-2 && nums[i]<=0;i++){
            // 若当前元素与前一个元素相同,则跳过,防止得到重复结果
            // 要记得限制i的大小,否则下标会越界
            while(i>0 && i<len-1 && nums[i]==nums[i-1]) i++;
            // L:左指针,初始化为当前元素+1位置
            int L = i+1;
            // R:右指针,初始化为末尾元素位置
            int R = len-1;
            // 开始循环查找符合条件的组合,当右指针小于或等于左指针时跳出循环
            while(L<R){
                if(nums[i]+nums[L]+nums[R] == 0){
                    // 若该组合符合条件,则加入结果集
                    result.push_back({nums[i], nums[L], nums[R]});
                    // 左指针往右移
                    L++;
                    while(L>0 && L<len-1 && nums[L] == nums[L-1]){
                        // 若左指针往右移时发现该元素与前一个元素相同,则继续往右移,防止得到重复结果
                        // 此处同样要记得限制L的大小,否则下标会越界
                        L++;
                    }
                    // 右指针往左移
                    R--;
                    while(R>0 && R<len-1 && nums[R] == nums[R+1]){
                        // 若右指针往左移时发现该元素与后一个元素相同,则继续往左移,防止得到重复结果
                        // 此处同样要记得限制R的大小,否则下标会越界
                        R--;
                    }
                }else if(nums[i]+nums[L]+nums[R] < 0){
                    // 若该组合和小于0
                    // 则调整左指针往右移
                    L++;
                    while(L>0 && L<len-1 && nums[L] == nums[L-1]){
                        // 若左指针往右移时发现该元素与前一个元素相同,则继续往右移,防止得到重复结果
                        L++;
                    }
                }else if(nums[i]+nums[L]+nums[R] > 0){
                    // 若该组合和大于0
                    // 则调整右指针往左移
                    R--;
                    while(R>0 && R<len-1 && nums[R] == nums[R+1]){
                        // 若右指针往左移时发现该元素与后一个元素相同,则继续往左移,防止得到重复结果
                        R--;
                    }
                }
            }
        }
        // 最后返回结果集
        return result;
    }
};

时间复杂度:
排序时间复杂度O(Nlog2(N))
遍历数组O(n)
双指针遍历O(n)
总体时间复杂度O(N
log2(N))+O(n)*O(n),即O(n^2)。

空间复杂度:O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值