目录
注意:
主要和b站大雪菜一起刷题,宝藏up主(https://www.bilibili.com/video/BV1Ft41157zW)
模板
- 确定二分的边界
- 编写二分的代码框架
- 设计一个check(性质)
- 判断一下区间如何更新
- 如果更新方式写的是l=mid,r=mid-1,那么就在算mid的时候+1
# 模板1
while l<r:
mid = (l+r)>>2
if 性质:
l = mid + 1
else:
r = mid
retrun l
# 模板2
while l<r:
mid = (l+r+1)>>2
if 性质:
l = mid
else:
r = mid - 1
69. x 的平方根
思路:
- 最后答案取的是x的向下取整
- 利用t^2<=x将整个[0,x]划成两个部分,[0,t][t+1,x]
class Solution:
def mySqrt(self, x: int) -> int:
l,r = 0, x
while l < r:
mid = (l+r+1)>>1
if mid<=x/mid:
l = mid
else:
r = mid-1
return l
35. 搜索插入位置
思路:
- 利用模板,寻找插入位置。
- 因为数组是递增序列,若数组为空,则直接插入到0,若数组最后一个数比target小,则插入到末尾
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
if not nums or target>nums[-1]:
return len(nums)
l,r = 0, len(nums)-1
while l<r:
mid = (l+r)//2
if nums[mid]>=target:
r = mid
else:
l = mid+1
return r
34. 在排序数组中查找元素的第一个和最后一个位置
思路:
- 寻找第一个和最后一个位置
- 第一个位置寻找设置的check性质为:t>=target,寻找这个的左端点
- 最后一个位置寻找设置的check性质为:t<=target,寻找这个的右端点
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
if not nums:
return [-1,-1]
l, r = 0, len(nums)-1
# 寻找左端点
while l<r:
mid = (l+r)>>1
if nums[mid]>=target:
r = mid
else:
l = mid + 1
start = l
if nums[start]!=target:
return [-1,-1]
# 寻找右端点
l, r = 0, len(nums)-1
while l<r:
mid = (l+r+1)>>1
if nums[mid] <= target:
l = mid
else:
r = mid - 1
end = l
return [start, end]
74. 搜索二维矩阵
思路:
- 将这个二维矩阵看成一个一维的0~n*m-1且满足递增的序列
- 然后直接套二分法做即可,这里的check(性质):t>=target,我们需要寻找的是这个区间的最左端点
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
if len(matrix)==0 or len(matrix[0])==0:
return False
m, n = len(matrix), len(matrix[0])
l, r = 0, m*n-1
while l<r:
mid = (l+r)>>1
if matrix[mid//n][mid%n]>=target:
r = mid
else:
l = mid+1
if matrix[l//n][l%n]!=target:
return False
return True
153. 寻找旋转排序数组中的最小值
思路:
- 找到一个性质将这个排序数组分成两段,这里选用check(性质):t<=nums[-1],目标是寻找这段区间的最左端点
class Solution:
def findMin(self, nums: List[int]) -> int:
l,r = 0, len(nums)-1
while l < r:
mid = (l+r)>>1
if nums[mid]<=nums[-1]:
r = mid
else:
l = mid+1
return nums[l]
33. 搜索旋转排序数组
思路:
- 上一题是要求寻找旋转后的排序数组里的最小值,这题是寻找旋转后的排序数组target的下标
- 不能直接用性质划成两段,可以先寻找target在旋转后的前一段,还是后一段里,因为在这某一段里面仍然是单调的
- 可以用上一题的方法先找到旋转数组中的最小值的坐标,判断一下target和nums[-1]的大小关系,即可知道target在哪一段里,在在这里一段里用二分法即可
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
# 寻找最小值的坐标
l, r = 0, len(nums)-1
while l < r:
mid = (l+r)>>1
if nums[mid]<=nums[-1]:
r = mid
else:
l = mid+1
if nums[-1]>=target:# [0, 1, 2]
r = len(nums)-1
else:# [4,5,6,7]
l,r = 0, r-1
while l<r:
mid = (l+r)>>1
if nums[mid]>=target:
r = mid
else:
l = mid+1
if nums[l] != target:
return -1
return l
278. 第一个错误的版本
思路:
- 有[1,2....n]个版本号,寻找第一个错误的版本
- 这里利用二分模板,它的check也已经给了函数,所以只需要套模板即可
# The isBadVersion API is already defined for you.
# @param version, an integer
# @return a bool
# def isBadVersion(version):
class Solution:
def firstBadVersion(self, n):
"""
:type n: int
:rtype: int
"""
l, r = 1, n
while l<r:
mid = (l+r)>>1
if isBadVersion(mid):
r = mid
else:
l = mid + 1
return l
162. 寻找峰值
思路:
- 峰值指的就是其两边的值比它小,用二分法寻找这个峰值的话,可以想象在这个峰值之前的数比它小,在这个峰值之后的数比它小
- 这里语言可能描述不太清楚,可以看下大雪菜的视频,画图之后更好理解。lc 162. Find Peak Element 106:39
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
l,r = 0, len(nums)-1
while l<r:
mid = (l+r)>>1
if nums[mid]>nums[mid+1]:
r = mid
else:
l = mid+1
return l
287. 寻找重复数
思路:
- 利用抽屉原理做,苹果数如果大于抽屉数,就一定有多余的
- 同理,如果统计的个数大于这个区间应该的长度,就一定有重复的数字
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
l,r = 1, len(nums)
while l<r:
mid = (l+r)>>1
cnt = 0
for num in nums:
if num<=mid and num>=l:
cnt += 1
if cnt > mid-l+1:
r = mid
else:
l = mid+1
return l
275. H指数 II
思路:
- lc 275. H-Index II 129:47
class Solution:
def hIndex(self, citations: List[int]) -> int:
l,r=0,len(citations)
while l<r:
mid = (l+r+1)>>1
if citations[len(citations)-mid]>=mid:
l = mid
else:
r = mid-1
return l