欢迎来到我的:世界
希望作者的文章对你有所帮助,有不足的地方还请指正,大家一起学习交流 !
前言
内容
第一题:有效三角形的个数
思路:排序+双指针法
因为是非负整数的数组nums
,需要找出其中可以组成三角形的三元组的数量。
根据三角形的性质:对于三条边a、b、c
,要构成三角形,必须满足任意两边之和大于第三边,即a + b > c
、a + c > b
且b + c > a
。当数组是有序的,设三边为nums[i]
、nums[j]
、nums[k](i < j < k)
,只要满足nums[i] + nums[j] > nums[k]
,就可以保证其他两个条件也满足。整体思路是:先对数组进行排序,然后从大到小遍历数组,对于每个元素
nums[i]
,使用双指针法在[0, i - 1]
区间内寻找满足三角形条件的组合。
class Solution {
public:
int triangleNumber(vector<int>& nums) {
// 对数组进行升序排序,方便后续使用双指针法判断三角形条件
// 排序后,若 nums[i] + nums[j] > nums[k](i < j < k),则可保证构成三角形
sort(nums.begin(),nums.end());
// 获取数组的最后一个元素的索引
int n = nums.size() - 1;
// 用于记录可以构成三角形的三元组的数量,初始化为 0
int count = 0;
// 从数组的最后一个元素开始向前遍历,直到索引为 2 的元素
// 因为构成三角形至少需要三个元素,所以 i 最小为 2
for(int i = n; i >= 2; i--) {
// 初始化双指针,begin 指向区间 [0, i - 1] 的起始位置
int begin = 0;
// end 指向区间 [0, i - 1] 的末尾位置
int end = i - 1;
// 双指针遍历区间 [0, i - 1],只要 begin 小于 end 就继续循环
while(begin < end) {
// 判断是否满足三角形条件:nums[begin] + nums[end] > nums[i]
if(nums[begin] + nums[end] > nums[i]) {
// 若满足条件,说明对于当前的 end,从 begin 到 end - 1 的所有元素
// 与 nums[end] 和 nums[i] 都可以构成三角形
// 所以可以构成的三角形数量为 end - begin,累加到 count 中
count += (end - begin);
// 将 end 指针向左移动一位,继续寻找其他可能的组合
end--;
} else {
// 若不满足条件,说明当前的 begin 对应的元素太小
// 将 begin 指针向右移动一位,尝试更大的元素
begin++;
}
}
}
// 遍历完数组后,count 中记录的就是可以构成三角形的三元组的数量,将其返回
return count;
}
};
复杂度分析
时间复杂度:排序的时间复杂度为 O(nlongn)
,双指针遍历的时间复杂度为O(n^2)
,因此总的时间复杂度为 。
空间复杂度:排序的空间复杂度取决于具体的排序算法,一般为 O(longn)
,因此总的空间复杂度为 O(longn)
。
第二题:三数之和
解题思路:排序+双指针+set去重 即给定一个包含若干整数的数组 nums,找出数组中所有和为 0 且不重复的三元组
[nums[i], nums[j], nums[k]]
,其中i != j
、i != k
且j != k
。
题目要求不能出现重复的三元组,
但是如何解决重复的三元组,可以利用
去重算法
或者利用set去重
这里的两种思路,利用set去重
,如果你深刻理解set容器的基本方法,应该都可以想到这种方法;
整体的解题思路是先对数组进行排序,这样能方便后续使用双指针法来查找符合条件的三元组,同时利用 set 数据结构自动去重的特性,避免结果中出现重复的三元组,最后将 set 中的元素转移到 vector 中返回。
#include <vector>
#include <algorithm>
#include <set>
class Solution {
public:
// 该函数用于找出数组中所有和为 0 且不重复的三元组
std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
// 定义一个 set 容器 s1,用于存储满足三数之和为 0 的三元组
// set 会自动对元素进行排序,并且保证元素的唯一性,可避免结果中出现重复的三元组
std::set<std::vector<int>> s1;
// 定义一个二维 vector v1,用于存储最终要返回的结果
std::vector<std::vector<int>> v1;
// 对输入的数组 nums 进行升序排序
// 排序后方便后续使用双指针法,也便于发现重复元素
std::sort(nums.begin(), nums.end());
// 外层循环,从数组的最后一个元素开始向前遍历,直到索引为 2 的元素
// 因为要构成一个三元组,至少需要三个元素,所以 i 的下限是 2
for (int i = nums.size() - 1; i >= 2; i--) {
// 如果当前元素 nums[i] 小于 0,由于数组已升序排序,其前面元素也都小于 0
// 三个负数相加不可能等于 0,所以直接跳出循环,减少不必要的计算
if (nums[i] < 0) break;
// 初始化双指针 left 和 right
// left 指向区间 [0, i - 1] 的起始位置
int left = 0;
// right 指向区间 [0, i - 1] 的末尾位置
int right = i - 1;
// 双指针遍历,在 [0, i - 1] 区间内寻找另外两个数,使三数之和为 0
while (left < right) {
// 计算三个数的和
int sum = nums[left] + nums[right] + nums[i];
// 如果和为 0,说明找到了一个满足条件的三元组
if (sum == 0) {
// 将该三元组插入到 set 中
s1.insert({nums[left], nums[right], nums[i]});
// 左指针右移一位
left++;
// 右指针左移一位
right--;
}
// 如果和大于 0,说明当前的和太大
// 由于数组升序排序,将 right 指针左移,减小和的值
else if (sum > 0) {
right--;
}
// 如果和小于 0,说明当前的和太小
// 将 left 指针右移,增大和的值
else {
left++;
}
}
}
// 遍历 set 中的所有元素
for (auto s : s1) {
// 将每个三元组依次添加到 vector v1 中
v1.push_back(s);
}
// 返回包含所有满足条件且不重复的三元组的 vector
return v1;
}
};
如果使用去重算法:在寻找过程中,通过跳过重复元素来避免结果中出现重复的三元组。就可以不用使用set容器来实现去重了;
#include <vector>
#include <algorithm>
class Solution {
public:
// 该函数用于找出数组中所有和为 0 且不重复的三元组
std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
// 定义一个二维向量 v1,用于存储最终找到的所有满足条件的三元组
std::vector<std::vector<int>> v1;
// 对数组 nums 进行升序排序,方便后续使用双指针法以及跳过重复元素
std::sort(nums.begin(), nums.end());
// 外层循环,从数组的最后一个元素开始向前遍历,直到索引为 2 的元素
// 因为要构成一个三元组,至少需要三个元素,所以 i 的下限是 2
for (int i = nums.size() - 1; i >= 2; ) {
// 如果当前元素 nums[i] 小于 0,由于数组已升序排序,其前面元素也都小于 0
// 三个负数相加不可能等于 0,所以直接跳出循环,减少不必要的计算
if (nums[i] < 0) {
break;
}
// 初始化双指针,left 指向数组起始位置
int left = 0;
// right 指向 nums[i] 前面一个元素的位置
int right = i - 1;
// 双指针遍历,在 [0, i - 1] 区间内寻找另外两个数,使三数之和为 0
while (left < right) {
// 计算当前三个数的和
int sum = nums[left] + nums[right] + nums[i];
// 如果和为 0,说明找到了一个满足条件的三元组
if (sum == 0) {
// 将该三元组添加到结果容器 v1 中
v1.push_back({nums[left], nums[right], nums[i]});
// 左指针右移一位
left++;
// 右指针左移一位
right--;
// 跳过与当前 left 指向元素相同的元素,避免产生重复的三元组
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
// 跳过与当前 right 指向元素相同的元素,避免产生重复的三元组
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
}
// 如果和大于 0,说明当前的和太大
// 由于数组升序排序,将 right 指针左移,减小和的值
else if (sum > 0) {
right--;
}
// 如果和小于 0,说明当前的和太小
// 将 left 指针右移,增大和的值
else {
left++;
}
}
// 外层循环指针左移一位
i--;
// 跳过与当前 nums[i] 相同的元素,避免产生重复的三元组
while (i >= 2 && nums[i] == nums[i + 1]) {
i--;
}
}
// 返回存储所有满足条件且不重复的三元组的二维向量
return v1;
}
};
第三题:四数之和
解题思路:排序+双指针+set去重:这题和上一题非常相似,几乎是一模一样的解题思路,就是在外面套了一层循环即可:通过两层循环固定两个数,再使用双指针法在剩余元素中寻找另外两个数,使得四个数的和等于目标值 target。
在这里我使用set 去重法,另一种方法就不做介绍了,有兴趣的小伙伴可以自己试试;
#include <vector>
#include <algorithm>
#include <set>
class Solution {
public:
// 该函数用于找出数组中所有和为 target 的不重复四元组
std::vector<std::vector<int>> fourSum(std::vector<int>& nums, int target) {
// 定义一个 set 容器 ss,用于存储满足四数之和为 target 的四元组
// set 会自动对元素进行排序,并且保证元素的唯一性,可避免结果中出现重复的四元组
std::set<std::vector<int>> ss;
// 定义一个二维 vector vv,用于存储最终要返回的结果
std::vector<std::vector<int>> vv;
// 对输入的数组 nums 进行升序排序
// 排序后方便后续使用双指针法,也便于发现重复元素
std::sort(nums.begin(), nums.end());
// 获取数组的长度
int n = nums.size();
// 外层循环,固定第一个数 nums[i]
// 从数组的最后一个元素开始向前遍历,直到索引为 3 的元素
// 因为要构成一个四元组,至少需要四个元素,所以 i 的下限是 3
for (int i = n - 1; i >= 3; i--) {
// 中层循环,固定第二个数 nums[j]
// 从 nums[i] 的前一个元素开始向前遍历,直到索引为 2 的元素
// 同样,要构成四元组,此时剩余元素至少要有两个,所以 j 的下限是 2
for (int j = i - 1; j >= 2; j--) {
// 初始化双指针,left 指向数组起始位置
int left = 0;
// right 指向 nums[j] 前面一个元素的位置
int right = j - 1;
// 计算剩余两个数需要满足的和,为了避免整数溢出,使用 long long 类型
long long two = (long long)target - nums[i] - nums[j];
// 双指针遍历,在 [0, j - 1] 区间内寻找另外两个数,使四数之和为 target
while (left < right) {
// 计算当前两个数的和
int sum = nums[left] + nums[right];
// 如果和等于 two,说明找到了一个满足条件的四元组
if (sum == two) {
// 将该四元组插入到 set 中
ss.insert({nums[left], nums[right], nums[j], nums[i]});
// 左指针右移一位
left++;
// 右指针左移一位
right--;
}
// 如果和大于 two,说明当前的和太大
// 由于数组升序排序,将 right 指针左移,减小和的值
else if (sum > two) {
right--;
}
// 如果和小于 two,说明当前的和太小
// 将 left 指针右移,增大和的值
else {
left++;
}
}
}
}
// 遍历 set 中的所有元素
for (auto s : ss) {
// 将每个四元组依次添加到 vector vv 中
vv.push_back(s);
}
// 返回包含所有满足条件且不重复的四元组的 vector
return vv;
}
};
总结
到了最后:感谢支持
------------对过程全力以赴,对结果淡然处之