有效三角形个数
思路:
我们可以通过暴力枚举,三重for循环来算但,时间复杂度过高。
有没有效率更高的算法呢?
我们知道如果两条较短的边小于最长的一条边,那么就可以构成三角形。
如果这个数组是升序的,两条较大的边的值事先知道,最小的边从下标0开始找,不能构成三角形就向前移直到构成三角形,此时前面的元素都是大于等于它的 所以肯定也可以构成三角形。
顺着这个思路来写:
1.先对vector排升序
2.cur指向最大值,right指向cur-1,left指向下标0。如果left+right<=cur,那么构不成三角形,left++移向下一个值。
反之left+right>cur,left指向的元素以及left~right之间的元素都是可以构成三角形的,count+=right-left记录可以构成的元素个数,right--进入下一轮直到left==right。
3.内部循环结束后,可以和cur指向的元素构成三角形的情况统计结束,重新赋值left,right ,cur--直到指向下标为 2的位置。
代码实现:
class Solution {
public:
int triangleNumber(vector<int>& nums) {
int cur=nums.size()-1;
int count=0;
sort(nums.begin(),nums.end());
while(cur>=2)
{
int left=0,right=cur-1;
while(left<right)
{
if(nums[left]+nums[right]>nums[cur])
{
count+=right-left;
right--;
}
else left++;
}
cur--;
}
return count;
}
};
三数之和(medium)
https://leetcode.cn/problems/3sum/submissions/570394385/
我们做过两数和为x的题,那三数和为0,就是求两数和等于第三个数的相反数。
我们可以先固定一个数,再去找两数和等于固定数的相反数。
注意:题目中不可以输出相同的三元组
思路一:三层for循环+set去重
思路二:排序+双指针+去重
对数组进行sort排序,先固定第一个数下标为 i,再从后面区间[i+1,end]找和为nums[i]相反数的两个数。如果nums[i]>0即最小数都>0,三数和不可能==0,直接break
1.两数和大于它,左边++
2.小于,右边--
3.等于,记录后,左边++,右边--。如果左边是数和上一个数一样,那就会出现重复的情况,就需要再++,直到不同。注意不要越界。同理,右边也一样。
完成一轮循环后,i++,换到第二个数。如果第二个数和第一个数一样,也没必要进行查找,也要进行去重操作。直到不足3个数就不进行循环.
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> ret; //排序 sort(nums.begin(),nums.end()); //至少3个数才能进行循环 for(int i=0;i<nums.size()-2;) { if(nums[i]>0) break;//最小数为正数和不可能为0 int left=i+1,right=nums.size()-1;//查找区间 int target=-nums[i]; //在区间内找2数和为target的数 while(left<right) { if(nums[left]+nums[right]<target) left++; else if(nums[left]+nums[right]>target) right--; else { ret.push_back({nums[i],nums[left],nums[right]}); left++;right--; //去重 while(left<right&&nums[left]==nums[left-1]) left++; while(left<right&&nums[right]==nums[right+1]) right--; } } i++; //去重 while(i<nums.size()-2&&nums[i]==nums[i-1]) i++; } return ret; } };
四数之和(medium)
https://leetcode.cn/problems/4sum/submissions/570424616/
四数取和是在三数取和的基础上再固定一个数,也就是两层for循环,再用双指针。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ret;
sort(nums.begin(),nums.end());
for(int i=0;i<(int)nums.size()-3;)
{
for(int j=i+1;j<(int)nums.size()-2;)
{
int left=j+1,right=(int)nums.size()-1;
long long tar=(long long)target-nums[i]-nums[j];
while(left<right)
{
int sum=nums[left]+nums[right];
if(sum>tar) right--;
else if(sum<tar) left++;
else
{
ret.push_back({nums[i],nums[j],nums[left],nums[right]});
left++;right--;
//去重
while(left<right&&nums[left]==nums[left-1]) left++;
while(left<right&&nums[right]==nums[right+1]) right--;
}
}
j++;
while(j<(int)nums.size()-2&&nums[j]==nums[j-1]) j++;
}
i++;
while(i<(int)nums.size()-3&&nums[i]==nums[i-1]) i++;
}
return ret;
}
};
需要注意的是,计算tar时,数据可能会超出int范围,建议使用long long.
可以先减去两个固定的数再进行比较。