LeetCode 16. 最接近的三数之和(3Sum Closest)解题详解
难度:中等
相关标签:数组、双指针、排序
相关企业:各大科技公司面试常见题目
题目描述
给你一个长度为 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
解释:与 target 最接近的和是 0(0 + 0 + 0 = 0)。
提示
3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-10^4 <= target <= 10^4
解题思路
这道题要求我们找到三个数的和最接近目标值 target
。为了解决这个问题,我们可以采用以下步骤:
-
排序数组:先将数组
nums
进行排序。这有助于我们利用双指针技术高效地寻找三元组。 -
遍历数组:固定第一个数,然后使用双指针在剩余的数组中寻找另外两个数,使得三数之和最接近
target
。 -
双指针:
- 初始化两个指针,一个指向当前固定数之后的第一个数(
left
),另一个指向数组的末尾(right
)。 - 计算当前三数之和
a = nums[i] + nums[left] + nums[right]
。 - 如果
a
比当前最接近的和更接近target
,则更新结果。 - 根据
a
与target
的关系,移动left
或right
指针:- 如果
a < target
,移动left
指针向右,以增加和。 - 如果
a > target
,移动right
指针向左,以减少和。 - 如果
a == target
,直接返回a
,因为这是最接近的和。
- 如果
- 初始化两个指针,一个指向当前固定数之后的第一个数(
-
记录最小差值:在遍历过程中,持续记录与
target
差值最小的三数之和。
代码实现
以下是基于上述思路的 Python 代码实现:
from typing import List
from collections import Counter
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
# 排序数组
nums.sort()
n = len(nums)
# 初始化最接近的和为无穷大
ans = float('inf')
# 遍历数组,固定第一个数
for i in range(n - 2):
# 初始化双指针
left = i + 1
right = n - 1
while left < right:
# 计算当前三数之和
a = nums[i] + nums[left] + nums[right]
# 如果当前和比之前的更接近目标,更新答案
if abs(a - target) < abs(ans - target):
ans = a
# 根据当前和与目标的关系,移动指针
if a < target:
left += 1
elif a > target:
right -= 1
else:
# 如果和等于目标,直接返回
return a
return ans
代码解析
1. 排序数组
nums.sort()
n = len(nums)
首先对数组进行排序,这样可以方便地使用双指针技术。排序的时间复杂度是 O(n log n)
。
2. 初始化最接近的和
ans = float('inf')
使用 ans
来记录当前最接近 target
的三数之和,初始值设为无穷大。
3. 遍历数组并固定第一个数
for i in range(n - 2):
left = i + 1
right = n - 1
通过遍历数组的每个元素,固定第一个数 nums[i]
,然后在剩余的数组中使用双指针 left
和 right
寻找另外两个数。
4. 使用双指针寻找最接近的三数之和
while left < right:
a = nums[i] + nums[left] + nums[right]
if abs(a - target) < abs(ans - target):
ans = a
if a < target:
left += 1
elif a > target:
right -= 1
else:
return a
- 计算当前三数之和
a
。 - 如果
a
与target
的差值比之前的差值更小,更新ans
。 - 根据
a
与target
的大小关系,移动left
或right
指针:a < target
:说明当前和偏小,需要增加和,因此移动left
向右。a > target
:说明当前和偏大,需要减少和,因此移动right
向左。a == target
:直接返回a
,因为已经是最接近的和。
5. 返回结果
return ans
遍历完成后,返回最接近的三数之和 ans
。
示例分析
示例 1
输入:nums = [-1, 2, 1, -4], target = 1
输出:2
步骤:
- 排序后
nums = [-4, -1, 1, 2]
。 - 固定第一个数
-4
,使用双指针在[-1, 1, 2]
中寻找:- 当前和
-4 + (-1) + 2 = -3
,差值为4
。 -3 < 1
,移动左指针。- 当前和
-4 + 1 + 2 = -1
,差值为2
。 -1 < 1
,移动左指针。
- 当前和
- 固定第一个数
-1
,使用双指针在[1, 2]
中寻找:- 当前和
-1 + 1 + 2 = 2
,差值为1
。 2 > 1
,移动右指针,结束。
- 当前和
- 最终最接近的和为
2
。
示例 2
输入:nums = [0, 0, 0], target = 1
输出:0
步骤:
- 排序后
nums = [0, 0, 0]
。 - 固定第一个数
0
,使用双指针在[0, 0]
中寻找:- 当前和
0 + 0 + 0 = 0
,差值为1
。 0 < 1
,移动左指针,结束。
- 当前和
- 最终最接近的和为
0
。
性能分析
-
时间复杂度:
O(n^2)
- 排序需要
O(n log n)
。 - 外层循环遍历
n
个元素,内层双指针遍历最多n
个元素。 - 总体时间复杂度为
O(n^2)
。
- 排序需要
-
空间复杂度:
O(1)
(忽略输入和输出)- 除了排序所需的空间外,没有使用额外的空间。
总结
这道题目采用了排序和双指针的经典技巧,通过固定一个元素,并在剩余部分使用双指针寻找最接近的三数之和。关键在于理解如何利用排序和双指针高效地缩小搜索范围,并持续更新最接近的结果。
这种方法不仅适用于本题,也在很多类似的数组问题中有广泛应用,如 3Sum
等。
如果你对这道题还有疑问,欢迎在评论区讨论!