二分查找
题目
2529. 正整数和负整数的最大计数 - 力扣(LeetCode)
给你一个按 非递减顺序 排列的数组 nums,返回正整数数目和负整数数目中的最大值。
换句话讲,如果 nums 中正整数的数目是 pos,而负整数的数目是 neg,返回 pos 和 neg 二者中的最大值。
注意:
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] <= 2000nums按 非递减顺序 排列。
题解
解法一:手写二分查找(开区间写法)
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)
给你两个整数数组 arr1、arr2 和一个整数 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^30 <= 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站@灵茶山艾府

浙公网安备 33010602011771号