.. EOF
面试算法题 - 二分查找搜索范围
去再惠面试的时候, 问了我一道二分查找的变种题, 我当时答的并不是特别清楚, 用这篇日志整理记录一下.
问题的定义
input:
- 给定一个升序排列的自然数数组, eg. [1, 3, 3, 5, 7, 7, 7, 7, 8, 14, 14]
- 任意自然数, eg. 7
output:
数组内 值为7区域的左右边界index: [1, 3, 3, 5, 7, 7, 7, 7, 8, 14, 14]
这个例子中就是**(4, 7)**
我的思路
我首先想到的是生成inverted index再去查找, 或者用Galloping search.
后来才想到考官想考察的是binary search.
于是我的思路就变成先用binary找到那个值的区域里的随机一点, 然后用两个while去找左右的边界.
但如果这个区域太大, 时间复杂度又变成O(n)了.
最后考官提醒我可以对二分查找做一下改动就可以啦.
原版的二分查找(返回index和val)
A = [1, 2, 4, 4, 4, 6, 7]
B = [1, 3, 3, 3, 3, 3]
def bin_search(val, l, left=None, right=None):
if left is None or right is None:
left, right = 0, len(l)-1
# 终止条件
if left > right:
return -1
mid = (left + right) // 2
if l[mid] > val:
return bin_search(val, l, left, mid-1)
elif l[mid] < val:
return bin_search(val, l, mid+1, right)
else:
return mid, val
print(bin_search(4, A))
改动后的二分查找
思路:
在找左边界的时候:if left > right: return left
if l(mid) >= val: (left, mid-1)
elif l(mid) <= val: (mid+1, right)
找右边界思路同上
def bin_search_l(val, l, left=None, right=None):
if left is None or right is None:
left, right = 0, len(l) - 1
if left > right: return left
mid = (left + right) // 2
if l[mid] >= val:
return bin_search_l(val, l, left, mid-1)
elif l[mid] < val:
return bin_search_l(val, l, mid+1, right)
def bin_search_r(val, l, left=None, right=None):
if left is None or right is None:
left, right = 0, len(l) - 1
if left > right: return right
mid = (left + right) // 2
if l[mid] > val:
return bin_search_r(val, l, left, mid-1)
elif l[mid] <= val:
return bin_search_r(val, l, mid+1, right)
def bin_search(val, l):
left, right = bin_search_l(val, l), bin_search_r(val, l)
if left > right:
return -1
else:
return left, right
if __name__ == '__main__':
A = [1, 4, 4, 4, 4, 4, 6, 6]
print("Input:", A)
print("Output:", bin_search(4, A))
# Input: [1, 4, 4, 4, 4, 4, 6, 6]
# Output: (1, 5)
总结
看起来算法好像很复杂, 但核心的思想其实就那么几句伪代码.
还是那句永恒不变的真理: 先想请思路再下笔!
几个月后重写的一个
def binary_search_l(l, v):
left, right = 0, len(l) - 1
while left <= right:
mid = (left + right) // 2
if l[mid] < v:
left = mid + 1
else:
right = mid - 1
if l[left] == v:
return left
def binary_search_r(l, v):
left, right = 0, len(l) - 1
while left <= right:
mid = (left + right) // 2
if l[mid] <= v:
left = mid + 1
else:
right = mid - 1
if l[right] == v:
return right
def range_search(l, v):
return binary_search_l(l, v), binary_search_r(l, v)
if __name__ == '__main__':
print(range_search(A, 7))