二分查找
算法要求
- 顺序存储结构
- 元素大小有序
二分查找过程
- 将元素排序;
- 将中间位置记录的这个元素与目标元素比较;
2.1 如果相同,则查找成功;
2.2 否则将元素分为前、后两部分,如果目标元素相比查找元素在左,则更新右边界;如果目标元素相比查找元素在右,则更新左边界;
2.3 重复步骤2
如何更新左右边界
- 查找区间为 [left, right],循环条件为 left <= right;左右分别更新为 mid+1、mid-1
- 查找区间为 [left, right),循环条件为 left < right;左右分别更新为 mid+1、mid
时间复杂度
二分查找过程的时间复杂度是O(n),其中 n 是元素个。每次查找都缩短一半的长度。
案例
type1:常规对比中间元素
367. 有效的完全平方数
def isPerfectSquare(num):
left,right=0,num
while left<=right:
mid=(left+right)//2
if mid*mid==num:
return True
elif mid*mid<num:
left=mid+1
else:
right=mid-1
return False
167. 两数之和 II - 输入有序数组
def twoSum(numbers, target):
n = len(numbers)
for i in range(n):
tmp = target - numbers[i]
left,right=i+1, n-1
while left<=right:
mid=(left+right)//2
if numbers[mid]==tmp:
return [i+1, mid+1]
elif numbers[mid]>tmp:
right=mid-1
else:
left=mid+1
return [-1,-1]
33. 搜索旋转排序数组
def search(nums: List[int], target: int):
left,right=0,len(nums)-1
while left<=right:
mid=(left+right)//2
if nums[mid]==target:
return mid
elif nums[left]<=nums[mid]:
if nums[left]<=target<=nums[mid]:
right=mid-1
else:
left=mid+1
else:
if nums[mid]<=target<=nums[right]:
left=mid+1
else:
right=mid-1
return -1
type2:跳出循环时的左边界
跳出循环时,left 满足 right 更新时 if 后的条件, left-1 满足满足 left 更新时 if 后的条件
69. x 的平方根
def findSqrt(x):
left,right=0,x
while left<=right:
mid=(left+right)//2
if mid*mid<=x:
left=mid+1
else:
right=mid-1
#跳出循环时 left>right,说明上一个更新的是 left,即(left-1)*(left-1)<=x;又 right*right>x,所以left*left>x,因此 left-1 就是要查找的目标数
return left-1
35. 搜索插入位置
给定一个排序数组nums和一个目标值target,返回目标值索引。如果不存在,返回它将会被按顺序插入的位置。
def searchInsert(nums, target):
left,right=0,len(nums)-1
while left<=right:
mid=(left+right)//2
if nums[mid]==target:
return mid
elif nums[mid]<target:
left=mid+1
else:
right=mid-1
#跳出循环时后,nums[left-1]<target & nums[right]>target i.e nums[left]>target,因此target要放在left位置
return left
type3:第一个和最后一个位置
34. 在排序数组中查找元素的第一个和最后一个位置
def searchRange(nums: List[int], target: int):
def binarySearch(condition):
left, right = 0, len(nums)-1
while left <= right:
mid = (left+right)//2
if condition(nums[mid], target):
right = mid-1
else:
left = mid+1
return left
start = binarySearch(lambda x,y: x >= y)
end = binarySearch(lambda x,y: x > y) - 1
return [start, end] if start<=end else [-1,-1]