611. 有效三角形的个数
题意:
给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。
示例 1:
输入: [2,2,3,4]
输出: 3
解释:
有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3
解题思路:
我的想法是这样的,用双指针(定二动一),左边指向第一个节点,右边指向最后一个节点,然后一个滑标 从右边出发,知道左边加上滑标的值小于最后的大的.加上下标的差,然后左边+1,重复上面循环直到左边和右边相差1.
问 答 构思 用双指针,左边指向第一个节点,右边指向最后一个节点.然后定义一个滑标 什么时候结束滑动? 左指针的值加上滑动的值小于右指针的值(两边之和大于第三边),就可以获得下标差. 这样一个个算的时间复杂度太大了,有两层循环还要一个个比较? 因为只需要找到一个值使得两边之和小于第三边即可,所以可以用二分查询第一个小于等于的值即可, 怎么查找第一个小于等于的值? 可以自己写一个函数判断,也可以直接用lower_bound()函数 细节 **注意寻找值时左边是从左指针+1开始的,不能一条边取两次 ; 因为函数找的是大于等于,而两边之和大于第三边,那么就需要边差值 +1 **
- 到这里基本就可完成问题,遍历全部数组可以组的双指针,然后找第一个大于等于右边-左边的值,然后加上下标差即可
代码:
class Solution {
public:
int triangleNumber(vector<int>& nums) {
//如果边数小于3条,不可能组成
if(nums.size() < 3)
return 0;
//先排序
sort(nums.begin(),nums.end());
int res = 0;
//获得最左边的值
vector<int>::iterator left = nums.begin();
vector<int>::iterator right = --nums.end();
//第一层循环,左边到最后的边要大于3条,
while (left != nums.end() - 2)
{
//左边需要+1才是找的范围
vector<int>::iterator left_round = left + 1;
//第二次循环,找每一个值
while (right - left > 1)
{
//找第一个大于等于的值,但是两边之和要大于第三步,所以需要 +1
auto it = lower_bound(left_round, right, *right - *left + 1);
//下标差就是个数
res += right - it;
right--;
}
//外层循环继续
left++;
right = --nums.end();
}
return res;
}
};
第二种
第一种的时间复杂度太高,为O(n2 log2n) ,那么需不需要更快的呢.这时候就想到另外的办法,
解题思路
还是利用双指针,但是定一滑二.
问 答 定一 遍历数组,从最后开始到第三个数值结束 滑二 一个从开始位置,一个从定一的上一位开始 怎么获得结果? 如果滑二的两个值之和大于定义,就获取它的下标差.如果不是就让左边+1(这样才能让边更大). 直到相加大于定一为止
- 大体思路就完成了,每个值只需要比较滑二即可.代码如下
class Solution {
public:
int triangleNumber(vector<int>& nums) {
//个数小于3个,直接返回
if(nums.size() < 3)
return 0;
//先排序
sort(nums.begin(), nums.end());
//最后一个值
int end = nums.size() - 1;
int res = 0;
//遍历全部数组,定一
for (int right = end; right >= 2; --right)
{
//滑二
int i = 0;
int j = right - 1;
while (i < j)
{
//如果两边之和大于第三边,直接加下标差
if (nums[i] + nums[j] > nums[right])
{
res += j - i;
j--;
}
//如果小于就缩小范围
else
i++;
}
}
return res;
}
};
总结:
建议使用第二种,时间复杂度最差的情况为O(n2),但是基本达不到这种最坏情况.所以时间快第一种很多.