二分查找(Python)
寻找一个数
def binarysearch(nums, target):
n = len(nums)
if n == 0:
return -1
left = 0
right = n - 1
while left <= right:
mid = (left + right) // 2
if target == nums[mid]:
return mid
elif target > nums[mid]:
left = mid + 1
else:
right = mid - 1
return -1
寻找左侧边界
搜索区间:[left, right]
使右边界一直收敛到所找target的左侧边界之前,而结束条件是left==right+1,因此返回left的值
left有可能大于len(nums)-1,但不会小于0
def _find_left_bound(nums, target):
"""
寻找target的左边界
"""
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1
else:
# target <= nums[mid]
right = mid - 1
# nums[left]以左的元素全部小于target,nums[right]以右的元素全部大于或等于target
# 终止时 left = right + 1,易知此时left就是target的左边界
# 由于right的最大值能取到len(nums)-1,因此left有可能超过len(nums)-1,但left不可能小于0
# target还可能在nums中不存在
if left < len(nums) - 1 and nums[left] == target:
return left
else:
return -1
实验结果:
>> leftbound([1, 2, 2, 3, 5, 7, 9], 2)
>> result = 1 left = 1
当target不存在,left为首个比target大的值
>> leftbound([1, 3, 5, 7, 9], 2)
>> result = -1 left = 1
当target小于数组最小值,left为0
>> leftbound([1, 3, 5, 7, 9], 0)
>> result = -1 left = 0
当target大于数组最小值,left为len(nums)
>> leftbound([1, 3, 5, 7, 9], 10)
>> result = -1 left = 5
寻找右侧边界
def _find_right_bound(nums, target):
"""
寻找target的右边界
"""
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if target < nums[mid]:
right = mid - 1
else:
# nums[mid] <= target
left = mid + 1
# nums[right]以右的元素全部大于target,nums[left]以左的元素全部小于或等于target
# 终止时 left = right + 1,易知此时right就是target的右边界
# 由于left的最小值能取到0,因此right有可能小于0,但不可能超过len(nums)-1
if right >= 0 and nums[right] == target:
return right
else:
return -1
寻找左右边界问题的本质是找到一个位置,使所有小于target的值(且仅有这些值)都在左边(找左边界),或者所有大于target的值(且仅有这些值)都在右边(找右边界)
时间复杂度O(logn)
相当于从一颗二叉搜索树的根走到叶子节点,二叉搜索树的高度为O(logn),因此时间复杂度为O(logn)