持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第27天,点击查看活动详情
1、写在前面
大家好,我是翼同学,这里是【水滴计划 | 刷题日志】
每日两题,拒绝摆烂。
2、内容
2.1、题目一:三数之和
(1) 描述
(2) 举例
(3) 解题
哈希法求解
// 设 a = nums[i], b = nums[j], 本题需寻找是否有一个元素 c 使得 a+b+c=0,此时 c = -(a+b)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
// 定义一个二维vector容器用于存放结果
vector<vector<int> > rs;
// 对数组nums进行排序
sort(nums.begin(), nums.end());
// 遍历数组 nums 取到 a
for (int i = 0; i < nums.size(); i++) {
// 由于数组已经排好序,此时如果数组首元素已经大于零,则不存在三元组使得相加等于零
if (nums[i] > 0) {
break;
}
// 去重 a 操作
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// 定义一个unordered_set容器record 用于寻找容器里是否有 -(a+b)
unordered_set<int> record;
// 再次遍历数组,取到 b
for (int j = i + 1; j < nums.size(); j++) {
// 去重 b 操作
if (j>i+2 && nums[j] == nums[j-1] && nums[j-1] == nums[j-2]) {
continue;
}
// 取值 c
int c = 0 - (nums[i] + nums[j]);
// 如果在容器record中寻找到 c,则插入该三元组 (a, b, c) 到容器 rs 中
if (record.find(c) != record.end()) {
// 插入一个结果
rs.push_back({nums[i], nums[j], c});
// 去重 c 操作
record.erase(c);// 三元组元素c去重
}
// 如果在容器record中找不到的 c,则记录这个 b
else {
record.insert(nums[j]);
}
}
}
// 最后返回结果
return rs;
}
};
难点在于不能包含重复的三元组,因此需要去重操作。
双指针求解
在双指针解法中,利用一个for循环遍历一遍数组,循环变量i从下标0开始。此时再定义两个变量left和right,分别指向i+1和nums.size()-1。设a = nums[i],b = nums[left],c = nums[right],则题目要求就是寻找到a + b + c = 0,由于数组已经排好序,如果当前a + b + c < 0,则说明三数之和偏小,此时应该将left向后移动。这样三数之和才有变大的可能。如果当前a + b + c > 0,则说明三数之和偏大,此时应该将right向前移动。这样三数之和才有变小的可能。最后如果left和right相遇,则退出当前循环。
难点依旧是去重操作。需要注意的是,我们去重后的结果是没有重复的三元组,但三元组内的元素是可以重复的!
参考代码如下:
// 设定 a = nums[i], b = nums[left], c = nums[right],此时题目要求为找出 a + b + c = 0
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
// 定义一个二维容器用于存放运算结果
vector<vector<int> > rs;
// 将数组nums排好序
sort(nums.begin(), nums.end());
// 循环遍历 nums 数组
for (int i = 0; i < nums.size(); i++) {
// 由于数组已经排好序,如果元素i已经大于或等于零,则nums[i]和后续元素是不可能凑成三元组,因此返回运算结果即可
if (nums[i] > 0) return rs;
// 去重操作一,将 a 去重
if (i > 0 && nums[i] == nums[i - 1]) continue;
// 定义两个指针(其实就是表示数组下标)
int left = i + 1;
int right = nums.size() - 1;
// 当两个指针相遇则退出循环
while (right > left) {
// 当 a + b + c > 0 则应将 right 向左边移动,三数之和才有变小的可能
if (nums[i] + nums[left] + nums[right] > 0) {
right--;
}
// 当 a + b + c < 0 则应将 left 向右边移动,三数之和才有变大的可能
else if (nums[i] + nums[left] + nums[right] < 0) {
left++;
}
// 当 a + b + c = 0 则表示已找到三数之和,此时将结果插入到rs中并进行去重操作
else {
// 将三元组 (a, b, c) 插入到运算结果中
rs.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 去重操作二,将 b 去重
while (right > left && nums[right] == nums[right - 1]) {
right--;
}
// 去重操作二,将 c 去重
while (right > left && nums[left] == nums[left + 1]) {
left++;
}
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
// 最后返回运算结果
return rs;
}
};
2.2、题目二:四数之和
(1) 描述
(2) 举例
(3) 解题
解题代码如下:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
// 定义一个二维数组 rs 用于存放运算结果
vector<vector<int> > rs;
// 将数组排好序
sort(nums.begin(), nums.end());
// 第一层for遍历数组 nums
for (int i = 0; i < nums.size(); i++) {
// 遇到特殊情况,可直接退出循环
if (nums[i] > target && nums[i] >= 0) {
break;
}
// 去重操作,对 nums[k] 去重
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// 第二层for循环遍历
for (int j = i + 1; j < nums.size(); j++) {
// 遇到特殊情况,则退出循环
if (nums[i] + nums[j] > target && nums[i] + nums[j] >= 0) {
break;
}
// 去重操作,对 nums[i] 去重
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
// 定义两个指针,分别指向下一个元素和数组末尾元素
int left = j + 1;
int right = nums.size() - 1;
// 当 left > right 则退出循环
while (left < right) {
// 如果四数之和偏大,则让right向左移动
if ((long) nums[i] + nums[j] + nums[left] + nums[right] > target) {
right--;
}
// 如果四数之和偏小,则让left向右移动
else if ((long) nums[i] + nums[j] + nums[left] + nums[right] < target) {
left++;
}
// 最后找到了四数之和
else {
// 将四元组存入运算结果中
rs.push_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});
// 最后一步去重操作,对 nums[left] 和 nums[right] 进行去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return rs;
}
};
3、写在最后
好了,今天就刷到这里,明天再来。