正整数和负整数的最大计数

标签: 数组 二分查找 计数

难度: Easy

给你一个按 非递减顺序 排列的数组 nums ,返回正整数数目和负整数数目中的最大值。

  • 换句话讲,如果 nums 中正整数的数目是 pos ,而负整数的数目是 neg ,返回 posneg二者中的最大值。

注意:0 既不是正整数也不是负整数。

示例 1:

输入:nums = [-2,-1,-1,1,2,3]
输出:3
解释:共有 3 个正整数和 3 个负整数。计数得到的最大值是 3 。

示例 2:

输入:nums = [-3,-2,-1,0,0,1,2]
输出:3
解释:共有 2 个正整数和 3 个负整数。计数得到的最大值是 3 。

示例 3:

输入:nums = [5,20,66,1314]
输出:4
解释:共有 4 个正整数和 0 个负整数。计数得到的最大值是 4 。

提示:

  • 1 <= nums.length <= 2000
  • -2000 <= nums[i] <= 2000
  • nums非递减顺序 排列。

进阶:你可以设计并实现时间复杂度为 O(log(n)) 的算法解决此问题吗?

Submission

运行时间: 25 ms

内存: 16.2 MB

class Solution:
    def maximumCount(self, nums: List[int]) -> int:
        return max(bisect_left(nums, 0), len(nums) - bisect_right(nums, 0))

Explain

此题解利用了二分查找方法,通过 Python 的 bisect 模块来高效地确定正数和负数的数量。由于数组已经按非递减顺序排列,可以使用 bisect_left 来查找第一个非负数(0或正数)的位置,这个位置也表示负数的数量。同理,使用 bisect_right 查找第一个大于0的位置,用数组长度减去这个位置得到正数的数量。最后,比较这两个数量,取最大值返回。

时间复杂度: O(log n)

空间复杂度: O(1)

class Solution:
    def maximumCount(self, nums: List[int]) -> int:
        # 使用 bisect_left 找到第一个非负数的位置
        neg_count = bisect_left(nums, 0)
        # 使用 bisect_right 找到第一个正数的位置
        pos_count = len(nums) - bisect_right(nums, 0)
        # 返回负数和正数数量的最大值
        return max(neg_count, pos_count)

Explore

选择使用二分查找而不是遍历整个数组的原因是效率。二分查找适用于已排序的数组,并且其时间复杂度为O(log n),这比遍历数组的O(n)时间复杂度要低得多。在大数据量的情况下,二分查找可以显著减少计算时间。

`bisect_left(nums, 0)`函数会返回数组中第一个不小于0的元素的位置。如果数组中包含0,则它会指向第一个0;如果数组中不包含0,它会指向第一个正数。这个位置同时也是负数数量的计数,因为它表示了数组中从开始到这个位置之前所有的数都是负数。

`bisect_right(nums, 0)`函数会返回数组中第一个严格大于0的元素的位置。如果数组中包含一个或多个0,`bisect_right`将返回最后一个0之后的位置。这确保了所有的0都被包括在内,而计算正数的数量时,从这个位置开始到数组结束的所有元素都是正数。因此,这个位置用于确定正数的开始位置,从而正确计算正数的数量。

对于全是正数或全是负数的数组,这种方法仍然是效率的,因为二分查找在这种情况下也能迅速确定正数或负数的边界。例如,如果数组全是正数,`bisect_left(nums, 0)`将会返回0,`bisect_right(nums, 0)`也将返回0,从而快速确定没有负数。对于全是负数的数组,这两个函数将返回数组的长度和数组长度,从而确定没有正数。在这种特殊情况下,二分查找的效率与直接检查数组的第一个或最后一个元素的效率相当,因为二分查找迅速缩小了搜索范围。