首先我们来回忆一下基本的二分查找,二分查找是什么呢,主要用在从一个已经排序的序列中找到给定的数字的索引。
假设数组内的元素都是不严格的升序排列,
首先写一个基本的递归法二分查找。
如果能找到就返回相应的索引,否则就返回-1。这里left和right都是包含在搜索范围内的。
def binary_search(x,arr,left,right):
if right<left:
return -1
mid=(left+right)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
return binary_search(x,arr,left,mid-1)
return binary_search(x,arr,mid+1,right)
对应的二叉搜索树👆
如果找到就会直接返回索引,找不到就会进入下面的粉(?)色方框中,返回-1。
代码也可以写成非递归的形式:
def binary_search(x,arr,left,right):
while right>=left:
mid=(left+right)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
right=mid-1
else:
left=mid+1
return -1
我们来分析一下这棵二叉搜索树的索引们,特别是未找到的索引。
如果搜索时没有找到,进入了方框中。要进入方框,那么一定是和某个与方框节点相连的圆形节点进行了比较。此时这个圆形节点就是arr[mid]。
如果是向左走,那么其left是没有变化的,也就是mid,right变为了mid-1。arr[mid]是一个比x大的数,而且是第一个比x大的数。right即mid-1是最后一个比x小的数。
同理,如果是向右走,right不变,仍然是mid,left变为了mid+1。arr[mid]是一个比x小的数,而且是最后一个比x小的数。而left代表的arr[mid+1]也就是第一个大于x的数。
那么,如果我们想求不大于x的最后一个数,也就是小于等于x的最后一个数的索引,应该怎么求呢?
def binary_search(x,arr,left,right):
if right==left:
if arr[left]<=x:
return left
return left-1
mid=(left+right)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
return binary_search(x,arr,left,mid-1)
return binary_search(x,arr,mid+1,right)
但是这么写就不是非常有水平,毕竟循环条件不是在right==left时终止的嘛!
所以再回去看看原来的分析:
当我们要求小于等于x的最后一个数时,如果是走了左边的分支,说明arr[mid]>x,而我们应该返回的小于等于x的最后一个数应该不是mid,而是mid-1,left=原mid,right=原mid-1,我们应该返回的数就是mid,所以返回right。
如果走了右边的分支,小于等于x的最后一个数应该就是arr[mid],那么right=原mid,left=原mid+1,返回right。
def binary_search(x,arr,left,right):
if right<left:
return right
mid=(left+right)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
return binary_search(x,arr,left,mid-1)
return binary_search(x,arr,mid+1,right)
迭代形式如下:
def binary_search(x,arr,left,right):
while right>=left:
mid=(left+right)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
right=mid-1
else:
left=mid+1
return right
同理我们也可以写出求不小于x的第一个数,返回left即可~
def binary_search(x,arr,left,right):
while right>=left:
mid=(left+right)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
right=mid-1
else:
left=mid+1
return left
也有人会更喜欢前闭后开的表示方式,但个人觉得那样会比较容易混淆,所以喜欢前闭后闭这样写一些。
前闭后开普通:
def binary_search(x,arr,left,right):
while right>left:
mid=(left+right)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
right=mid
else:
left=mid+1
return -1
前闭后开查询大于等于x的第一个数。查询到最后一个方块时,如果是左方块,left=前mid,right也等于前mid,要返回的数也是前mid。如果是右方块,left=right=前mid+1,要返回的也是前mid+1。
def binary_search(x,arr,left,right):
while right>left:
mid=(left+right)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
right=mid
else:
left=mid+1
return left
前开后闭查询小于等于x的最后一个数。查询到最后一个方块时,如果是左方块,left=right=前mid-1,要返回的数也是前mid-1。右方块,left=right=前mid,要返回的也是前mid。
def binary_search(x,arr,left,right):
while right>left:
mid=(left+right+1)/2
if arr[mid]==x:
return mid
if x<arr[mid]:
right=mid-1
else:
left=mid
return left
为了不溢出,我们最好用
mid=left+(right-left)/2
- Post link: http://yoursite.com/2020/09/25/various-binary-search/
- Copyright Notice: All articles in this blog are licensed under unless stating additionally.