LeetCode 1099. 小于 K 的两数之和
题目描述
给定一个整数数组 nums 和一个整数 k,请选择下标满足 i < j 的两个元素,返回小于 k 的两数和的最大可能值。如果不存在这样的两个元素,返回 -1。
示例 1:
输入:nums = [34,23,1,24,75,33,54,8], k = 60
输出:58
解释:可以选择 34 和 24,它们的和为 58,是小于 60 的最大可能值。
示例 2:
输入:nums = [10,20,30], k = 15
输出:-1
解释:不存在两数之和小于 15 的数对。
提示:
2 <= nums.length <= 1001 <= nums[i] <= 10001 <= k <= 2000
题意分析
选择两个不同下标,使它们的和严格小于
k,并在所有合法和中取最大值。返回这个和,没有合法数对时返回-1;两个数值可以相等,但不能重复使用同一个位置。题面保证
1 <= nums[i] <= 1000,所以合法两数和一定为正,-1可以同时作为未找到答案的标记和最大值初值。只要求返回和、不要求保留原下标,因此可以先排序,当前实现会重排输入数组。
解法:排序 + 双指针
核心思路
[!blue]
排序后,让
left、right从两端向内移动。best保存已经见过的最大合法和,每轮根据当前两数和能安全排除一个端点,避免枚举全部数对。若
sum >= k,当前最小左值与right配对都不合法;在剩余区间内换成任何更大的左值,只会让和更大或不变。因此这个右端点不可能再产生合法数对,直接执行right--。若
sum < k,当前右端是剩余候选中的最大值,所以它已经给当前左端提供了最大的剩余合法配对和。先用它更新best,再排除left,尝试增大左值;与更小右端的配对不会改善刚记录的答案。两种移动都只删除不可能带来更优答案的剩余数对,之前排除的数对也已被检查或证明无用,因此指针无需回退。相遇后不再有两个不同位置可选,返回历史最佳值;全程没有合法和时就保留负一。
解题步骤
- 升序排序,初始化
left = 0、right = n - 1、best = -1。- 当
left < right时计算当前和。- 和严格小于
k时,用最大值比较更新best,再右移左端点。- 和大于或等于
k时,左移右端点;扫描结束返回best。
代码实现
class Solution {
public int twoSumLessThanK(int[] nums, int k) {
Arrays.sort(nums);
int left = 0;
int right = nums.length - 1;
int best = -1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum < k) {
// 当前左值的最佳剩余配对已经检查,可排除左端。
best = Math.max(best, sum);
left++;
} else {
// 最小左值也超界,该右端无法与剩余值合法配对。
right--;
}
}
return best;
}
}
import "sort"
func twoSumLessThanK(nums []int, k int) int {
sort.Ints(nums)
left, right := 0, len(nums)-1
best := -1
for left < right {
sum := nums[left] + nums[right]
if sum < k {
// 当前左值的最佳剩余配对已经检查,可排除左端。
if sum > best {
best = sum
}
left++
} else {
// 最小左值也超界,该右端无法与剩余值合法配对。
right--
}
}
return best
}
复杂度分析
- 时间复杂度:$O(n\log(n+1))$。
- 空间复杂度:扫描 $O(1)$,另计标准库排序空间;会重排输入。
关键点总结
[!green]
- 不合法时排除最大右端,合法时先记录该左端的最佳剩余配对,再排除左端。
- 严格小于不包含等于,等于阈值时也要继续减小和。
- 正数值域保证合法结果大于
-1,初值不会压过真正的可行答案。- 两个下标不同即可,数值相等仍可以配对。
易错点总结
[!yellow]
- 合法和不一定逐轮增大,不能直接覆盖历史答案,应取最大值。
- 和已经超界时增加左值,只会进一步增大和,不能修复当前右端。
- 条件写成
left <= right会让同一下标被使用两次。- 接受
sum == k会违反严格小于要求。best = -1依赖本题正数范围,不能不加说明地当作任意整数版本的最大值初始条件。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 167. 两数之和 II - 输入有序数组 | 中等 | 同样排序后双指针,本题在和小于k时更新最优并继续增大,而非找到精确和即结束。 |
| 259. 较小的三数之和 | 中等 | 把两数推广到三数,原题累计所有小于阈值的组合,本题只返回最大的合格和。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!