文章目录
1. 花式二分查找
搜索一个元素时,搜索区间两端闭
while条件带等号,否则需要打补丁
if相等就返回,其他事情甭操心
mid必须加减一,因为区间两端闭
while结束就凉了,凄凄惨惨返-1
搜索左右区间时,搜索区间要阐明
左闭右开最常见,其余逻辑便自明
while要用小于号,这样才能不漏掉
if相等别返回,利用mid锁边界
1.1 有序数组的查找
1.1.1 普通二分查找
nums = [1, 1, 3, 6, 6, 6, 7, 8, 8, 9]
def binary_search(nums, target):
'''找一个数'''
l = 0
r = len(nums) - 1
while l <= r:
mid = (l + r) // 2
# 如果是Java Cpp
# mid = l + (r - l) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
l = mid + 1
elif nums[mid] > target:
r = mid - 1
return -1
print("binary_search", binary_search(nums, 6))
1.1.2 lower_bound
def lower_bound(nums, target):
'''找左边界'''
l = 0
r = len(nums)
while l < r:
mid = (l + r) // 2
if nums[mid] == target:
r = mid
elif nums[mid] < target:
l = mid + 1
elif nums[mid] > target:
r = mid
# 对未命中情况进行后处理
if l == len(nums):
return -1
return l if nums[l] == target else -1
print("lower_bound", lower_bound(nums, 6))
print("lower_bound", lower_bound(nums, 2))
1.1.3 upper_bound
def upper_bound(nums, target):
'''找右边界'''
l = 0
r = len(nums)
while l < r:
mid = (l + r) // 2
if nums[mid] == target:
l = mid + 1
elif nums[mid] < target:
l = mid + 1
elif nums[mid] > target:
r = mid
if l == 0:
return -1
return l - 1 if nums[l - 1] == target else -1
print("upper_bound", upper_bound(nums, 100))
print("upper_bound", upper_bound(nums, -1))
print("upper_bound", upper_bound(nums, 2))
print("upper_bound", upper_bound(nums, 6))
1.1.4 三种方法串讲
我们来做两个题,这道题涉及了lower_bound
和upper_bound
:
面试题53 - I. 在排序数组中查找数字 I(二分法,清晰图解)
class Solution:
def search(self, nums: List[int], target: int) -> int:
def lower_bound(nums, x):
l, r = 0, len(nums)
while l < r: # 左闭右开
mid = (l + r) // 2
# lower_bound在去到这个数时,将区间往左逼近
if nums[mid] == x:
r = mid
elif nums[mid] < x:
l = mid + 1 # 左闭右开
elif nums[mid] > x:
r = mid
return l
def upper_bound(nums, x):
l, r = 0, len(nums)
while l < r:
mid = (l + r) // 2
# lower_bound在去到这个数时,将区间往右逼近
if nums[mid] == x:
l = mid + 1
elif nums[mid] < x:
l = mid + 1
elif nums[mid] > x:
r = mid
return r
lb, rb = lower_bound(nums, target), upper_bound(nums, target)
if lb >= len(nums):
return 0
return rb - lb
看了题解才知道,只用一个upper_bound就可以了:
class Solution:
def search(self, nums: List[int], target: int) -> int:
def upper_bound(nums, x):
l, r = 0, len(nums)
while l < r:
mid = (l + r) // 2
if nums[mid] == x:
l = mid + 1
elif nums[mid] < x:
l = mid + 1
elif nums[mid] > x:
r = mid
return r
return upper_bound(nums, target) - upper_bound(nums, target - 1)
1.2 旋转数组
1.2.1 查找旋转数组的某个数
1.2.1.1 无重复
class Solution:
def search(self, nums: List[int], target: int) -> int:
n = len(nums)
l = 0
r = n - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
# 左边有序
if nums[l] < nums[mid] or l==mid:
if nums[l] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
# 右边有序
else:
if nums[mid] < target <= nums[r]:
l = mid + 1
else:
r = mid - 1
return -1
1.2.1.2 有重复
class Solution:
def search(self, nums: List[int], target: int) -> int:
n = len(nums)
l = 0
r = n - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return True
# 相比于原版就加了这个if判断
if nums[l] == nums[mid]:
l += 1
continue
if nums[l] < nums[mid] or l==mid:
if nums[l] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target <= nums[r]:
l = mid + 1
else:
r = mid - 1
return False
⭐️ 1.2.2 查找旋转数组的最小值
1.2.2.1 无重复
class Solution:
def findMin(self, numbers: List[int]) -> int:
l = 0
r = len(numbers) - 1
while l < r:
mid = (l + r) // 2
if numbers[l] >= numbers[r]:
if numbers[mid] < numbers[r]:
r = mid
else:
l = mid + 1
else:
return numbers[l]
return numbers[r]
1.2.2.2 有重复
class Solution:
def findMin(self, numbers: List[int]) -> int:
l = 0
r = len(numbers) - 1
while l < r:
mid = (l + r) // 2