文章目录
前言
【注1】:本节二分查找都要求有序,我们规定都是按照从小到大的顺序
一、二分查找(nums中有序无重复元素)
二分查找最基本的对应下面这个leetcode题:704.二分查找
二分查找(binary search)是一种基于分治策略的高效搜索算法。它利用数据的有序性,每轮缩小一半搜索范围,直至找到目标元素或搜索区间为空为止。
- 问题:给定一个长度为 n n n的数组 nums ,元素按从小到大的顺序排列且不重复。请查找并返回元素 target 在该数组中的索引。若数组不包含该元素,则返回 − 1 -1 −1。示例如下图所示。
1 双闭区间写法
下面给出算法流程:我们同样采用闭区间方式
- step1:我们先初始化指针 l e f t = 0 left=0 left=0和 r i g h t = n − 1 right=n-1 right=n−1 ,分别指向数组首元素和尾元素,代表搜索区间 [ 0 , n − 1 ] [0,n-1] [0,n−1] 。请注意,中括号表示闭区间,其包含边界值本身。
- step2: while left <= right:循环
- 计算中点索引 : mid = left + (right-left)//2
- 判断 nums[mid] 和 target 的大小关系,分为以下三种情况。
(a)当 nums[mid] < target 时,说明 target 在区间[mid+1,right]中,因此执行left=mid+1
(b)当 nums[mid] > target 时,说明 target 在区间[left,mid-1]中,因此执行right=mid-1
(c)当 nums[mid] = target 时,说明找到 target ,因此返回索引 mid。
- step3:若数组不包含目标元素,搜索区间最终会缩小为空。此时返回 -1
def binary_search(nums:List[int],target:int) -> int:
'''二分查找,双闭区间'''
# 初始化双闭区间 [0, n-1] ,即 left, right 分别指向数组首元素、尾元素
size = len(nums)
left, right = 0, size-1
# 循环,当搜索区间为空时跳出(当 left > right 时为空,表明越界了)
while left <= right:
# 防止大数相加溢出,使用 left + (right-left)//2
mid = left + (right-left)//2 # 计算中间索引
if target == nums[mid]:
return mid
elif target < nums[mid]:
# 此情况说明 target 在区间 [left, mid-1] 中
right = mid - 1
elif target > nums[mid]:
# 此情况说明 target 在区间 [mid+1, right] 中
left = mid + 1
return -1
if __name__ == "__main__":
target = 6
nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
# 二分查找(双闭区间)
index = binary_search(nums, target)
print("目标元素 6 的索引 = ", index) # 2
- 算法复杂度分析:
- 时间复杂度为 O ( l o g n ) O(logn) O(logn):在二分循环中,区间每轮缩小一半,因此循环次数为 O ( l o g 2 n ) O(log_2n) O(log2n) 。
- 空间复杂度为 O ( 1 ) O(1) O(1) :指针left和 right使用常数大小空间。
2 左开右闭区间写法
补充:右开区间写法(左闭右开)
上面是完全闭区间的写法,方便对比这里也给出开区间的写法
def binary_search2(nums:List[int],target:int) -> int:
'''二分查找,左闭右开'''
size = len(nums)
left, right = 0, size
while left < right:
mid = left + (right - left