LeetCode 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

解法1:排序 + 二分查找

对于正整数 a,b,c,它们可以作为三角形的三条边,当且仅当: 
a+b>c
a+c>b
b+c>a
均成立。如果我们将三条边进行升序排序,使它们满足 a ≤ b ≤ c,那么 a+c>b 和 b+c>a 使一定成立的,我们只需要保证 a+b>c。

因此,我们可以将数组 nums 进行升序排序,随后使用二重循环枚举 a 和 b。设 a=nums[i],b=nums[j],为了防止重复统计答案,我们需要保证 i<j。剩余的边 c 需要满足 c<nums[i]+nums[j],我们可以在 [ j+1,n−1 ] 的下标范围内使用二分查找(其中 n 是数组 nums 的长度),找出最大的满足 nums[k]<nums[i]+nums[j] 的下标 k,这样一来,在 [ j+1,k ] 范围内的下标都可以作为边 c 的下标,我们将该范围的长度 k−j 累加入答案。

当枚举完成后,我们返回累加的答案即可。

注意到题目描述中 nums 包含的元素为非负整数,即除了正整数以外,nums 还会包含 0。三角平行的边长不能为0,因此我们对数组排完序之后需要先跳过0。

Java版:

class Solution {
    public int triangleNumber(int[] nums) {
        // 数组先排序,才能进行二分查找
        Arrays.sort(nums);
        int n = nums.length;
        int ans = 0;
        for (int i = 0; i <= n - 3; i++) {
            if (nums[i] == 0) {
                continue;
            }
            for (int j = i + 1; j <= n - 2; j++) {
                int k = binarySearch(nums, nums[i] + nums[j], j + 1, n - 1);
                ans += k - j;
            }
        }
        return ans;
    }

    // 在搜索范围内,找到最大的小于target的下标
    private int binarySearch(int[] nums, int target, int l, int r) {
        while (l <= r) {
            // nums[l - 1] < target
            // nums[r + 1] >= target
            int mid = (r - l) / 2 + l;
            if (nums[mid] < target) {
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return r;
    }
}


Python3版:

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

    def binarySearch(self, nums: List[int], target: int, l: int, r: int) -> int:
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] < target:
                l = mid + 1
            else:
                r = mid - 1
        return r

复杂度分析

  • 时间复杂度:O(n^2 logn),其中 n 是数组 nums 的长度。我们需要 O(nlogn) 的时间对数组 nums 进行排序,随后需要 O(n^2 logn) 的时间使用二重循环枚举 a,b 的下标以及使用二分查找得到 c 的下标范围。
  • 空间复杂度:O(logn),即为排序需要的栈空间。

解法2:排序 + 双指针

我们将当 a=nums[i],b=nums[j] 时,最大的满足 nums[k]<nums[i]+nums[j] 的下标 k 记为 k。可以发现,如果我们固定 i,那么随着 j 的递增,不等式右侧 nums[i]+nums[j] 也是递增的,因此 k 也是递增的。

这样一来,我们就可以将 j 和 k 看成两个同向(递增)移动的指针,将方法一进行如下的优化:

我们使用一重循环枚举 i。当 i 固定时,我们使用双指针同时维护 j 和 k,它们的初始值为 i + 1 ,     i + 2;

我们每一次将 j 向右移动一个位置,并尝试不断向右移动 k,使得 k 是最大的满足 nums[k]<nums[i]+nums[j] 的下标。我们将 k−j 累加入答案。

当枚举完成后,我们返回累加的答案即可。

Java版:

class Solution {
    public int triangleNumber(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        int ans = 0;
        for (int i = 0; i <= n - 3; i++) {
            if (nums[i] == 0) {
                continue;
            }
            int k = i + 2;
            for (int j = i + 1; j <= n - 2; j++) {
                if (k < j) {
                    k = j + 1;
                }
                while (k < n && nums[k] < nums[i] + nums[j]) {
                    k++;
                }
                // nums[k] >= nums[i] + nums[j]
                // k - 1 是满足要求的最大下标
                ans += k - 1 - j;
            }
        }
        return ans;
    }
}

Python3版:

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

复杂度分析

  • 时间复杂度:O(n^2),其中 n 是数组 nums 的长度。我们需要 O(nlogn) 的时间对数组 nums 进行排序,随后需要 O(n^2) 的时间使用一重循环枚举 a 的下标以及使用双指针维护 b,c 的下标。
  • 空间复杂度:O(logn),即为排序需要的栈空间。
  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值