LeetCode 分类刷题: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 可以知道,对于三元组 (2,3,4) 和 (4,3,2),我们只统计了其中的 (2,3,4),并没有把 (4,3,2) 也统计到答案中,所以题目意思是把这两个三元组当成是同一个三元组,我们不能重复统计。既然有这样的规则,那么不妨规定三角形的三条边 a,b,c 满足:1 ≤ a ≤ b ≤c
这可以保证我们在统计合法三元组 (a,b,c) 的个数时,不会把 (c,b,a) 这样的三元组也统计进去。那么问题变成,从 nums 中选三个数,满足 1≤a≤b≤c 且 a+b>c 的方案数。
采用方法:枚举最长边 + 相向双指针
为了能够使用相向双指针,先对数组从小到大排序。 外层循环枚举最长边 c=nums[k],内层循环用相向双指针枚举 a=nums[i] 和 b=nums[j],具体如下:
- 初始化左右指针 i=0, j=k−1。
- 如果 nums[i]+nums[j]>c,由于数组是有序的,nums[j] 与下标 i' 在 [i,j−1] 中的任何 nums[i'] 相加,都是 >c 的,因此直接找到了 j−i 个合法方案,加到答案中,然后将 j 减一。
- 如果 nums[i]+nums[j]≤c,由于数组是有序的,nums[i] 与下标 j' 在 [i+1,j] 中的任何 nums[j'] 相加,都是 ≤c 的,因此后面无需考虑 nums[i],将 i 加一。
- 重复上述过程直到 i≥j 为止。
与 LeetCode Hot 100:15. 三数之和-CSDN博客 类似,
在设立相向双指针之前,有两个优化:
作者:灵茶山艾府
链接:https://leetcode.cn/problems/valid-triangle-number/solutions/2432875/zhuan-huan-cheng-abcyong-xiang-xiang-shu-1ex3/
来源:力扣(LeetCode)
答案
/*** @param {number[]} nums* @return {number}*/
var triangleNumber = function(nums) {nums.sort((a, b) => a - b);let ans = 0;for(let k = nums.length - 1; k > 1; k--) { //倒序枚举if(nums[0] + nums[1] > nums[k]) { //最小两数之和都大于最大数(边)ans += (k+1) * k * (k-1) / 6; //任选三条边都可以构成一个三角形break;}if(nums[k-2] + nums[k-1] <= nums[k]) continue; //次大两数之和都小于最大数let i = 0, j = k - 1;while(i < j) {if(nums[i] + nums[j] > nums[k]) { //从nums[i]到nums[j-1]都可以作为最短边ans += j - i;j--; //找新的第二短边} else {i++; //找新的最短边}}}return ans;
};
复杂度分析
时间复杂度:O(n^2),其中 n 为 nums 的长度。
空间复杂度:O(1)。不计入排序的栈开销,仅用到若干额外变量。