二分查找没有变形
经典的二分查找相信大家都已烂熟于心,但是经典的二分查找只给出了true或false的回答,也就是是否存在,存在返回true,不存在则返回false。但是,基于经典的二分查找却会有很多的变式,每一种变式可能都会有相应的代码解决方案,本文之所以命名为"二分查找没有变形",是因为我找到了一个统一的思路,无论哪种变形都能根据此思路写出相应的代码。
本文默认约束
是从数组中查找某一个特定的元素(不一定是等于该目标元素,可能是大于等于目标元素,或者小于等于目标元素),并且数组从小到大排序; 左右边界分别为start=0,end=len(data)-1,也就是说查找的区间是[start,end],左右都是闭区间; 为了代码简洁,本文使用的是python语言,但是我并没有使用python语言特有的函数,只要有其他任何语言基础,都能够很方便转成自己需要的语言; 在求mid时,可能会有溢出,或者考虑到效率问题,本文重点在于统一二分查找的思路,对于这种细枝末节的优化问题不予讨论。 上述所有约束都是为了描述方便,我相信只要你看懂了我的思路,可以很方便地举一反三,比如对于约束1,数组默认从小到大排序,那么只要你看懂了我的思路,数组如果是从大到小排序也是很快能够掌握的。
思路分析
以一个具体的例子来说明我的思路 在一个从小到大排序的数组data中,找到第一个大于等于key的元素的下标,如果不存在则返回-1.题目来了,那么如何写出相应的代码呢。 步骤详解一(while 循环部分,注意循环的条件是start<=end):
先写出不满足条件的情况。题目中说"找到第一个大于等于key"的,先不管第一个还是最后一个,重点在于"大于等于key",所以,第一个判断条件就是if data[mid]<key
,此时的决策因该是start=mid+1
。注意这里不满足的条件只有一个,如果要求"找到第一个等于key的元素",那么不满足条件就有两种情况,所以要写两个if判断。 能够走到第二步说明已经满足了条件,也就是说当前data[mid]>=key
,那么此时应该如何决策呢,这就是本文的重点,此时的决策依旧比较困难,还应该做一步判断,if mid==0 or data[mid-1]<key
,这步判断是什么意思呢,就是解决题目中所要求的"第一个大于等于key"的问题,如果当前mid==0
或者data[mid-1]<key
都说明mid是data中"第一个大于等于key"的元素的下标,此时的决策是return mid
。 能够走到这里说明第二步中if mid==0 or data[mid-1]<key
不满足,也就是说mid>0 and data[mid-1]>=key
,那么说明什么问题?说明在mid之前,还有大于等于key的元素,那么此时的决策应该是end=mid-1
步骤详解二(跳出while循环部分) 能够走到这里一定是start>end,也就是说数组中没有找到这样的元素,直接返回-1。 完整的代码如下
def binary_search(data, start, end, key):
while start <= end:
mid = (start+end)//2
if data[mid] < key:
start = mid + 1
elif mid == 0 or data[mid-1] < key:
return mid
else:
end=mid-1
return -1
有了这个基础,那么写其他的变形就易如反掌了,如果还不太清楚,欢迎讨论。