611. 有效三角形的个数

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)。
  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值