Leetcode: 15. 3Sum(Week3,Medium)

本文详细解析了LeetCode第15题“三数之和”的算法实现过程,介绍了如何通过排序和双指针技巧高效地寻找数组中三个数相加等于0的所有不重复组合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:本题算法无需实现去重操作。


Leetcode 15
Given an array S of n integers, are there elements a, b, c in S 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.
For example, given array S = [-1, 0, 1, 2, -1, -4],

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

  • 题意:

    • 现有一个数组S,题目希望你实现一个算法,能够找出该数组中满足a+b+c = 0的所有a、b、c组合,并且不能有重复的情况。
  • 思路

    • 首先说一下常规思路:先求出所有的情况,然后再进行去重。

      • 对于求解所有情况,可以采用暴力解法,但开销有点大;也可以采用排序的方式,开销比较小,这个待会会解释。

      • 对于去重,很容易就想到用STL中的set容器。但是有个问题,set容器可以检测出[1,2,3]与[1,2,3]是相同的,但是无法得到出[3,2,1]与[1,2,3]也是相同的情况,应该去除。不过也可以考虑将[3,2,1]与[1,2,3]加入一个set中,如果set的大小为3,说明[3,2,1]与[1,2,3]只需要一个。不过还是好麻烦好麻烦~

    • 所以讲一下我的思路:在查找的时候避开重复的情况,这样最终得到的vector是不会包含重复的结果的。

      • 首先,需要对数组进行排序。为什么进行排序?有三点原因:第一,观察题目给出的example,可以发现每种情况都是有序的;第二,通过排序,可以减少循环的次数;第三,通过排序,为避开重复的情况奠定了基础。

      • 然后,定义第一层循环,遍历数组元素。这层循环中遍历的数组元素,可以作为a,于是就能得出b+c = -a。定义front与back变量,使用一个while循环,从两边向中间遍历,找到符合条件的组合,加入到vector中。当b+c>-a, 说明和太大了,需要减少,所以将back–;当b+c< -a,说明和太小了,需要增大,所以front++; 当b+c = -a时,满足条件,加入vector,并进行一个或多个front++与back–,找到与之前不同的s[front]与s[back]。当然,这样只是避开了b、c的重复情况,对于a也要做相应处理。即在while循环外进行i++,直到s[i+1]!=s[i]。

  • 算法:在思路中已经较为详细的阐述,如果还是有点抽象,可以参考下面代码及注释。

代码如下:

/*
2017/9/24  3Sum(a+b+c=0)
主要思路:
1. 将数组排序(减少循环次数;避免重复情况;结果是有序的);
2. 第一层循环遍历vector数组,数组元素代表a;
3. 定义两个变量front与back,一个从第一层循环的数组元素的下一个开始,一个从数组的最后一个开始。
   用一个while循环实现front与back向中间移动,查找两个元素的和为第一个数组元素的相反数;
4. 避开了重复的情况。因为数组排序了,在遍历的过程中可以跳过相同的元素。
 */
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int> > res;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            int target = -nums[i];
            int front = i+1;
            int back = nums.size()-1;
            while(front < back) {
                // 说明和过大,需要减少
                if (nums[front] + nums[back] > target) {
                    back--;
                // 说明和过小,需要增大
                } else if (nums[front] + nums[back] < target) {
                    front++;
                // 和满足的情况下
                } else {
                    vector<int> item;
                    vector<int> temp(3, 0);
                    temp[0] = nums[i];
                    temp[1] = nums[front];
                    temp[2] = nums[back];
                    item.push_back(nums[i]);
                    item.push_back(nums[front]);
                    item.push_back(nums[back]);
                    res.push_back(item);
                    // 找到与之前不同的b、c
                    while(front < back && nums[front] == temp[1]) front++;
                    while(front < back && nums[back] == temp[2]) back--;
                }
            }
            // 找到与之前不同的a
            // 此处使用i+1是因为for循环的循环更新变量(i)会在本次循环结束的时候递增
            while(i+1 < nums.size() && nums[i+1] == nums[i]) i++;
        }
        return res;
    }
};

以上内容皆为本人观点,欢迎大家提出批评和指导,我们一起探讨!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值