思考:退出条件恒定设置为while L ≤ R,真的退出时,L与R紧挨着,但L在左,R在右,此时要考虑答案选择L还是R。
比如,考虑搜索最左与搜索最右(搜索是否在数组中可认为是最左或者最右均可)
搜索最左
,比如[0, 1, 2, 2, 2, 3, 4]搜索最左的2,退出时,L指向最左的2,R指向1。也就是退出时,A[L] ≥ 2, A[R] < 2。那么,平时就是A[L] < 2,A[R] ≥ 2。
def bsearch_left(A, L, R, T):
lo, hi = L, R
while L <= R:
M = (L + R) // 2
if A[M] < T:
L = M + 1
else: # 等于在退出时再判断
R = M - 1
# 退出时要判断A[L]是否为T,因为等号不一定取到
if lo <= L <= hi and A[L] == T: # L可能到了hi的右侧。
return L
return -1
或者说,第1个2的右侧
都是可能的潜在范围,所以让L
停留在正确答案。
搜索最右
,最后1个2的左侧
都是可能的潜在范围,所以让R
停留在正确答案。退出时A[R] ≤ 2, A[L] > 2。平时则相反,A[R] > 2, A[L] ≤ 2
def bsearch_left(A, L, R, T):
lo, hi = L, R
while L <= R:
M = (L + R) // 2
if A[M] <= T:
L = M + 1
else: # 等于在退出时再判断
R = M - 1
# 退出时要判断A[L]是否为T,因为等号不一定取到
if lo <= R <= hi and A[R] == T: # R可能到了lo的左侧。
return R
return -1
插入位置
,比如[1, 2, 2, 5, 6],插入3,则会找到5(比3大,但最接近);插入2,可以找第一个2或者最后一个2。综合起来,就是找≥的但最接近的,或者说它左边的都比它小,也就是会找第一个2。退出时答案为L。如果插入10,插入6的右边,L确实会指向那里。知道答案肯定存在,退出时就不用再判断了。
求根号
,向下取整,比如根号10取3,根号9自然是3。因为向下取整,所以左边(小的数)是潜在的答案范围(潜在范围的最右),退出时让R停留在这个区间,A[R] ≤ T,那么平时这个条件就给L用。知道答案肯定存在,退出时就不用再判断了。
class Solution(object):
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
def bsearch(L, R, x):
while L <= R:
M = (L + R) // 2
if M * M <= x:
L = M + 1
else:
R = M - 1
return R
return bsearch(0, x, x)
这样统一之后,while条件判断保持一致,M不需要考虑偏左还是偏右,都行,因为下一轮的范围肯定是缩小的,不管是变左边还是右边。里面判断只需要2种,相等情况不用特意判断了,第一个if用于处理L,它的判断条件就是退出时R的状态。else只需要无脑处理R就行。
相关题: