基础二分查找
def binary_search(nums,target):
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left)/2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid -1
# 基础框架
elif nums[mid] == target:
return mid
二分查找左边界
def left_bound(nums,target):
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left)/2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid -1
# 1.缩小右边界
elif nums[mid] == target:
right = mid -1
# 2.越界判定
if left > len(nums) or nums[left] != target:
return -1
return left
二分查找右边界
def right_bound(nums,target):
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left)/2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid -1
#1.缩小左边界
elif nums[mid] == target:
left = mid + 1
#2.越界判定
if right < 0 or nums[right] != target:
return -1
return right
差异主要在边界缩小和越界判定上,都是基于二分查找基础框架进行的改良
退出时,left指向右子数组的首位元素,right指向左子数组的末尾元素
变形1:旋转
问题分析:因为旋转前,最小值的右侧是单调增加的,所以number[right]可以作为二分查找模板中的target元素
1. numbers[mid] < numbers[high],right = mid(注意不是mid-1)
2. numbers[mid]> numbers[high],left = mid + 1
3. numbers[mid] == numbers[right] :由于重复元素的存在,我们并不能确定numbers[mid]究竟在最小值的左侧还是右侧,所以对右侧数据进行步进
注释:这里的mid就是图标中的pivot
def minArray(self, numbers: List[int]) -> int:
left, right = 0, len(numbers) - 1
while left <= right:
mid = int(left + (right - left) / 2)
if numbers[mid] < numbers[right]:
right = mid
elif numbers[mid] > numbers[right]:
left = mid + 1
elif numbers[mid] == numbers[right]:
right = right -1
if left>len(numbers):
return -1
return numbers[left]
变形2:统计多个连续数字
解法1:
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0,len(nums) -1
#查找右边界
while left <= right:
mid = int(left + (right - left)/2)
if nums[mid] <= target:#注意,这里将《=逻辑合并了
left = mid + 1
elif nums[mid] > target:
right = mid - 1
right_bound = left #注意,这里右查找nums[right] == target,但是我们要找的是target的下一个元素,正好是left即(mid + 1)
if right <0 or nums[right]!= target:return 0 #有边界查找没有找到直接返回0
#查找左边界,因为右边界已经找到了,直接搜索[0,right_bound]-1
left,right = 0,right_bound-1
while left <= right:
mid = int(left + (right - left)/2)
if nums[mid] < target:
left = mid + 1
elif nums[mid] >= target:
right = mid - 1
left_bound = right
return right_bound - left_bound -1
链接:https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/solution/mian-shi-ti-53-i-zai-pai-xu-shu-zu-zhong-cha-zha-5/
解法2:
由于数组 numsnums 中元素都为整数,因此可以分别二分查找 targettarget 和 target - 1target−1 的右边界,将两结果相减并返回即可。
链接:https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/solution/mian-shi-ti-53-i-zai-pai-xu-shu-zu-zhong-cha-zha-5/
def search(self, nums: List[int], target: int) -> int:
def right_bound(tar):
left,right = 0,len(nums)-1
while left<= right:
mid = int(left + (right-left)/2)
if nums[mid]<tar:
left = mid +1
elif nums[mid] > tar:
right = mid -1
elif nums[mid] == tar:
left = mid +1
return left #这里因为left会以mid + 1 退出,正好是target的右侧边界
return right_bound(target) - right_bound(target-1)
逻辑合并
def search(self, nums: List[int], target: int) -> int:
def right_bound(tar):
left,right = 0,len(nums)-1
while left<= right:
mid = int(left + (right-left)/2)
if nums[mid]<=tar: #合并<=逻辑
left = mid +1
else:
right = mid -1
return left
return right_bound(target) - right_bound(target-1)
变形3:寻找缺失的数字 nums[i] = i 类型
def missingNumber(self, nums: List[int]) -> int:
left, right = 0,len(nums)-1
while left <= right:
mid = left + (right - left)//2
if nums[mid] == mid:
left = mid +1
else:
right = mid -1
return left
注意:i 为left,j 为right
返回值: 跳出时,变量 i 和 j 分别指向 “右子数组的首位元素” 和 “左子数组的末位元素” 。因此返回i即可。
变形4:稀疏数组
def findString(self, words: List[str], s: str) -> int:
left, right = 0,len(words)-1
while left<=right:
mid = left+ (right-left)//2
temp = mid
#连续空字符过滤
while words[mid]=="" and mid <right:
mid += 1
if words[mid] =="":
right = temp - 1
continue
#二分查找框架
if words[mid] < s:
left = mid + 1
elif words[mid] > s:
right = mid -1
elif words[mid] == s:
return mid
return -1