文章目录
二分查找python
什么是查找?
查找是在一组数据中寻找特定元素
的过程。它在计算机科学和实际编程中扮演着重要的角色,因为我们经常需要从大量数据中快速找到所需的信息。
什么是二分查找?
二分查找是一种高效的查找算法,特别适用于有序列表
。它通过将查找范围逐渐缩小一半来快速定位目标元素,从而大大减少了查找的时间复杂度。
时间复杂度对于二分查找算法来说,其时间复杂度为
O(log n)
,其中 n 表示数据集中元素的数量。
这是因为在每一步迭代中,二分查找都将查找范围缩小一半。假设初始数据集中有 n 个元素,经过一次迭代后剩下 n/2 个元素,再经过一次迭代后剩下 n/4 个元素,以此类推。经过 k 次迭代后,剩下的元素个数将是 n/(2^k)。
当剩下的元素个数变为 1 时,查找结束。即 n/(2^k) = 1,解这个方程可以得到 k = log2(n),因此算法最多需要 log2(n) 步来找到目标元素。这就是为什么二分查找的时间复杂度是 O(log n)。
需要注意的是,这里的 log 是以 2 为底的对数。这种时间复杂度的特点是,随着数据量的增加,算法的性能仍然能够保持较高的效率,相比于线性查找等时间复杂度较高的算法,二分查找在大规模数据集上表现更优秀。
二分查找的前提
二分查找的前提是数据必须有序。这可以是升序或降序,但必须是有序的,以便能够有效地将查找范围缩小。
二分查找的图解
二分查找的代码实现
如何获取中间值
中间值的获取一般有两种方法:
- mid = (left + right)// 2
这种方式是将左边界left
和右边界right
的和除以 2 来计算中间位置。这种方式的优点是简单明了,直观易懂。但是在特别大的数据范围下,left + right 可能会导致整数溢出
问题,因此在实际使用时需要注意。 - mid = left + (right - left) // 2
这种方式是将右边界right
减去左边界left
的差再除以 2,然后加上左边界 left,从而计算中间位置。这种方式避免了整数溢出的问题,因为它是在较小的数值范围内进行运算。这种方式更安全,尤其在处理大范围的数据时更为可靠。
递归版
二分查找可以用递归实现。递归版本的代码会更简洁,但需要注意递归深度可能会影响性能。
def binary_search(nums,tagret,l ,r ):
'''
递归版的二分查找 # 大前提 :nums必须是有序数组
:param nums: 带查找的数组
:param tagret: 带查找的目标值
:param l: 左指针
:param r: 右指针
:return: 查找到的索引 没找到 返回-1
'''
if l>r:
return -1 # 表示不知道
mid = l + (r - l) // 2
if nums[mid] == tagret:
return mid
elif nums[mid] > tagret:
return binary_search(nums,tagret,l,mid - 1)
else:
return binary_search(nums, tagret, mid + 1, r)
nums = [1,2,2,2,2,2,3,4,5,6,7,8,9,10]
print(binary_search(nums,2,0,len(nums)-1))
循环版
def binary_search(nums,tagret,l ,r ):
'''
:param nums: 查找的序列
:param tagret: 带查找值
:param l: 查找的左边界
:param r: 查找的右边界
:return: 返回元素所在的索引位置 否则 返回-1
'''
while l <= r:
mid = l + (r - l) // 2
if nums[mid] == tagret:
return mid
elif nums[mid] > tagret:
r = mid - 1
else:
l = mid + 1
return -1
nums = [1, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(binary_search(nums, 2, 0, len(nums) - 1))
二分查找进阶
如何查找多个tagret的边界索引?
边界值的特点如下:
左侧二分查找
方法1
def searchLeft(nums, target):
# 左边界需要满足两个条件:
# 1. 他的值为target
# 2. 他的左边元素小于他,或者他的下标为0
left, right = 0, len(nums)-1
while left <= right:
mid = (right-left)//2 + left
if nums[mid] == target:
if mid == 0 or nums[mid-1] < target:
return mid
else:
right = mid - 1
elif nums[mid] > target:
right = mid - 1
else:
left = mid + 1
return -1
作者:Qi_654321
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/solutions/2362521/liang-ci-er-fen-fa-cha-zhao-zuo-you-bian-hwhz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法2:
def findleft(nums,tagret):
l = 0
r = len(nums)-1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] < tagret:
l = mid + 1
else: # nums[mid] >= tagret
r = mid - 1
return l
nums = [3,3,3,3,3,3,4,5,6]
print(findleft(nums,3))
右侧二分查找
方法1:
该方法简介明了,可读性较强!
def searchRight(nums, target):
# 右边界需要满足两个条件:
# 1. 他的值为target
# 2. 他的右边元素大于他,或者他的下标为len(nums)-1
left, right = 0, len(nums)-1
while left <= right:
mid = (right-left)//2 + left
if nums[mid] == target:
if mid == len(nums)-1 or nums[mid+1] > target:
return mid
else:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
left = mid + 1
return -1
作者:Qi_654321
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/solutions/2362521/liang-ci-er-fen-fa-cha-zhao-zuo-you-bian-hwhz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法2:
def findright(nums,tagret):
l = 0
r = len(nums)-1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] <= tagret:
l = mid + 1
else: # nums[mid] >= tagret
r = mid - 1
return r
nums = [3,3,3,3,3,3,4,5,6]
print(findright(nums,3))
方法2 的弊端
无法查找不在序列中的元素索引
但是当程序需要同时得到左端点和右端点的索引值的时候,同时调用两个函数,最终的结果必定会左端点>右端点
'''
@Time : 2023-08-26 11:37
@Author : TAGRENLA
@File : 直接查左右的二分.py
'''
def findleft(nums,tagret):
l = 0
r = len(nums)-1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] < tagret:
l = mid + 1
else: # nums[mid] >= tagret
r = mid - 1
return l
def findright(nums,tagret):
l = 0
r = len(nums)-1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] <= tagret:
l = mid + 1
else: # nums[mid] >= tagret
r = mid - 1
return r
nums = [11,22,33,44,55,66,77,88]
tagret = 12
print(findleft(nums,tagret=tagret))
print(findright(nums,tagret=tagret))
print(findleft(nums,tagret=tagret) > findright(nums,tagret=tagret))
1
0
True
本文创作来源
本文创作来源:力扣34:在排序数组中查找元素的第一个和最后一个位置
# 博主解题源代码
class Solution:
def searchRange(self,nums, target):
def findLeft(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
else: # nums[mid] >= tagret:
right = mid - 1
return left
def findRight(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] <= target:
left = mid + 1
else: # nums[mid] >= tagret:
right = mid - 1
return right
left = findLeft(nums, target)
right = findRight(nums, target)
if left <= right:
return [left, right]
else:
return [-1, -1]