LeetCode - 15 三数之和

这篇具有很好参考价值的文章主要介绍了LeetCode - 15 三数之和。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

题目来源

题目描述

示例

提示

题目解析

算法源码


题目来源

15. 三数之和 - 力扣(LeetCode)

题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != 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
  • -10^5 <= nums[i] <= 10^5

题目解析

本题需要从nums数组中找出所有和为0的三元组,且找出的三元组不能重复。

本题可以分解为两部分逻辑:

  1. 找到和为0的三元组
  2. 三元组去重

找和为0的三元组,最简单的方法就是三重for暴力枚举,时间复杂度O(n^3),n = nums.length(),结合备注的nums长度,可以得出暴力法会超时。

因此,我们需要一种更优的算法。

当前最优算法思路为:

首先,将nums数组进行升序,

然后,利用三个指针 i, l, r 来选择三个数,如果i, l,r指针指向的三数之和为0,则找到一个符合要求的三元组。

三指针指向的元素值的大小关系:nums[i] <= nums[l] <= nums[r]

上面三指针的运动逻辑其实可以概括,

首先确定三元组的最小值nums[i],然后找到一个与之匹配的nums[l],nums[r]

这样的话,就将三指针运动 转为了 双指针运动。

我们通过下面 示例1 图示来理解运动过程:

nums = [-1,0,1,2,-1,-4] 经过升序后,变为nums = [-4,-1,-1,0,1,2]

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript

如上图,我们分别找了:

  • 三元组中最小值为-4,即 i 固定指向 0
  • 三元组中最小值为-1,即 i 固定指向 1
  • 三元组中最小值为-1,即 i 固定指向 2
  • 三元组中最小值为0,即 i 固定指向 3

的三元组情况。而针对上面情况,初始时l,r指针固定为:

  • l = i + 1
  • r = nums.length - 1

由于nums已经升序,因此,如果i, l, r三元组之和sum

  • sum > 0,则说明三元组之和大了,我们应该减小它,此时
  1. i 指针是固定的,因此不能移动 i 指针
  2. l 指针只能向右移动,因为 l 不能比 i 小,但是 l 指针右移只会增大三元组之和,因此不能移动 l 指针
  3. r 指针只有向左移动,而 r 指针向左移动的话,会导致nums[r]减少,因此三元组之和也会减少,所以我们左移 r 指针,即r--,可以减少三元组之和
  • sum < 0,则说明三元组之和小了,我们应该增大它,此时我们应该右移 l 指针,即 l++,来增大三元组之和
  • sum == 0,则说明当前i, l, r指向的三个数就是一个和为0的三元组

上面就是找和为0的三元组的逻辑。


下面就是去重逻辑的实现了

我们通过示例一可以发现,三指针运动过程中,产生了重复的三元组,如下图所示

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript

那么如何才能去重呢?

最简单的方法,其实就是将所有和为0的三元组求出后,对比去重,但是效率非常低,不推荐。

更高效的去重策略是,发现规律,通过上面图示,我们可以发现:

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript

重复的两个三元组,如果基于后面一个来看的话,那么变化的只有 i 指针,且nums[i] == nums[i-1],而l,r位置未发生改变。

如果我们把nums[i]和nums[i-1]看出一个整体的话,那么这两个重复二元组就是同一个。

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript

且他们对于的l,r指针的运动逻辑也是一致的。因此后续会产生重复的二元组。

因此,我们可得去重结论,如果 num[i] == nums[i-1],则后续的l,r指针运动就可以不做了,因为会产生重复的二元组。


如果上面的 i 指针会产生重复二元组外,对于 l, r 指针同样会产生重复二元组。

比如下面例子:

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript

上面例子中,产生了多个重复的三元组

但是实际上,

如果 nums[l] == nums[l+1],则可以直接 l++ 

如果 nums[r] == nums[r-1],则可以直接 r--

即运动过程如下:

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript

即 nums[l] == nums[l+1] 的话,则 i, l, r 其实和 i, l+1,r 三元组是重复的,因此,我们可以直接跳过l+1的三元组,选择l++

    nums[r] == nums[r-1] 的话,则i ,l , r其实和i, l, r-1 三元组是重复的,因此,我们可以直接跳过r-1的三元组,选择r-- 


另外本题还有一个剪枝优化,

如果 nums[i] > 0的话,由于nums[i]是三元组中最小值,因此nums[i] > 0的话,则nums[l],nums[r]必然也大于0,三元组之和也大于0。

并且nums已经升序,因此nums[i+1]>0也是必然的,即nums[i+1]对应的三元组也是大于0的。

因此如果nums[i] > 0了,则可以直接break掉整个三指针运动。

JS算法源码

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function (nums) {
  const ans = [];

  nums.sort((a, b) => a - b);

  for (let i = 0; i < nums.length - 2; i++) {
    // 剪枝
    if (nums[i] > 0) break;

    // 去重
    if (i > 0 && nums[i] == nums[i - 1]) continue;

    let l = i + 1;
    let r = nums.length - 1;

    while (l < r) {
      const sum = nums[i] + nums[l] + nums[r];

      if (sum > 0) {
        r--;
      } else if (sum < 0) {
        l++;
      } else {
        ans.push([nums[i], nums[l], nums[r]]);

        // 去重
        while (l + 1 < r && nums[l] == nums[l + 1]) l++;
        // 去重
        while (r - 1 > l && nums[r] == nums[r - 1]) r--;
        l++;
        r--;
      }
    }
  }

  return ans;
};

三数之和 - 提交记录 - 力扣(LeetCode)

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript

Java算法源码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

class Solution {
  public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> ans = new ArrayList<>();

    Arrays.sort(nums);

    for (int i = 0; i < nums.length - 2; i++) {
      // 剪枝优化
      if (nums[i] > 0) break;

      // 去重
      if (i > 0 && nums[i] == nums[i - 1]) continue;

      int l = i + 1;
      int r = nums.length - 1;

      while (l < r) {
        int sum = nums[i] + nums[l] + nums[r];

        if (sum > 0) {
          r--;
        } else if (sum < 0) {
          l++;
        } else {
          ArrayList<Integer> tmp = new ArrayList<>();
          Collections.addAll(tmp, nums[i], nums[l], nums[r]);
          ans.add(tmp);

          // 去重
          while (l + 1 < r && nums[l] == nums[l + 1]) l++;
          // 去重
          while (r - 1 > l && nums[r] == nums[r - 1]) r--;

          l++;
          r--;
        }
      }
    }

    return ans;
  }
}

三数之和 - 提交记录 - 力扣(LeetCode)

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript

Python算法源码

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        ans = []

        nums.sort()

        for i in range(len(nums) - 2):
            # 剪枝
            if nums[i] > 0:
                break

            # 去重            
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            l = i + 1
            r = len(nums) - 1

            while l < r:
                total = nums[i] + nums[l] + nums[r]

                if total > 0:
                    r -= 1
                elif total < 0:
                    l += 1
                else:
                    ans.append([nums[i], nums[l], nums[r]])

                    # 去重
                    while l + 1 < r and nums[l] == nums[l + 1]:
                        l += 1

                    # 去重
                    while r - 1 > l and nums[r] == nums[r - 1]:
                        r -= 1

                    l += 1
                    r -= 1

        return ans

三数之和 - 提交记录 - 力扣(LeetCode)

LeetCode - 15 三数之和,算法与数据结构,leetcode,算法,Java,Python,JavaScript文章来源地址https://www.toymoban.com/news/detail-704349.html

到了这里,关于LeetCode - 15 三数之和的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • (双指针 ) 15. 三数之和 ——【Leetcode每日一题】

    难度:中等 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j 、 i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请 你返回所有和为 0 且不重复的三元组。 注意 :答案中不可以包含重复的三元组。 示例 1: 输入:nums = [-1,0,1,2,-1,-4] 输出:

    2024年02月09日
    浏览(51)
  • Leetcode每日一题:15. 三数之和(2023.7.9 C++)

    目录 15. 三数之和 题目描述: 实现代码与解析: 双指针 原理思路:         给你一个整数数组  nums  ,判断是否存在三元组  [nums[i], nums[j], nums[k]]  满足  i != j 、 i != k  且  j != k  ,同时还满足  nums[i] + nums[j] + nums[k] == 0  。请 你返回所有和为  0  且不重复的三元组

    2024年02月13日
    浏览(42)
  • 【算法挨揍日记】day04——15. 三数之和、18. 四数之和

      15. 三数之和 https://leetcode.cn/problems/3sum/ 给你一个整数数组  nums  ,判断是否存在三元组  [nums[i], nums[j], nums[k]]  满足  i != j 、 i != k  且  j != k  ,同时还满足  nums[i] + nums[j] + nums[k] == 0  。请你返回所有和为  0  且不重复的三元组。 注意: 答案中不可以包含重复的三元

    2024年02月09日
    浏览(55)
  • 【算法专题--双指针算法】leecode-15.三数之和(medium)、leecode-18. 四数之和(medium)

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 双指针 常见的双指针有两种形式,一种是对撞指针,⼀种是左右指针。 对撞指针:一般用于顺序结构中

    2024年04月09日
    浏览(49)
  • 【算法】排序+双指针——leetcode三数之和、四数之和

    三数之和 (1)排序+双指针   算法思路: 和之前的两数之和类似,我们对暴力枚举进行了一些优化,利用了 排序+双指针 的思路:   我们先排序,然后固定⼀个数 a ,接着我们就可以在这个数后面的区间内,使用之前两数之和使用的算法,快速找到两个数之和和固定的

    2024年02月13日
    浏览(53)
  • [双指针] (三) LeetCode LCR 179. 查找总价格为目标值的两个商品 和 15. 三数之和

    [双指针] (三) LeetCode LCR 179. 查找总价格为目标值的两个商品 和 15. 三数之和 查找总价格为目标值的两个商品 LCR 179. 查找总价格为目标值的两个商品 题目分析 (1) 数组内数字是升序 (2) 目标值为target (3) 找两数之和为target 解题思路 找两个数字的和与目标值相等,我们可以想到

    2024年02月05日
    浏览(50)
  • 【Leetcode刷题(数据结构)】:三路划分与三数随机取中的思想实现快速排序的再优化

    快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程

    2024年02月08日
    浏览(46)
  • 面试经典题---15.三数之和

    15.三数之和 我的解法: 预处理 当nums大小小于3时,直接返回空的res 对nums排序后,若首元素小于0或末尾元素大于0,也直接返回空的res 双指针法找出三元组(nums[i]、nums[left]和nums[right]) i从0开始取值,对于每个i,判断是否存在三元组和为0的left(初值为i+1)和right(初值为

    2024年01月20日
    浏览(31)
  • leetcode两数、三数、四数之和

    如有错误,感谢不吝赐教、交流 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返

    2023年04月23日
    浏览(73)
  • 【代码随想录06】454. 四数相加 II 383. 赎金信 15. 三数之和 18. 四数之和

    题目描述 给你四个整数数组 nums1 、 nums2 、 nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 = i, j, k, l n nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0 做题思路 本题可以使用哈希表, key 为 nums1[i] + nums2[j] 的和, value 为其出现的次数。然后再遍历 nums3 和

    2024年01月16日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包