在问 · 算法题解

611. 有效三角形的个数

中等二分查找排序 + 双指针力扣原题 ↗

题目描述

输入一个非负整数数组 nums。

从中选出三个不同下标上的元素作为三条边的长度,如果这三条边能组成一个三角形(任意两边之和大于第三边),就算一个有效的三元组。

返回有效三元组的数量。下标集合不同的选法分别计数,即使数值相同。

示例 1
输入:nums = [2,2,3,4]
输出:3

解释:2,3,4 可以用两个不同的 2 各组成一次,再加上 2,2,3,共 3 个。

示例 2
输入:nums = [4,2,3,4]
输出:4

数据范围

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 1000

解题思路:排序 + 双指针

先排序,从大到小枚举最长边 nums[k],在它左边用双指针 i、j 找两条短边。若 nums[i] + nums[j] > nums[k],那么把 i 换成 i..j-1 中任何一个更大的数都成立,一次性加上 j - i 个,再让 j 左移;否则和太小,i 右移。

为什么正确

固定 k 时,所有 (i', j') 中 i' < i 或 j' > j 的组合都已经被统计或排除。

复杂度分析

时间 O(n^2)(排序 O(n log n)),空间 O(log n)(排序栈)。

易错点

和大于 c 时要一次加 j - i 个,而不是只加 1;还要注意 0 长度的边永远构不成三角形,判定用严格大于。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def triangleNumber(self, nums: List[int]) -> int:
        nums.sort()
        ans = 0
        for k in range(len(nums) - 1, 1, -1):
            i, j = 0, k - 1
            while i < j:
                if nums[i] + nums[j] > nums[k]:
                    ans += j - i
                    j -= 1
                else:
                    i += 1
        return ans

载入编辑器,自己跑一遍 →

其他解法

两种解法都先排序,再统计满足两边之和大于第三边的组合;二分查找对每对边单独查找,双指针利用单调性让指针不回退,省掉 log n。

解法 2:排序 + 二分查找 时间 O(n² log n),空间 O(log n)

排序后枚举两条较短边 nums[i] 和 nums[j](i < j)。第三条边 nums[k] 只需满足 nums[k] < nums[i] + nums[j]。在 j 之后用二分查找第一个不小于这个和的位置 p,下标 j + 1 到 p − 1 都可以作第三条边,共 p − j − 1 个。

直接把“两边之和大于第三边”翻译成一次二分,容易写对。比双指针多一个 log n 因子。

from bisect import bisect_left

class Solution:
    def triangleNumber(self, nums: List[int]) -> int:
        nums.sort()
        n = len(nums)
        ans = 0
        for i in range(n):
            for j in range(i + 1, n):
                p = bisect_left(nums, nums[i] + nums[j], j + 1)
                ans += p - j - 1
        return ans

二分查找 专题的其他题目

查看 二分查找 全部题目 →