彻底搞懂二分查找

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 区别之分

字符串匹配,一文彻底搞懂

彻底搞懂JavaScript中的作用域和闭包

Leader笑着对我说:孩子10分钟搞懂二分查找,不然世界这么大你就得去看看了

还不懂MySQL索引?这1次彻底搞懂B+树和B-树

一文彻底搞懂linux全局环境变量生效顺序