什么是查找算法
对于一个数组或者是链表,如果想要知道其中是否包含我们需要的target值,可以进行一次线性的查找,每个元素逐个比对来确定是否包含target,这就是一个最简单的查找算法。时间复杂度为O(N)
。
什么是二分查找算法
假设给定的数组中的元素是一个有序的状态,比如是单调不递减
或者是单调不递增
的状态,对于这种情况可以使用二分查找来完成。即二分查找需要满足两个条件:
- 数组存储
- 元素有序,单调性
二分查找的具体过程
以数组[0,1,2,3,4,5,6]
为例。我们需要查找的目标值target是为2,具体的查找过程如下图所示:
- 首先要查找的区间是
【0,6】
,即区间起始位置low=0,区间结束位置high=6。
- 需要比较区间中间元素和target的大小。中间元素是3,很明显3大于target说明目标元素在target的左半边,瞬间将区间缩小一半。如下图所示。
- 这时面对的区间是
【0,2】
,即low=0,high=2,target = 2。同样需要比较区间中间的数和target的大小,区间中间的数是1,比target小。说明target在区间的右半部分。区间同样缩小一半,如下图所示:
- 这时区间已经变成了
【2,2】
,low=2,high=2,target= 2同样按照上面的方式计算,找出区间最中间的那个元素,这时中间的元素是2,和目标值target相等,直接返回结果即可。
上面介绍的是target存在数组中的情况,如果target不存在数组中是什么样的呢?假设target=2.5那么在区间【2,2】之前的查找过程都和上面一样,只有在最后一步时是不一样的。这时同样比较区间【2,2】的中间值和target比较,发现target比该值大,那么就要将low继续往右移动,这时发现 low>high ,说明该元素不在该数组中,可以终止查找了。
二分查找代码
def binary(nums,target):
low = 0
high = len(nums) -1
while low <= high:
mid = (low + high) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
high = mid -1
else:
low = mid + 1
return -1
print(binary([0,1,2,3,4,5,6],2))
print(binary([0,1,2,3,4,5,6],2.5))
一些其他的情况
上面的代码针对的情况是如果target在数组中,那么返回target在数组中的下标;如果target不在数组中则返回-1。对于直接返回-1的这种情况其实是对信息的一种浪费。我们可以返回数组比元素比target小且最接近于target的那个下标。比如数组[0,1,2,3,4,5,6]
,target = 2.5,这时返回值就应该是2。
返回比该元素大且最接近该元素的下标
def binary(nums,target):
low = 0
high = len(nums) -1
while low <= high:
mid = (low + high) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
high = mid -1
else:
low = mid + 1
return high + 1
print(binary([0,1,2,3,4,5,6],2))
print(binary([0,1,2,3,4,5,6],2.5))
返回比该元素小且最接近于该元素的下标
def binary(nums,target):
low = 0
high = len(nums) -1
while low <= high:
mid = (low + high) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
high = mid -1
else:
low = mid + 1
return low-1
print(binary([0,1,2,3,4,5,6],2))
print(binary([0,1,2,3,4,5,6],2.5))
比如leetcode 354 俄罗斯套娃信封问题中就需要用到返回比该元素小且最接近于该元素的下标
这种二分查找方式,有兴趣的朋友的可以尝试一下。虽然一个基础版本的二分查找代码非常好写,但是对于二分查找的变形的问题还是要深入理解二分查找过程的具体细节,才能在实际中熟练的应用。