库函数实现二分查找
对于非降序数组nums和需要查找的目标值target,可以使用Python3中的库函数bisect进行查找。有三种用法,输入都是一个列表和需要查找的元素:
index1 = bisect(nums, target)
index2 = bisect_left(nums, target)
index3 = bisec_right(nums, target)
其中bisect()和bisect_right()两者是等价的。总共有如下三种情况:
- target不在nums中:三者返回值相同,都是插入后使原数组有序的位置
import bisect
nums = [1,5,9,13,17]
index1 = bisect.bisect(nums,7)
index2 = bisect.bisect_left(nums,7)
index3 = bisect.bisect_right(nums,7)
print("index1 = {}, index2 = {}, index3 = {}".format(index1, index2, index3))
#输出 index1 = 2, index2 = 2, index3 = 2
- target在nums中唯一:左等、右加一
如果列表中只有一个元素等于target,那么bisect_left(nums, target)的值是target在nums中的索引,nums[index2] = target。而bisec_right(nums, target)的值是target在nums中的索引加1,ls[index3] > target。
import bisect
nums = [1,5,9,13,17]
index1 = bisect.bisect(nums,9)
index2 = bisect.bisect_left(nums,9)
index3 = bisect.bisect_right(nums,9)
print("index1 = {}, index2 = {}, index3 = {}".format(index1, index2, index3))
# 输出: index1 = 3, index2 = 2, index3 = 3
- target在nums中不唯一:左等左、右等右加一
如果列表中存在多个元素等于target,那么bisect_left(nums, target)返回最左边的那个索引,此时nums[index2] = target。bisec_right(nums, target)返回最右边的那个索引加1,此时ls[index3] > target。
import bisect
nums = [1,5,5,5,17]
index1 = bisect.bisect(nums,5)
index2 = bisect.bisect_left(nums,5)
index3 = bisect.bisect_right(nums,5)
print("index1 = {}, index2 = {}, index3 = {}".format(index1, index2, index3))
# 输出 index1 = 4, index2 = 1, index3 = 4
注意事项:
- 数组必须是非降序排列好的,才能使用上述库函数
- 刷题时可能要自己实现特殊功能,因此还需要知道上述库函数是如何实现的。
- 注意区分三者的差别:中和右等价,右总是加一。
代码实现库函数功能
def my_bisect(nums, k):
#查找元素是否存在,存在则返回某一个索引,不存在则返回-1
i, j = 0, len(nums)
while i < j:
mid = i + ((j-i) >> 1)
if nums[mid] > k:
j = mid
elif nums[mid] < k:
i = mid+1
else:
return i
return -1
def bisect_left(nums, k):
# 返回最左边的索引
i, j = 0, len(nums)
while i < j:
mid = i + ((j-i) >> 1)
if nums[mid] >= k:
j = mid
else:
i = mid+1
return i
def bisect_right(nums, k):
# 返回最右边的索引加一
i, j = 0, len(nums)
while i < j:
mid = i + ((j-i) >> 1)
if nums[mid] <= k:
i = mid+1
else:
j = mid
return i
二分法易错点:区间概念不清!
二分法容易出错主要是由于区间问题分不清楚,这里引用代码随想录中的总结,一共有两种区间方式:左闭右闭和左闭右开。一般使用左闭右开会更好。两者代码如下:
class Solution:
#左闭右闭
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
middle = (left + right) // 2
if nums[middle] < target:
left = middle + 1
elif nums[middle] > target:
right = middle - 1
else:
return middle
return -1
class Solution:
#左闭右开
def search(self, nums: List[int], target: int) -> int:
left,right =0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid+1
elif nums[mid] > target:
right = mid
else:
return mid
return -1
参考文献: