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
提示:
1 <= nums.length <= 1000
0 <= nums[i] <= 1000
首先要排序。
对于任意三角形,其三边 a
, b
, c
必须满足: a+b>c、a+c>b、b+c>a
对数组进行排序后,有 a≤b≤c,所以只需要验证 a+b>c 即可。
排序+二分
先排序,再固定前两条边,二分查找满足条件的第三条边的右边界。
例如[4,2,3,6,4],排序后变成[2,3,4,4,6]。我们先固定前两个数2,3,在后面的4,4,6中查找满足条件的数的右边界,查找到的右边界是第4个位置,因此从第3个位置到第4个位置的数都满足条件,个数为3-2+1=2。
class Solution {
/**
* 计算能够组成三角形的三条边的数量
*
* @param nums 包含边长的整数数组
* @return 能够组成三角形的三条边的数量
*/
public int triangleNumber(int[] nums) {
int n = nums.length; // 数组的长度
Arrays.sort(nums); // 对数组进行排序
int ans = 0; // 记录能够组成三角形的数量
// 枚举第一个边 nums[i]
for (int i = 0; i < n; ++i) {
// 枚举第二个边 nums[j]
for (int j = i + 1; j < n; ++j) {
int left = j + 1, right = n - 1, k = j; // 二分查找初始化
// 在 nums[j+1] 到 nums[n-1] 的范围内查找满足条件的第三个边 nums[k] 的 k 的右边界
while (left <= right) {
int mid = (left + right) / 2; // 中间位置
// 如果 nums[mid] 小于 nums[i] + nums[j],说明第三个边可以是 nums[mid]
if (nums[mid] < nums[i] + nums[j]) {
k = mid; // 更新 k
left = mid + 1; // 继续查找右半部分
} else {
right = mid - 1; // 继续查找左半部分
}
}
ans += k - j; // 计算满足条件的第三个边的数量
}
}
return ans; // 返回结果
}
}
- 时间复杂度: O ( n 2 log n ) O\left(n^2 \log n\right) O(n2logn) ,其中 n n n 是数组 n u m s nums nums 的长度。我们需要 O ( n log n ) O(n \log n) O(nlogn) 的时间对数组 nums 进行排序,随后需要 O ( n 2 log n ) O\left(n^2 \log n\right) O(n2logn) 的时间使用二重循环枚举 a , b a, b a,b 的下标以及使用二分查找得到 c c c 的下标范围。
- 空间复杂度: O ( log n ) O(\log n) O(logn) ,即为排序需要的栈空间。
排序 + 双指针
先排序,然后固定最大边,双指针查找较短的两条边。
因为数组是升序排序的,且我们想找到满足 a+b>c 的a、b、c,因此需要固定最大值c,然后用双指针查找a和b。
例如[4,2,3,6,4]
,排序后变成[2,3,4,4,6]
。我们先固定最大值6,在前面的2,3,4,4
中查找满足条件的数,查找需要用两个指针相向移动,一开始left
指向2,right
指向第二个4,因为2+4=6
,所以{2,4}
不满足条件,于是left++
,left指向3,3+4>6,满足条件,所以记录个数3-1+1=3(3和1都是索引)
class Solution {
public int triangleNumber(int[] nums) {
// 对数组进行排序,便于后续使用双指针进行操作
Arrays.sort(nums);
int n = nums.length; // 数组的长度
int res = 0; // 用于记录能够组成三角形的三元组数量
// 从后往前遍历数组,固定一个元素 nums[i] 作为三角形的最长边
for (int i = n - 1; i >= 2; --i) {
int l = 0; // 左指针,从数组开头开始
int r = i - 1; // 右指针,从当前元素的前一个位置开始
// 使用双指针法寻找满足条件的三角形
while (l < r) {
// 如果左指针和右指针所指的元素之和大于固定的 nums[i],
// 说明 nums[l] 到 nums[r] 之间的所有元素都能与 nums[r] 组成三角形
if (nums[l] + nums[r] > nums[i]) {
// 将满足条件的组合数累加到结果中,这些组合是{l,r},...,{l,l+1}
res += r - l;
// 右指针左移,尝试下一个较小的边
--r;
} else {
// 如果条件不满足,则需要更大的左边,左指针右移
++l;
}
}
}
// 返回最终能够组成三角形的三元组数量
return res;
}
}
- 时间复杂度为 O(n^2)。
- 空间复杂度O(log n)。