leetcode
leetcode 201 ~ 250
较小的三数之和

较小的三数之和

难度:

标签:

题目描述

代码结果

运行时间: 44 ms, 内存: 16.5 MB


/*
题目思路:
给定一个数组nums和一个目标值target,找出数组中三个不同元素的组合,使它们的和小于目标值。
使用Java Stream,我们将数组转换为一个IntStream,然后使用多重嵌套来处理组合问题。
由于Stream不直接支持组合操作,我们需要转换成boxed的形式并处理集合。 
*/
import java.util.Arrays;
import java.util.stream.IntStream;
 
public class Solution {
    public long threeSumSmaller(int[] nums, int target) {
        Arrays.sort(nums); // 先对数组进行排序
        return IntStream.range(0, nums.length - 2) // 遍历第一个元素
                .mapToLong(i -> IntStream.range(i + 1, nums.length - 1) // 遍历第二个元素
                        .mapToLong(j -> IntStream.range(j + 1, nums.length) // 遍历第三个元素
                                .filter(k -> nums[i] + nums[j] + nums[k] < target) // 过滤符合条件的组合
                                .count()) // 计数符合条件的组合数量
                        .sum()) // 汇总第二层循环的计数
                .sum(); // 汇总第一层循环的计数
    }
}

解释

方法:

这个题解使用了排序加双指针的方法来解决问题。首先,将输入数组排序,这样可以更容易地使用双指针来找出满足条件的三元组。对于数组中的每一个元素`c`,使用两个指针`p1`和`p2`,分别指向`c`后面的第一个元素和数组的最后一个元素。通过移动这两个指针来寻找所有满足`nums[p1] + nums[p2] < target - c`的元素对。如果找到这样的对,那么`p1`到`p2`之间的所有元素都可以与`c`组合满足条件,因为数组已经排序。因此,直接将`p2-p1`加到结果中,并将`p1`向右移动查找新的组合。如果不满足条件,则将`p2`向左移动,减小总和。

时间复杂度:

O(n^2)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在这个算法中选择对数组进行排序?
在这个算法中选择对数组进行排序是因为排序可以帮助简化双指针遍历的逻辑。排序后,数组中的元素是按升序排列的,这使得使用双指针技术寻找和小于特定值的两个数变得更直接和高效。当我们固定一个元素`c`,并使用双指针`p1`和`p2`分别指向`c`之后的第一个元素和数组的最后一个元素时,如果`nums[p1] + nums[p2]`小于目标值减去`c`的差值,那么`p1`到`p2`之间的所有元素对都将满足条件,因为它们都小于`nums[p2]`。如果数组未排序,我们无法保证这种属性,从而无法简单地使用双指针技术统计符合条件的组合数。
🦆
在算法的执行过程中,如何确保在移动指针后,组合仍然满足条件`nums[p1] + nums[p2] < target - c`?
在算法执行过程中,确保组合满足条件`nums[p1] + nums[p2] < target - c`的关键在于正确地移动双指针。当我们发现`nums[p1] + nums[p2]`小于目标值减去`c`时,可以确定从`p1`到`p2`之间的任何索引与`p1`组合都会满足条件,因为数组是排序的,所有这些元素都不会大于`nums[p2]`。在这种情况下,我们将`p1`指针向右移动,以试图找到更多的满足条件的组合。如果`nums[p1] + nums[p2]`不小于目标值减去`c`,则需要将`p2`指针向左移动以减小总和,因为这样做会使得`nums[p2]`变小,可能再次满足条件。这种策略保证了每次移动指针后,新的组合仍然有可能满足条件。
🦆
请问`ret += p2 - p1`这一步骤是如何计算出所有有效组合的数量的?能否详细解释其逻辑?
在算法中,`ret += p2 - p1`这一步骤是用来计算所有有效的组合数量的关键。当算法确定`nums[p1] + nums[p2]`小于目标值减去`c`时,由于数组已经排序,我们知道`p1`和`p2`之间的任何索引都可以与`p1`形成满足条件的组合。因此,从`p1`到`p2`之间共有`p2 - p1`个元素,而每个元素与`p1`组合都是一个有效的组合。例如,如果`p1`是3,`p2`是6,那么`nums[3]`可以与`nums[4]`、`nums[5]`和`nums[6]`组成三个有效的组合。因此,这一步骤通过简单地加上这些索引之间的数量来快速统计所有可能的满足条件的组合数,极大地提高了算法的效率。

相关问题

三数之和

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

最接近的三数之和

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。

假定每组输入只存在恰好一个解。

 

示例 1:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

示例 2:

输入:nums = [0,0,0], target = 1
输出:0

 

提示:

  • 3 <= nums.length <= 1000
  • -1000 <= nums[i] <= 1000
  • -104 <= target <= 104

有效三角形的个数

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

 

示例 1:

输入: nums = [2,2,3,4]
输出: 3
解释:有效的组合是: 
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3

示例 2:

输入: nums = [4,2,3,4]
输出: 4

 

提示:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 1000

小于 K 的两数之和

小于 K 的两数之和