[leetcode数组系列]2三数之和

文章首发于公众号[我是程序员小贱]

前言

秋招的结束,面试了大大小小的公司,最大的问题在于算法上。所以打算坚持在leetcode打卡,看看到底能不能行,如果你想见证,那我来开车,你坐稳,一起走向更好的远方。

在学习今天内容之前,先学习上一篇的两数之和会更好哟
leetcode两数之和求解

一 题目

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

1 leetcode链接

https://leetcode-cn.com/problems/3sum/

示例

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

满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

2 思路1---暴力解法

在思考两数之和解决方法的时候,我们使用了两层循环把所有的结果给求出来,相信读者很快就想到三数之和我就用三个循环,很棒,思路是一样,只是之前的a+b=0,现在的b=c+d了。好了代码呈上。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        if(nums.size()<3) return{};
        vector<vector<int>> res;//结果
        set<vector<int>> ret;//用来去重
        for(int i=0;i<nums.size()-2;i++)
        {
            for(int j=i+1;j<nums.size()-1;j++)
            {
                for(int k=j+1;k<nums.size();k++)
                {
                    if(nums[i]+nums[k]+nums[j]==0)
                    {
                        vector<int> tp;
                        int a=(nums[i]<nums[j]?nums[i]:nums[j])<nums[k]?(nums[i]<nums[j]?nums[i]:nums[j]):nums[k];//放最小的元素
                        int b=(nums[i]>nums[j]?nums[i]:nums[j])>nums[k]?(nums[i]>nums[j]?nums[i]:nums[j]):nums[k];//放最大的元素
                        int c=0-a-b;
                        tp.push_back(a);
                        tp.push_back(c);
                        tp.push_back(b);
                        ret.insert(tp);
                    }
                }
            }
        }
        for(auto it:ret)
        {
            res.push_back(it);
        }
        return res;
    }
};

3 思路2---排序加双指针

  • 第一步将整个数组排序,如下图。
    在这里插入图片描述

  • 从左侧开始,选定第一个数为定值比如下面的-4,然后左右指针分别指向对应位置如下图,是不是很像快排。
    在这里插入图片描述

  • 定义的左右和定值相加

    • 如果等于0,记录下三个值
    • 如果小于0,左指针右移动
    • 如果大于0,右指针左移
  • 然后定值右移,重复这个步骤

下面先看看整体的代码,随后图解整个代码流程以及如何处理重复的情况

c++版本

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
         int target;
        vector<vector<int>> ans;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            if ((target = nums[i]) > 0) break;
            int left = i + 1, right = nums.size() - 1;
            //取出三个数 所以不用left=right
            while (left < right) {
                if (nums[left] + nums[right] + target < 0) ++left;
                else if (nums[left] + nums[right] + target > 0) --right;
                else {
                    ans.push_back({target, nums[left], nums[right]});
                    ++left, --right;
                    //去重 
                    //测试数据[-2,0,0,2,2]
                    while (left < right && nums[left] == nums[left - 1]) ++left;
                    while (left < right && nums[right] == nums[right + 1]) --right;
                }
            }
        }
        return ans; 
    }
};

一次循环的图解

在这里插入图片描述
如果有重复数,怎么去重的呢。如果测试数据为[-2,0,0,2,2]。

在这里插入图片描述
我想起在参考招聘要求的时候有句话是熟悉c/c++,java之一,同时了解python等脚本更好,所以在此放上python的方法。

python版本

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:

        nums.sort()
        n = len(nums)#获取列表长度
        res = []#存放结果

        for i in range(n):
            if i > 0 and nums[i] == nums[i-1]:
                continue
            num1 = nums[i]#取得定值
            if num1 > 0:
                break
            #定义双指针初始状态
            j = i + 1
            k = n - 1
            while j < k:
                total = num1 + nums[j] + nums[k]
                if total == 0:
                    res.append((num1, nums[j], nums[k]))
                    j += 1
                    while nums[j] == nums[j-1] and j < k:
                        j += 1
                    k -= 1
                    while nums[k] == nums[k+1] and j < k:
                        k -= 1
                elif total > 0:
                    k -= 1
                    while nums[k] == nums[k+1] and j < k:
                        k -= 1
                else:
                    j += 1
                    while nums[j] == nums[j-1] and j < k:
                        j += 1
        return res

4 总结

文中使用了两种方式来解决这个问题,第一种为复杂度较高的暴力解答,第二种使用了类似快排思想的双指针法,后面还会有更多关于双指针的用法,敬请期待。至此,想想有学会点什么了?

5 结尾

希望读者和咱一起一步一个脚印去把基础知识打牢固。如果读者发现有什么错误或者不太好的地方,欢迎私我,我会及时修改。如果觉得不错或者方便手机上查看可以在下面公众号温故复习哟!

img

原文地址:https://www.cnblogs.com/lanjianhappy/p/12142024.html