双指针——有效三角形的个数
一.题目描述
611. 有效三角形的个数 - 力扣(LeetCode)
二.题目解析
题目其实很简单就是让我们在数组中找到可能构成三角形的所有可能。构成三角形的前提是:任意两边之和大于第三边。所以我们要满足让下面三条同时成立才可以构成三角形:1、a+b>c,2、c+b>a,3、a+c>b。
但是我们可以对这个判断逻辑进行优化,我们可以先对三个数进行排序,a<=b<=c,这样我们只需要判断最小的两个数a+b>c即可。这样其实已经暗含了上面的三种情况了。
看着三次判断和一次判断好像不会对时间复杂度有太多的优化,但其实这个优化效果还是很大的。
三.算法原理
1、暴力解法
我们只需要枚举出所有可能的结果,然后依次判断是否可以构成三角形即可。
我们分析一下时间复杂度:三层循环,时间复杂度为O(N^3),再加上判断是否为三角形所以整体的时间复杂度为O(3*N^3)。
//伪代码
for(i=0; i<n; ++i)
for(j=i+1; j<n; ++j)
for(k=j+1; k<n; ++k)
check(nums[i],nums[j],nums[k]);
2.利用单调性+双指针
我们首先根据我们优化的逻辑先对数组进行排序,然后我们先选出一个最大的数c,然后用两个指针left、right来遍历剩下的数。我们在遍历的过程中会遇到两种情况:1.a+b>c; 2.a+b<=c.情况不同,我们的处理办法也不同。
当a+b>c时,left~right之间的任意一个数都可以与right以及c构成三角形,因为剩下的数都大于a。因此移动left是没有意义的,因为不管怎么移动都满足。我们只需要统计right和left之间的个数即可:right-left,然后right--。
当a+b<=c时,此时right移动没有意义,因为不管right怎么移动,剩下的值都比现在的小,所以更不可能构成三角形了,此时该区间内不会构成三角形,我们要让left++,在下一个区间内继续查找。
这就是我们这个算法的思路。
时间复杂度为:O(N*logN+N^2)。这个效率比三次方高很多。
空间复杂度为:O(1)。
四.代码实现
最大的那个数没有必要全部都遍历一遍,因为当i<2时,都没有三个数了,没必要判断。
int triangleNumber(vector<int>& nums)
{
sort(nums.begin(), nums.end());
int ret = 0;
int i = 0;
for (i = nums.size() - 1; i >= 2; --i)
{
int left = 0;
int right = i - 1;
while (left < right)
{
if (nums[left] + nums[right] > nums[i])
{
//可以构成三角形
ret += (right - left);
right--;
}
else
{
//构不成三角形
left++;
}
}
}
return ret;
}