二分查找

题目

2529. 正整数和负整数的最大计数 - 力扣(LeetCode)

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

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

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

示例

示例 1:

输入:

nums = [-2, -1, -1, 1, 2, 3]

输出:

3

解释:
共有 3 个正整数(1, 2, 3)和 3 个负整数(-2, -1, -1)。计数得到的最大值是 3

示例 2:

输入:

nums = [-3, -2, -1, 0, 0, 1, 2]

输出:

3

解释:
共有 2 个正整数(1, 2)和 3 个负整数(-3, -2, -1)。计数得到的最大值是 3

示例 3:

输入:

nums = [5, 20, 66, 1314]

输出:

4

解释:
共有 4 个正整数,0 个负整数。计数得到的最大值是 4

提示

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

题解

解法一:手写二分查找(开区间写法)

from typing import List

class Solution:
    def maximumCount(self, nums: List[int]) -> int:
        neg = self.lowerBound(nums, 0)          # < 0 的元素个数
        pos = len(nums) - self.lowerBound(nums, 1)  # > 0 的元素个数
        return max(neg, pos)
    
    def lowerBound(self, nums: List[int], target: int) -> int:
        left, right = -1, len(nums)  # 开区间 (left, right)
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid] >= target:
                right = mid
            else:
                left = mid
        return right  # 第一个 >= target 的下标

解法二:使用标准库 bisect

from typing import List
from bisect import bisect_left, bisect_right

class Solution:
    def maximumCount(self, nums: List[int]) -> int:
        neg = bisect_left(nums, 0)      # 第一个 >= 0 的位置 → 即负数个数
        pos = len(nums) - bisect_right(nums, 0)  # 所有 > 0 的元素个数
        return max(neg, pos)

说明:

  • bisect_left(nums, 0) 返回第一个 >= 0 的下标,即负数的个数。
  • bisect_right(nums, 0) 返回第一个 > 0 的下标,因此 n - bisect_right(...) 就是正数个数。
复杂度分析
  • 时间复杂度:$O(\log n)$,其中 $n$ 为 nums 的长度。仅进行常数次二分查找。
  • 空间复杂度:$O(1)$,仅使用常数额外空间。
总结(通用二分计数模板)
需求 写法
< x 的元素个数 lowerBound(nums, x)
≤ x 的元素个数 lowerBound(nums, x + 1)
≥ x 的元素个数 n − lowerBound(nums, x)
> x 的元素个数 n − lowerBound(nums, x + 1)

注:lowerBound(nums, x) 表示在 nums 中查找第一个 >= x 的下标。


1385. 两个数组间的距离值 - 力扣(LeetCode)

给你两个整数数组 arr1arr2 和一个整数 d,请你返回两个数组之间的 距离值

「距离值」定义为符合此距离要求的元素数目:
对于元素 arr1[i]不存在任何元素 arr2[j] 满足 |arr1[i] - arr2[j]| <= d

示例

示例 1:

输入:

arr1 = [4,5,8], arr2 = [10,9,1,8], d = 2

输出:

2

解释:

  • 对于 arr1[0] = 4

    • |4 - 10| = 6 > 2
    • |4 - 9| = 5 > 2
    • |4 - 1| = 3 > 2
    • |4 - 8| = 4 > 2
      所有差值均大于 d,符合条件。
  • 对于 arr1[1] = 5

    • |5 - 10| = 5 > 2
    • |5 - 9| = 4 > 2
    • |5 - 1| = 4 > 2
    • |5 - 8| = 3 > 2
      所有差值均大于 d,符合条件。
  • 对于 arr1[2] = 8

    • |8 - 10| = 2 <= 2
      存在差值 ≤ d,不符合条件。

因此,只有 arr1[0]arr1[1] 符合条件,距离值为 2

示例 2:

输入:

arr1 = [1,4,2,3], arr2 = [-4,-3,6,10,20,30], d = 3

输出:

2

示例 3:

输入:

arr1 = [2,1,100,3], arr2 = [-5,-2,10,-3,7], d = 6

输出:

1

提示

  • 1 <= arr1.length, arr2.length <= 500
  • -10^3 <= arr1[i], arr2[j] <= 10^3
  • 0 <= d <= 100

题解

from typing import List
from bisect import bisect_left

class Solution:
    def findTheDistanceValue(self, arr1: List[int], arr2: List[int], d: int) -> int:
        # 对 arr2 排序,以便使用二分查找
        arr2.sort()
        ans = 0
        
        for x in arr1:
            # 在 arr2 中查找第一个 >= (x - d) 的位置
            i = bisect_left(arr2, x - d)
            
            # 情况1: 所有元素都 < x - d → i == len(arr2)
            # 情况2: 第一个 >= x - d 的元素 > x + d → 区间 [x-d, x+d] 内无元素
            if i == len(arr2) or arr2[i] > x + d:
                ans += 1
                
        return ans

时间复杂度分析:

排序 arr2:O(m log m),其中 m = len(arr2)
遍历 arr1 并二分查找:O(n log m),其中 n = len(arr1)
总时间复杂度:O((n + m) log m)
空间复杂度:O(1)(不计排序的栈空间)
相比暴力法 O(n * m),此方法在数据规模较大时更优。

参考 B站@灵茶山艾府

posted @ 2026-01-18 11:36  米浆  阅读(3)  评论(0)    收藏  举报