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],具体如下:

  1. 初始化左右指针 i=0, j=k−1。
  2. 如果 nums[i]+nums[j]>c,由于数组是有序的,nums[j] 与下标 i' 在 [i,j−1] 中的任何 nums[i'] 相加,都是 >c 的,因此直接找到了 j−i 个合法方案,加到答案中,然后将 j 减一。
  3. 如果 nums[i]+nums[j]≤c,由于数组是有序的,nums[i] 与下标 j' 在 [i+1,j] 中的任何 nums[j'] 相加,都是 ≤c 的,因此后面无需考虑 nums[i],将 i 加一。
  4. 重复上述过程直到 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)

答案

javascript 复制代码
/**
 * @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)。不计入排序的栈开销,仅用到若干额外变量。