今日题目:
- 15. 三数之和 | LeetCode
- 18. 四数之和 | LeetCode
- 454. 四数相加 II | LeetCode
- 383. 赎金信 | LeetCode
目录
- Problem 1:nSum 问题 【必会】
- LC 15. 三数之和 【classic, important】
- LC 18. 四数之和 【classic, important】
- Problem 2:Other
- LC 454. 四数相加 II 【还行】
- LC 383. 赎金信 【easy】
今天学习的重点在于 nSum 问题,即如何使用双指针法解决 2sum 问题,然后再基于 2sum 解决 3sum、4sum 问题。
Problem 1:nSum 问题 【必会】
这是 LeetCode 上一个经典的问题,包括 2sum、3sum 以及 4sum 等,解决这类问题的关键是:使用双指针法解决 2sum 问题,基于此,对于 3sum 问题来说,就是固定一个 number 然后转化为 2sum 问题;对于 4sum 问题来说,就是固定一个 number 然后转化为 3sum 问题。
nSum 问题的通用解法的讲解可以参考 一个函数秒杀 2Sum 3Sum 4Sum 问题 | labuladong 文章。
理解这个问题有两个关键点:
- 如何使用双指针法解决 2sum 问题?
- 如何基于 2sum 解决 3sum 问题?
这里有两个易错点(亲身经历):
- 一定要先对
nums
进行排序,才能使用双指针法来解
2. 指针滑动时,注意跳过重复数字,并且要保证跳跃正确,这里很容易因为一些细节导致小错误
跳过重复数字的逻辑可以写成:
while ( i+1 在合法范围内 && nums[i + 1] == nums[i] ) {
i++;
}
i++;
以上伪代码中,while 循环跳到了重复数字的最后一个,最后一行 i++
使其跳过了重复数字。注意这里的最后的 i++
别忘了。
LC 15. 三数之和 【classic, important】
15. 三数之和 | LeetCode
按照前面介绍的,我们需要实现一个 twoSumQuestion()
函数来解决 2sum 问题,然后基于此函数来进一步解决 3sum 问题。其中在解决 2sum 问题时,使用双指针来解决。
所以 Solution 代码的大致框架如下:
class Solution {
private List<List<Integer>> twoSumQuestion(int[] nums, int target, int start, int end) {
// 双指针法解决 2sum 问题
return results;
}
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums); // 对 nums 排序
// ************
// 基于 2sum 函数解决 3sum 问题
// ************
return results;
}
}
我的具体实现代码如下:
class Solution {
private List<List<Integer>> twoSumQuestion(int[] nums, int target, int start, int end) {
if (start >= end || start >= nums.length || end > nums.length) {
return null;
}
int left = start, right = end - 1;
List<List<Integer>> result = new ArrayList<>();
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
result.add(List.of(nums[left], nums[right]));
// left 跳过重复值
while (left + 1 < right && nums[left + 1] == nums[left]) {
left++;
}
left++;
// right 跳过重复值
while (right - 1 > left && nums[right - 1] == nums[right]) {
right--;
}
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
return result;
}
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> results = new ArrayList<>();
int i = 0;
while (i < nums.length - 2) {
int num = nums[i];
var twoSumAnswer = twoSumQuestion(nums, 0 - num, i + 1, nums.length);
twoSumAnswer.forEach(answer -> {
results.add(List.of(num, answer.get(0), answer.get(1)));
});
while (i + 1 < nums.length - 2 && nums[i + 1] == nums[i]) {
i++;
}
i++;
}
return results;
}
}
LC 18. 四数之和 【classic, important】
18. 四数之和 | LeetCode
类比上一个三数之和题目,这个解题思路也是类似,先使用双指针法实现 2sum 问题,基于 2sum 实现 3sum,然后再基于 3sum 实现 4sum 问题。
class Solution {
private List<List<Integer>> twoSumQuestion(int[] nums, long target, int start, int end) {
// 使用双指针法实现 2sum 问题
return results;
}
private List<List<Integer>> threeSumQuestion(int[] nums, long target, int start, int end) {
// 基于 2sum 实现 3sum
return results;
}
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums); // 对 nums 排序
// 基于 3sum 实现 4sum
return results;
}
}
框架是这样,但其中仍存在不少细节,所以还是要亲自多写写。
Problem 2:Other
LC 454. 四数相加 II 【还行】
454. 四数相加 II | LeetCode
这个就和前面的 nSum 不是同一个思路了,主要考察如何使用 HashMap 来降低算法的复杂度,整体略微具有难度。
LC 383. 赎金信 【easy】
题目很容易,直接跳过即可。