Medium
题目描述
给定一个包含非负整数的数组 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 <= 10000 <= nums[i] <= 1000
解题思路
解题思路
判断三个数能否组成三角形的关键在于:任意两边之和大于第三边。对于三条边 a、b、c,如果 a ≤ b ≤ c,那么只需要验证 a + b > c 即可(因为其他两个条件自动满足)。
方法一:排序 + 双指针(推荐)
- 排序:首先对数组排序,便于后续处理
- 固定最大边:从右往左遍历,将当前元素作为最大边 c
- 双指针查找:对于固定的最大边 c,使用双指针在其左侧寻找满足条件的 a 和 b
- 左指针 left = 0,右指针 right = i - 1
- 如果 nums[left] + nums[right] > nums[i],说明 [left, right-1] 范围内的所有元素都可以与 nums[right] 组成有效三角形,答案增加 (right - left),然后 right–
- 否则说明 nums[left] 太小,left++
这种方法的优势在于:当 nums[left] + nums[right] > nums[i] 时,nums[left+1]、nums[left+2]…nums[right-1] 与 nums[right] 的组合都满足条件,可以一次性计算多个结果。
方法二:暴力枚举
三重循环枚举所有可能的三元组,时间复杂度 O(n³),适用于数据量较小的情况。
方法三:排序 + 二分查找
固定两条边,使用二分查找第三条边的范围,时间复杂度 O(n² log n)。
代码实现
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());
int count = 0;
int n = nums.size();
for (int i = n - 1; i >= 2; i--) {
int left = 0, right = i - 1;
while (left < right) {
if (nums[left] + nums[right] > nums[i]) {
count += right - left;
right--;
} else {
left++;
}
}
}
return count;
}
};
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
nums.sort()
count = 0
n = len(nums)
for i in range(n - 1, 1, -1):
left, right = 0, i - 1
while left < right:
if nums[left] + nums[right] > nums[i]:
count += right - left
right -= 1
else:
left += 1
return count
public class Solution {
public int TriangleNumber(int[] nums) {
Array.Sort(nums);
int count = 0;
int n = nums.Length;
for (int i = n - 1; i >= 2; i--) {
int left = 0, right = i - 1;
while (left < right) {
if (nums[left] + nums[right] > nums[i]) {
count += right - left;
right--;
} else {
left++;
}
}
}
return count;
}
}
var triangleNumber = function(nums) {
nums.sort((a, b) => a - b);
let count = 0;
const n = nums.length;
for (let i = n - 1; i >= 2; i--) {
let left = 0, right = i - 1;
while (left < right) {
if (nums[left] + nums[right] > nums[i]) {
count += right - left;
right--;
} else {
left++;
}
}
}
return count;
};
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 排序 + 双指针 | O(n²) | O(1) |
| 暴力枚举 | O(n³) | O(1) |
| 排序 + 二分查找 | O(n² log n) | O(1) |
推荐使用排序 + 双指针的方法,时间复杂度最优为 O(n²)。
相关题目
. 3Sum Smaller (Medium)