- 二分法是经典的查找算法,时间复杂度是O(logn),其使用的前提是有序数组和无重复元素(有重复元素的话返回的结果不唯一)。
- 查找过程中,遵循循环一致性原则,各个环节都是保持一致的。初始化条件,决定搜索区间、while终止点、更新、返回
- 二分查找的写法有两种:[left,right]和[left,right)
- 二分查找的类型有三种:有序不重复找一个数、有序重复找第一次出现的索引位置、有序重复找最后一次出现的索引位置
一、题型
(一)找一个数
因为我们初始化 right = nums.length - 1
所以决定了我们的「搜索区间」是 [left, right]
终止条件是left==right+1
所以决定了 while (left <= right)
同时也决定了 left = mid+1 和 right = mid-1
因为我们只需找到⼀个 target 的索引即可
所以当 nums[mid] == target 时可以⽴即返回
(二)寻找左侧边界的⼆分查找
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
终止条件是left==right
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要⽴即返回 ⽽要收紧右侧边界以锁定左侧边界:right=mid
最后return left
例题:
35. 搜索插入位置
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left=0
right=len(nums)-1
while left<=right:
mid=left+(right-left)//2
if nums[mid]==target:
right=mid-1
elif nums[mid]<target:
left=mid+1
else:right=mid-1
return left
left=0
right=len(nums)
while left<right:
mid=left+(right-left)//2
if nums[mid]==target:
right=mid
elif nums[mid]<target:
left=mid+1
else:
right=mid
return left
69. Sqrt(x)
- 易错点:(1)在于初始边界尽量取大一些,不然会遇到0、1、2不符合的情况;(2)最后的返回值:[left,right)终止条件是left=right,所以返回right-1;[left,right}终止条件是left=right+1,所以返回right;
class Solution:
def mySqrt(self, x: int) -> int:
if x==0 or x==1:return x
else:
left,right=1,x//2+1
ans=0
while left<right:
mid=left+(right-left)//2
if mid<x/mid:
left=mid+1
elif mid==x/mid:
return mid
else:
right=mid
return right-1
class Solution:
def mySqrt(self, x: int) -> int:
if x==0 or x==1:return x
else:
left,right=1,x//2
ans=0
while left<=right:
mid=left+(right-left)//2
if mid<x/mid:
left=mid+1
elif mid==x/mid:
return mid
else:
right=mid-1
return right
(三)寻找右侧边界的⼆分查找
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
终止条件是left==right
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最右侧索引
所以当 nums[mid] == target 时不要⽴即返回 ⽽要收紧左侧边界以锁定右侧边界:left=mid+1
⼜因为收紧左侧边界时必须 left = mid + 1
所以最后⽆论返回 left 还是 right,必须减⼀:因为left因为收紧边界多加了1:return left-1\right-1
例题:
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
res=[]
left,right=0,len(nums)
while left < right:
mid=left+(right-left)//2
if nums[mid]==target:
right=mid
elif nums[mid]>target:
right=mid
elif nums[mid]<target:
left=mid+1
if left>=len(nums) or nums[left]!=target:res.append(-1)
else:res.append(left)
left,right=0,len(nums)
while left < right:
mid=left+(right-left)//2
if nums[mid]==target:
left=mid+1
elif nums[mid]>target:
right=mid
elif nums[mid]<target:
left=mid+1
if right<=0 or nums[right-1]!=target:res.append(-1)
else:res.append(right-1)
return res