Leetcode 611 Valid Triangle Number
题目原文
Given an array consists of non-negative integers, your task is to count the number of triplets chosen from the array that can make triangles if we take them as side lengths of a triangle.
Example 1:
Input: [2,2,3,4]
Output: 3
Explanation:
Valid combinations are:
2,3,4 (using the first 2)
2,3,4 (using the second 2)
2,2,3
Note:
- The length of the given array won't exceed 1000.
- The integers in the given array are in the range of [0, 1000].
题意分析
给一个数组,求该数组的子集,使得子集元素能构成三角形,求三角形个数。
解法分析
本题首先想到的是采用深度优先搜索(回溯)的方法,构造子集树,来得到所有满足条件的三元组,此时要注意,如果没有剪枝函数,只有上界函数,回溯法相当于暴力破解法,因为所有的可能的三元组合都被找到,只是判断了下是否能够成三角形,这种方法会超时。
为了加入剪枝操作,考虑三角形两边之和大于第三边的特性,如果对数据首先进行排序,则当两个较小的数(在子集树上部)之和小于第三个数时,它们之和一定小于更大的数(子集树下层),这样就避免了一些递归搜索操作。注意,剪枝造成的效果就是在pop之后,原本需要调用back_track()进入右子树,但如果剪枝操作认定右子树的所有数据都不能得到所求解时,就可以提前return,跳过函数调用(右子树递归调用完上一级函数也会return,所以剪枝函数的作用就是判断条件成立后提前return,减少调用次数)。C++代码如下:
class Solution {
private:
vector<int> temp;
vector<int> nums;
int count;
int n;
int sig;
public:
void back_track(int k) {
sig = 1;
if (temp.size() == 3) {
if ((temp[0] + temp[1])>temp[2]) {
count++;
return;
}
else {
sig = 0;
//temp.pop_back();
return;
}
}
if (k>n)
return;
temp.push_back(nums[k - 1]);
/*for (auto ww : temp)
cout << ww << ends;
cout << endl;*/
back_track(k + 1);
temp.pop_back();
if (sig == 0)
{
sig = 1;//remenber to be 1
return;
}
else
back_track(k + 1);
}
int triangleNumber(vector<int>& number) {
nums = number;
sort(nums.begin(), nums.end());
n = nums.size();
count = 0;
back_track(1);
return count;
}
};
无论如何,回溯法是一种近似遍历的方法,复杂度较高,本题有一种巧妙的思路,充分运用两边之和大于第三边的性质,并且,如果对数据进行排序后,如果两个较小数据之和大于较大数据,则一定能得到它们是三角形的边,因为两个较小边之差必然小于较大边,这两个条件结合在一起就能保证三角形的性质。
从最大数开始遍历,定义两个下标l和r,r=i-1,C++代码如下:
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());
int count = 0, n = nums.size();
for (int i = n-1; i >= 2; --i) {
int l = 0, r = i-1;
while (l < r) {
if (nums[l]+nums[r] > nums[i]) {
count += r - l;
-- r;
} else
++ l;
}
}
return count;
}
};
例如{2,2,3,4},当最大数为4时,较小边为2和3,且2+3>4,它们能构成三角形,由于第一个2右边的数一定大于等于它,则2和3中间的数与3,4一起一定能构成三角形,这也就是下面两句代码的含义:
if (nums[l]+nums[r] > nums[i]) {
count += r - l;
由于r和l是相向移动,复杂度为O(n),总的算法复杂度为O(n^2)。