这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
611. 有效三角形的个数
题目
给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。
示例 1:
输入: [2,2,3,4]
输出: 3
解释:
有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3
注意:
- 数组长度不超过1000。
- 数组里整数的范围为 [0, 1000]。
方法一
排序+二分:可以组成三角形的条件是,任意两边之和大于第三边,我们可以分别枚举三个点,然后判断是否满足组成三角形的条件,但是这样的事件复杂度就是O(n^3),会超时。所以,可以稍微的优化一下;
- 枚举三个点,我们变成枚举两个点,第三个通过二分来确定(这样时间复杂度就变成了O(n^2logn),使用二分的条件是数组单调,所以得先排序;
- 枚举第一二个点,在对后面的元素进行二分,找出最大的满足组成三角形条件的点,更新答案;
class Solution {
public int triangleNumber(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
int res = 0;
for (int i = 0; i < n - 2; i ++ )
for (int j = i + 1; j < n - 1; j ++ )
{
int l = j + 1, r = n - 1;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (nums[i] + nums[j] > nums[mid]) l = mid;
else r = mid - 1;
}
if (l == j + 1) {
if (nums[i] + nums[j] > nums[l])
res ++;
}
else res += l - j;
}
return res;
}
}
时间复杂度: O(n^2logn)
空间复杂度: O(logn)
方法二
双指针:我们可以发现当三个元素中的第二个元素往后移的过程中,第三个元素也是跟着往后移的,所以可以用双指针;
- 枚举第一个元素的位置
- 定义两个指针,
j和k - 当
nums[i] +nums[j] > nums[k]时,k往后移; - 当
nums[i] +nums[j] <= nums[k]时,计算k和j之前的元素,更新答案,并且j往后移;
class Solution {
public int triangleNumber(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
int res = 0;
for (int i = 0; i < n; i ++ ) {
int k = i;
for (int j = i + 1; j < n; j ++ ) {
while(k + 1 < n && nums[k + 1] < nums[i] + nums[j]) k ++;
res += Math.max(k - j, 0);
}
}
return res;
}
}
时间复杂度: O(n^2)
空间复杂度: O(1)