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].
public int triangleNumber(int[] nums) {
int[] map=new int[1001];
for(int i=0;i<nums.length;i++){
map[nums[i]]++;
}
int count=0;
for(int i=0;i<nums.length-2;i++){
map[nums[i]]--;
for(int j=i+1;j<nums.length-1;j++){
map[nums[j]]--;
//只找j后面的数
int one=nums[i];
int two=nums[j];
int min=Math.abs(one-two)+1;
int max=one+two-1;
for(int k=min;k<=max;k++){
count+=map[k];
}
}
//又加回去
for(int j=i+1;j<nums.length-1;j++){
map[nums[j]]++;
}
}
return count;
}
我发现大神的解法第一步都是:先排序。
假设 a
是最长边,b
and c
是较短的两边,要形成三角形,需要满足 len(b) + len(c) > len(a)
.
思路是排序之后,从数字末尾开始往前遍历,将left指向首数字,将right指向之前遍历到的数字的前面一个数字,然后如果left小于right就进行循环,循环里面判断如果left指向的数加上right指向的数大于当前的数字的话,那么right到left之间的数字都可以组成三角形,这是为啥呢,相当于此时确定了i和right的位置,可以将left向右移到right的位置,中间经过的数都大于left指向的数,所以都能组成三角形。加完之后,right自减一。如果left和right指向的数字之和不大于nums[i],那么left自增1,参见代码如下:
public class Solution {
public int triangleNumber(int[] nums) {
int result = 0;
if (nums.length < 3) return result;
Arrays.sort(nums);
for (int i = 2; i < nums.length; i++) {//令nums[i]为最长边
int left = 0, right = i - 1;//nums[right]为第二长边,nums[left]是最短边
while (left < right) {
if (nums[left] + nums[right] > nums[i]) {//当前已经能够满足短+短>长
result += (right - left);//那么[left,right)范围内的数作为最短边仍然能满足
right--;//改变第二长边
}
else {
left++;//当前不能满足短+短>长,增加最短边
}
}
}
return result;
}
}
这道题有solutions:
https://leetcode.com/problems/valid-triangle-number/solution/。
Approach #2 Using Binary Search [Accepted]
Algorithm
令 i<j<k,那么要使得nums[k]>nums[i]+nums[j],索引 k 有一个右边的临界点。任何索引只要>=该临界点,就肯定不会满足这个不等式。
如果我们找到了这个临界点,设为 k,我们就可以保证在 索引范围内的数能够满足上述不等式。在这范围内的数字都能够作为 ( i , j ) pair 的第三边,数量是 .
因为 数组已经被排序了,我们可以使用 Binary Search 来找到 k 的右临界点。
Java
public class Solution { int binarySearch(int nums[], int l, int r, int x) { while (r >= l && r < nums.length) { int mid = (l + r) / 2; if (nums[mid] >= x) r = mid - 1; else l = mid + 1; } return l; } public int triangleNumber(int[] nums) { int count = 0; Arrays.sort(nums); for (int i = 0; i < nums.length - 2; i++) { int k = i + 2; for (int j = i + 1; j < nums.length - 1 && nums[i] != 0; j++) { k = binarySearch(nums, k, nums.length - 1, nums[i] + nums[j]); count += k - j - 1; } } return count; } }
Complexity Analysis
-
Time complexity : . In worst case inner loop will take (binary search applied times).
-
Space complexity : . Sorting takes space.
Approach #3 Linear Scan [Accepted]:
Algorithm
我们需要找到索引对 (i,j)的右临界值 ,使得nums[i]+nums[j]>nums[k] 能够满足。
为了找到 k 的右临界值,我们可以从 开始遍历,直到找到第一个 不满足上述不等式。那么在 [ j+1 ,k ) 范围内的元素一定会满足上述不等式,数量是 .
Java
public class Solution { public int triangleNumber(int[] nums) { int count = 0; Arrays.sort(nums); for (int i = 0; i < nums.length - 2; i++) { int k = i + 2; for (int j = i + 1; j < nums.length - 1 ; j++) { while (k < nums.length && nums[i] + nums[j] > nums[k]) k++; //k最终值是:使得nums[i] + nums[j]<=nums[k]的最左索引 count += k - j - 1; } } return count; } }
Complexity Analysis
-
Time complexity : . Loop of and will be executed times in total, because, we do not reinitialize the value of for a new value of chosen (for the same ). Thus the complexity will be O(n^2+n^2)=O(n^2).
-
Space complexity : . Sorting takes O(logn) space.