彻底搞懂二分查找
Posted 焦虑的码农
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了彻底搞懂二分查找相关的知识,希望对你有一定的参考价值。
本文通过几道经典题目带你彻底搞懂二分查找的套路。准备好了吗?
首先我们先来了解下二分查找基本概念:
二分查找是一种在每次比较之后将查找空间一分为二的算法。每次需要查找集合中的索引或元素时,都应该考虑二分查找。如果集合是无序的,我们可以在应用二分查找之前先对其进行排序。
二分查找一般由三个主要部分组成:
预处理 —— 如果集合未排序,则进行排序。
二分查找 —— 使用循环或递归在每次比较后将查找空间划分为两半。
后处理 —— 在剩余空间中确定可行的目标值。
1.寻找旋转排序数组中的最小值
题目解析:
如果中值 < 右值,则最小值在左半边,可以收缩右边界。
如果中值 > 右值,则最小值在右半边,可以收缩左边界。
实现代码:
def findMin(nums):
"""
:type nums: List[int]
:rtype: int
"""
left = 0
right = len(nums)-1
while left < right:
mid = (left+right)//2
#因为中值 > 右值,中值肯定不是最小值,左边界可以跨过mid
if nums[mid]>nums[right]:
left = mid+1
#因为中值 < 右值,中值也可能是最小值,右边界只能取到mid处
else:
right = mid
return nums[left]
2. 寻找旋转排序数组中的最小值 II
题目解析:
这道题是第1题的变种,思路跟第一题一样,唯一不同的是可能出现重复元素, 如果都是重复元素时间复杂度最坏就退化成了O(n)
实现代码:
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
left = 0
right = len(nums)-1
while left < right:
mid = (left+right)//2
if nums[mid]> nums[right]:
left = mid+1
elif nums[mid]<nums[right]:
right = mid
#因为有重复元素无法确定位置,所以右侧缩进
else:
right -=1
return nums[left]
3.搜索旋转排序数组
题目解析:
数组[0,1,2,4,5,6,7]经过旋转可能会变成下面这样:
根据图片我们可以看出来,旋转点将待搜索区间从中间一分为二,mid 一定会落在其中一个有序区间里
实现代码:
def search(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums:
return -1
left = 0
right = len(nums)-1
while left<=right:
mid = (left+right)//2
if nums[mid] == target:
return mid
#[left,mid]这个区间nums是顺序排列的,接下来就判断目标值是否在这个区间中了
if nums[mid]>=nums[right]:
if nums[left]<=target<nums[mid]:
right = mid-1
else:
left = mid+1
# [mid,right]这个区间是顺序排列的,然后判断目标值是否在这个区间中
elif nums[mid]<nums[right]:
if nums[mid]<target<=nums[right]:
left = mid+1
else:
right = mid-1
return -1
4.搜索旋转排序数组II
题目解析:
这道题是第3题的变种,思路跟第一题一样,唯一不同的是可能出现重复元素
实现代码:
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: bool
"""
left = 0
right = len(nums)-1
while left<=right:
mid = (left+right)//2
if nums[mid] ==target:
return True
#[left,mid]这个区间nums是顺序排列的,接下来就判断目标值是否在这个区间中了
if nums[mid]>nums[right]:
if nums[left]<=target<nums[mid]:
right = mid-1
else:
left=mid+1
#[mid,right]这个区间是顺序排列的,然后判断目标值是否在这个区间中
elif nums[mid]<nums[right]:
if nums[mid]<target<=nums[right]:
left = mid+1
else:
right = mid-1
else:
right-=1
return False
5.在排序数组中查找元素的第一个和最后一个位置
题目解析:
先找到等于目标的其中一个值,然后再往左右找出边界
实现代码:
def searchRange(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
left =0
right = len(nums)-1
#寻找左侧边界
while left<=right:
mid = (left+right)//2
if nums[mid]>target:
right = mid-1
elif nums[mid]<target:
left=mid+1
else:
right = mid-1
#当target比nums中所有元素都大时,left会出现索引越界
if left>=len(nums) or nums[left]!=target:
idx = -1
else:
idx = left
#寻找右侧边界
left = 0
right = len(nums)-1
while left<=right:
mid = (left+right)//2
if nums[mid]>target:
right=mid-1
elif nums[mid]<target:
left=mid+1
else:
left=mid+1
#当target比nums中所有元素都小时,right会减到负,出现索引越界
if right<0 or nums[right]!=target:
idx1=-1
else:
idx1 = right
return [idx, idx1]
让我们一起探索未知的世界~,如果对你有启发和帮助,欢迎点赞和转发。
感谢
以上是关于彻底搞懂二分查找的主要内容,如果未能解决你的问题,请参考以下文章
三分钟!彻底搞懂PostgreSQL 和 MySQL 区别之分