611. 有效三角形的个数
给定一个包含非负整数的数组
nums
,返回其中可以组成三角形三条边的三元组个数。示例 1:
输入: nums = [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 2) 2,2,3示例 2:
输入: nums = [4,2,3,4] 输出: 4
一、暴力枚举
class Solution {
public:
bool IsTriangle(int a, int b, int c)
{
if(a+b>c && a+c>b && b+c>a) // 3、应该只需要判断俩边之和大于第三边
{
vector<int> arr = {a,b,c};
sort(arr.begin(), arr.end()); // 出错点2、if判断里面下标写错
if(arr[2]-arr[1]<arr[0] && arr[1]-arr[0]<arr[2] && arr[2]-arr[0]<arr[1])
return true;
}
return false;
}
int triangleNumber(vector<int>& nums) { // 三个for循环暴力枚举
int ret = 0;
for(int i = 0; i < nums.size(); i++)
{
for(int j = i+1; j<nums.size(); j++)
{
for(int k = j+1; k<nums.size(); k++)
{
if(IsTriangle(nums[i], nums[j], nums[k])) // 出错点1、直接把ijk传参
ret++;
}
}
}
return ret;
}
};
二、暴力枚举的改进
应该只需要判断俩边之和大于第三边,所以可以对俩边之差小于第三边进行优化掉
class Solution {
public:
bool IsTriangle(int a, int b, int c)
{
if(a+b>c && a+c>b && b+c>a) // 3、应该只需要判断俩边之和大于第三边
{
// vector<int> arr = {a,b,c};
// sort(arr.begin(), arr.end()); // 出错点2、if判断里面下标写错
// if(arr[2]-arr[1]<arr[0] && arr[1]-arr[0]<arr[2] && arr[2]-arr[0]<arr[1])
return true;
}
return false;
}
int triangleNumber(vector<int>& nums) { // 三个for循环暴力枚举
int ret = 0;
for(int i = 0; i < nums.size(); i++)
{
for(int j = i+1; j<nums.size(); j++)
{
for(int k = j+1; k<nums.size(); k++)
{
if(IsTriangle(nums[i], nums[j], nums[k])) // 出错点1、直接把ijk传参
ret++;
}
}
}
return ret;
}
};
三、算法的进一步优化
对于给定的三边长的三角形,可以将其边长进行排序,排序后较小的俩个边之和如果小于第三边,则就可以组成三角形。
那我们选择先对数组进行排序,然后从右边最大数开始,将其确定为c,那么问题就简化为在c之前寻找俩个数能够加和大于c。
显而易见了:1、固定位置c
2、然后对撞双指针在c的左区间来确定a、b位置。
再此基础上,我们发现如果下图这样的a、b位置加和肯定大于c,那么如果固定right,left左移根据单调性,他们的和一定会更大,所以我们不需要再进行从里面选,直接可以确定right-left种元组个数。
当c确定下来之后,左右指针指向的元素萨之和只有俩中情况,要么大于c,要么小于c
当和大于c的时候,a之后的元素和b加起来均大于c(根据排序的单调性)
那么就有right-left种元祖满足关系,继而向左移动right
当和小于c的时候,需要移动left位置,让a变大才有可能满足a+b>c
class Solution {
public:
int triangleNumber(vector<int>& nums) {
// if(nums.size() < 3) return 0;
int res = 0, n = nums.size();
sort(nums.begin(), nums.end());
for(int i = n-1; i > 1; i--)// 先固定最大的数c,从后往前
{
int left = 0, right = i-1; // 再固定最大的数之后,从它的左区间左右指针对撞确定另外俩个数a、b
while(left < right)
{
if(nums[left]+nums[right] > nums[i])
{
res += (right-left);
right--;
}
else
left++;
}
}
return res;
}
};