双指针(7)_单调性_三数之和

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

双指针(7)_单调性_三数之和

收录于专栏【经典算法练习
本专栏旨在分享学习C++的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1. 题目链接:

2.题目描述 :

3.解法 :

    解法一(暴力枚举) :

    算法思路 :

    代码展示 :

    结果分析 :

    解法二(排序 + 双指针) :

    算法思路 :

    图解流程 :

    代码展示 :

    结果分析 :

    4.总结 :


1. 题目链接:

OJ链接----. - 力扣(LeetCode)

2.题目描述 :

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

3.解法 :

    解法一(暴力枚举) :

    算法思路 :

1. 对整个数组进行排序

2. 三层循环遍历数组

3. 使用set去重 

    代码展示 :

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> T;
        sort(nums.begin(), nums.end());
        int n = nums.size();
        for(int i = 0; i < n - 2; i++)
            for(int j = i + 1; j < n; j++)
                for(int k = j + 1; k < n; k++)
                {
                    if(nums[i] + nums[j] + nums[k] == 0) 
                    {
                        T.insert({nums[i], nums[j], nums[k]});
                    }
                }

        vector<vector<int>> result(T.begin(), T.end());
        return result;
    }
};

 

    结果分析 :

由于数据范围是3000,所以我们这个三层循环就算有了排序的优化,也没能通过(只差5个例子), 

    解法二(排序 + 双指针) :

这里大家可以先去看查找总价格为目标值的两个商品这道题,那道题与本题思路很像

--双指针(6)_单调性_查找总价格为目标值的两个商品-CSDN博客

    算法思路 :

本题与两数之和类似,是非常经典的面试题

与两数之和稍微不同的是,题目中要求找到所有[不重复]的三元组.那我们可以利用在两数之和那里用的双指针思想,来对我们的暴力枚举做优化:

        i. 先排序

        ii. 然后固定一个数a:

        iii. 在这个数后面的区间内,使用双指针算法快速找到两个数之和等于-a即可

但是要注意的是,这道题需要有[去重]操作~

        i. 找到一个结果之后,left和right指针要[跳过重复]的元素

        ii. 当使用完一次双指针算法之后,固定a也要[跳过重复]的元素

    图解流程 :

开始我们就对数组进行排序,这样数组中的序列就是单调有序的,这样我们就可以使用双指针进行求解问题!!!

 

 固定一个数,剩下的数构成二元组,然后在二元组里面利用双指针进行寻找

如果left + right < -dest

说明right左边所有数加left都小于dest

那么left就无需遍历,left++

 

如果left + right > -dest

说明left右边所有数加right都大于dest

那么right就无需遍历,right--

 

如果left + right == -dest

说明找到题目符合条件的数,插入到vector

left++,right--

 

注意:(去重问题)

这里也是left + right == -dest

说明找到题目符合条件的数,插入到vector

left++,right--

但是这里要判断nums[left - 1] == nums[left] 如果相等left直接++

nums[right] == nums[right - 1] 如果相等直接--

 

 

进行去重处理后left指向1, right也指向1

此时left + right < -dest lest++

left < right(不满足)循环结束

dest++

 

dest++的小优化: 

当dest大于0时:

循环可以直接结束,因为我们的数组已经排序

left和right也一定是大于0的

 

去重的边界问题

left 和right:

它们两个去重时,可能会访问到未定义的内存,需要进行条件束缚

left < right

dest:

它在去重时也可能会访问到未定义的内存,也需要进行条件束缚\

dest < nums.size()

 

    代码展示 :

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;

        sort(nums.begin(), nums.end());
        
        int n = nums.size();

        for(int dest = 0; dest < n; )
        {
            if(nums[dest] > 0) break;
            int left = dest + 1, right = n - 1;

            while(left < right)
            {
                if(nums[left] + nums[right] < -nums[dest]) left++;
                else if(nums[left] + nums[right] > -nums[dest]) right--;
                else
                {
                    result.push_back({nums[dest], nums[left], nums[right]});
                    left++;
                    right--;
                    while(left < right && nums[left - 1] == nums[left]) left++;
                    while(left < right && nums[right] == nums[right + 1]) right--;
                }
            }
            
            dest++;
            while(dest < n && nums[dest] == nums[dest - 1]) dest++;
        }
        return result;
    }
};

 

    结果分析 :

顺利通过!!!
这道题很有价值,虽然难度是中等,但已经逼近困难了!

这道的思路其实不难(其实就是之前双指针(6)中那题一样的思想),它难的地方就在于如何去重,在去重的过程中是否会注意到一些细节问题!

    4.总结 :

 这道题使用的是排序 + 双指针算法

1. 排序:

2. 固定一个数a:

3. 在该数后面的区间内,利用双指针算法快速找到两个的和等于-a即可

处理细节问题:

1. 去重

        找到一种结果后,left和right指针要跳过重复元素

        当使用完一次双指针算法之后,i也需要跳过重复元素

2. 不漏

        找到一种结果后,不要停,缩小区间,继续寻找.

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值