检查一个数是否在数组中占绝大多数

Submission

运行时间: 22 ms

内存: 16.1 MB

class Solution:
    def isMajorityElement(self, nums: List[int], target: int) -> bool:
        def binarySearchLeft(nums, target):
            left, right = 0, len(nums)
            while left < right:
                mid = (left + right) // 2
                if nums[mid] < target:
                    left = mid + 1
                else:
                    right = mid
            return left

        # 二分查找找到target最后一次出现的位置
        def binarySearchRight(nums, target):
            left, right = 0, len(nums)
            while left < right:
                mid =  (left + right) // 2
                if nums[mid] <= target:
                    left = mid + 1
                else:
                    right = mid
            return left

        left_idx = binarySearchLeft(nums, target)
        right_idx = binarySearchRight(nums, target)

        # 如果target在数组中出现了超过N/2次,则返回True,否则返回False
        return (right_idx - left_idx) > len(nums) // 2

Explain

本题解利用二分查找算法来定位目标数字target在有序数组nums中的起始和结束位置。首先,通过一个辅助函数binarySearchLeft找到target首次出现的位置(如果存在)。接着,通过另一个辅助函数binarySearchRight找到target最后一次出现的位置加一(相当于下一个不同元素的位置,或数组边界)。最后,计算这两个位置的差值,即target在数组中出现的次数。如果这个次数超过数组长度的一半,则函数返回True,表示target占绝大多数;否则返回False。

时间复杂度: O(log n)

空间复杂度: O(1)

class Solution:
    def isMajorityElement(self, nums: List[int], target: int) -> bool:
        def binarySearchLeft(nums, target):
            left, right = 0, len(nums)
            while left < right:
                mid = (left + right) // 2
                if nums[mid] < target:
                    left = mid + 1  # target比中点大,搜索右半区
                else:
                    right = mid  # target小于等于中点,搜索左半区
            return left

        def binarySearchRight(nums, target):
            left, right = 0, len(nums)
            while left < right:
                mid =  (left + right) // 2
                if nums[mid] <= target:
                    left = mid + 1  # target小于等于中点,搜索右半区
                else:
                    right = mid  # target比中点大,搜索左半区
            return left

        left_idx = binarySearchLeft(nums, target)
        right_idx = binarySearchRight(nums, target)

        return (right_idx - left_idx) > len(nums) // 2  # 比较target出现次数是否超过数组长度的一半

Explore

在这种情况下,`binarySearchLeft` 和 `binarySearchRight` 函数会返回相同的索引值,该值指向数组中第一个大于 `target` 的元素位置。如果 `target` 大于数组中所有元素,这两个函数返回的索引值会是数组长度。因此,当 `left_idx` 等于 `right_idx` 时,表示 `target` 在数组中不存在,因此 `target` 出现次数为零。这样,通过检查 `right_idx - left_idx` 是否大于 `len(nums) // 2`,可以正确判断 `target` 是否占绝大多数,即使 `target` 不存在也不会错误返回位置。

算法中使用的是 `len(nums) // 2` 来确定多数的阈值。在数组长度为偶数时,这个值恰好是数组长度的一半;如果数组长度为奇数,这个值是数组长度除以 2 向下取整的结果。因此,只有当 `target` 出现的次数严格大于 `len(nums) // 2` 时,才返回 `True`。这意味着在边界情况下,例如当 `target` 出现次数恰好为数组长度的一半(不论数组长度是奇数还是偶数)时,算法仍会返回 `False`。因此,该算法在处理边界条件时是准确的,不会因为整数除法向下取整而产生错误判断。

返回 `target` 最后一次出现位置的下一个位置是常用的技术,用以计算元素的计数或确定范围。这种方法本身不会导致误判元素数量。它确保我们可以通过 `right_idx - left_idx` 直接得到 `target` 在数组中的出现次数。此外,即使 `target` 在数组中不存在,上述方法也能正确返回出现次数为 0,因为 `left_idx` 和 `right_idx` 将相等。因此,无论数组情况如何,这种处理方式都不会导致元素数量的误判。