算法题 有效三角形的个数

611. 有效三角形的个数

问题描述

给定一个包含非负整数的数组 nums,返回可以形成有效三角形的三元组数量。

有效三角形定义:三个正数能构成三角形,当且仅当任意两边之和大于第三边。

示例

输入: nums = [2,2,3,4]
输出: 3
解释: 有效组合为:
- (2,3,4): 2+3>4, 2+4>3, 3+4>2
- (2,3,4): 另一个2
- (2,2,3): 2+2>3, 2+3>2, 2+3>2

算法思路

核心思想:排序 + 双指针优化

  1. 排序预处理:将数组升序排列
  2. 固定最长边:枚举每个位置作为三角形的最长边
  3. 双指针查找:在左侧找两个较短边,使其和大于最长边

关键:

  • 三角形成立条件:a + b > c(其中 c 是最长边)
  • 排序后,只需验证 nums[i] + nums[j] > nums[k]
  • 固定 k 为最长边,用双指针在 [0, k-1] 范围内找满足条件的 i,j

优化:

  • 排序后剪枝:一旦 nums[i] + nums[j] > nums[k],则 jk-1 的所有位置都满足
  • 双指针移动:根据和的大小决定指针移动方向

代码实现

import java.util.Arrays;

class Solution {
    /**
     * 计算能构成有效三角形的三元组数量
     * 
     * @param nums 非负整数数组
     * @return 有效三角形的数量
     */
    public int triangleNumber(int[] nums) {
        // 边界情况:少于3个元素无法构成三角形
        if (nums == null || nums.length < 3) {
            return 0;
        }
        
        // 第一步:排序,便于后续双指针操作
        Arrays.sort(nums);
        
        int count = 0;  // 记录有效三角形数量
        
        // 第二步:枚举最长边的位置k(从2开始,因为需要至少3个元素)
        for (int k = 2; k < nums.length; k++) {
            int left = 0;    // 左指针,指向最短边
            int right = k - 1; // 右指针,指向次长边
            
            // 使用双指针在[0, k-1]范围内查找满足条件的组合
            while (left < right) {
                // 检查两个较短边之和是否大于最长边
                if (nums[left] + nums[right] > nums[k]) {
                    // 如果成立,则[left, right-1]到right的所有组合都成立
                    // 因为数组已排序,nums[i] + nums[right] >= nums[left] + nums[right] > nums[k]
                    count += right - left;
                    
                    // 尝试找更多组合,移动右指针
                    right--;
                } else {
                    // 和不够大,需要更大的较短边
                    left++;
                }
            }
        }
        
        return count;
    }
}

算法分析

时间复杂度:O(n²)

  • 排序:O(n log n)
  • 外层循环:O(n) - 枚举最长边
  • 内层双指针:O(n) - 每个元素最多被访问一次
  • 总体:O(n²)

空间复杂度:O(1)

  • 只使用常数额外空间
  • 排序使用的空间不计入(原地排序)

对比暴力解法:

方法时间复杂度空间复杂度
暴力三重循环O(n³)O(1)
排序+双指针O(n²)O(1)

算法过程

nums = [2,2,3,4]

1:排序

原始数组:[2,2,3,4] → 已排序

2:枚举最长边

k=2(nums[k]=3)

  • left=0, right=1
  • nums[0]+nums[1]=2+2=4 > 3 → 满足
  • count += right-left = 1-0 = 1
  • right-- → right=0
  • left >= right,退出
  • 贡献:1个三角形(2,2,3)

k=3(nums[k]=4)

  • left=0, right=2
  • nums[0]+nums[2]=2+3=5 > 4 → 满足
  • count += right-left = 2-0 = 2
  • right-- → right=1
  • nums[0]+nums[1]=2+2=4 = 4 → 不满足(必须严格大于)
  • left++ → left=1
  • left >= right,退出
  • 贡献:2个三角形(2,3,4)和(2,3,4)

总计:1 + 2 = 3

测试用例

public static void main(String[] args) {
    Solution solution = new Solution();
    
    // 测试用例1:标准示例
    int[] nums1 = {2,2,3,4};
    System.out.println("Test 1: " + solution.triangleNumber(nums1)); // 3
    
    // 测试用例2:无法构成三角形
    int[] nums2 = {1,2,3};
    System.out.println("Test 2: " + solution.triangleNumber(nums2)); // 0
    // 1+2=3,不大于3
    
    // 测试用例3:全相等
    int[] nums3 = {3,3,3,3};
    System.out.println("Test 3: " + solution.triangleNumber(nums3)); // 4
    // C(4,3)=4种组合,都满足3+3>3
    
    // 测试用例4:小数组
    int[] nums4 = {1};
    System.out.println("Test 4: " + solution.triangleNumber(nums4)); // 0
    
    // 测试用例5:包含0
    int[] nums5 = {0,1,2,3};
    System.out.println("Test 5: " + solution.triangleNumber(nums5)); // 0
    // 0不能作为边长
    
    // 测试用例6:大数组
    int[] nums6 = {2,2,4,4,5,6};
    System.out.println("Test 6: " + solution.triangleNumber(nums6)); // 11
    
    // 测试用例7:降序输入
    int[] nums7 = {4,3,2,2};
    System.out.println("Test 7: " + solution.triangleNumber(nums7)); // 3
    // 排序后与test1相同
    
    // 测试用例8:边界值
    int[] nums8 = {1,1,1};
    System.out.println("Test 8: " + solution.triangleNumber(nums8)); // 1
}

关键点

  1. 排序

    • 确保 nums[k] 是最大边
    • 只需验证 a + b > c
    • 其他不等式自动满足(a + c > b, b + c > a
  2. 双指针

    • left 从0开始,rightk-1 开始
    • nums[left] + nums[right] > nums[k] 时,leftright-1 的所有位置都满足
    • 因此可以一次性添加 right - left 个组合
  3. 指针移动策略

    • 和太大:right--(尝试更小的次长边)
    • 和太小:left++(需要更大的最短边)
  4. 边界处理

    • 数组长度小于3:返回0
    • 包含0或负数:排序后0会在前面,自然被排除

常见问题

  1. 为什么只需验证一个不等式?
    排序后 nums[k] ≥ nums[right] ≥ nums[left],所以:

    • nums[left] + nums[k] ≥ nums[left] + nums[right] > nums[k] ≥ nums[right] → 成立
    • nums[right] + nums[k] ≥ nums[left] + nums[k] > nums[right] → 成立
      只需验证 nums[left] + nums[right] > nums[k]
  2. 能否用哈希表优化?
    不适合。三角形问题具有顺序性,双指针利用了排序后的单调性。

  3. 如何处理重复元素?
    算法自然处理重复元素,每个位置被视为独立元素。

  4. 算法的鲁棒性
    已处理:

    • 空数组
    • 小数组
    • 包含0
    • 无解情况
  5. 是否有数学优化?
    对于特定分布的数据(如等差数列),可能有数学公式,但通用解法中双指针最优。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值