一、二分查找的介绍
1、二分查找的概念
二分查找(Binary Search)是一种在有序无重复数组中查找特定元素的搜索算法。
二分查找的基本思想是:
将整个有序数组分成前、中、后三个部分;
先比较中间部分元素是否等于要查找的元素,如果相等则查找成功;
否则如果中间部分元素大于要查找的元素,则在前半部分元素中继续查找;
如果中间部分元素小于要查找的元素,则在后半部分元素中继续查找。
重复以上过程,直到找到要查找的元素,或者查找范围为空。
2、二分查找的例子
如图,假设有一个长度为4的有序无重复元素数组,初始时,数组的左指针left指向数组第一个元素1,即下标为0的元素,数组的右指针right指向数组最后一个元素12,即下标为数组长度-1的元素,数组的中间指针mid指向中间的元素6,即下标为数组长度之和/2的元素
![](https://img-blog.csdnimg.cn/img_convert/6848c0f83476ee8a438e3e357bc5c046.png)
假设要查找的数字是6,则此时mid正是要查找的值的下标,直接返回mid下标值,表示找到了这个元素;假设要查找的数字是4,此时mid指向的值是6,显然是大于4的,则需要调整的是右指针right,将右指针的范围改为mid前面(至于right=mid还是mid-1涉及到区间的选择,左闭右开还是左闭右闭);假设要查找的数字是9,此时mid指向的值是6,显然是小于12的,则需要调整的是左指针left,将左指针的范围改为mid之后(至于left=mid还是mid+1也是涉及到了区间的选择)
3、二分查找的特点
被查找的需要是有序数组
二分查找的时间复杂度为O(log n)。
二、二分查找的代码
1、代码思路
二分查找的区间有左闭右开和左闭右闭,首先是以左闭右闭为例的代码步骤:
定义左右指针,left和right,初始化left=0,right=len(nums)-1,这里len(nums)-1指的是数组长度-1
定义while循环,当左指针小于等于右指针,即left<=right时(在左闭右闭的区间中,left可以等于right,如[1,1]是合理的只有一个元素的区间),则进入区间的判断
定义mid,指向中间值,mid=left+(right-left)// 2
接着进入值的判断,
如果指向的值等于要查找的值,即nums[mid]==target,说明指向的值就是要查找的值,直接返回mid的值,return mid
如果指向的值小于要查找的值,即nums[mid]<target,说明需要调整左区间的范围位于mid之后,left=mid+1,为什么+1?此时nums[mid]已经小于target了,说明中间的值肯定是小于要查找的值的,区间肯定是在这个值之后的。
最后的情况肯定是剩下指向的值大于要查找的值了,即nums[mid]>target,说明要调整右区间,right=mid-1,-1的原理和+1是相似的,区间肯定是位于这个值之前的。
2、注意事项
二分查找的代码需要注意几个点:
二分查找的数组需要是有序的,如果原数组不是有序的,可以用sort()排序后进行查找
注意middle得取值溢出得情况: 如果left和right都是两个极大int类型 ,那可能存在溢出得情况。所以要注意 middle = left + (right-left)//2
如果没有查到需要的值,返回其他的值(如-1,看题目具体需要)
3、具体代码参考
![](https://img-blog.csdnimg.cn/img_convert/1d38712b264980a9f772fe3d034149ba.png)
三、具体题目
题目1
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。原题:704. Binary Search - 力扣(Leetcode)
解题思路:这个题需要注意的是有序数组,可以直接使用二分查找来完成。
解题代码:
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
left = 0
right = len(nums) - 1
while(left <= right):
middle = left + (right - left) // 2
if nums[middle] > target:
right = middle - 1
elif nums[middle] < target:
left = middle + 1
else:
return middle
return -1
题目2
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。35. 搜索插入位置 - 力扣(Leetcode)
解题思路:这道题给了排序数组且需要复杂度为O(log n)的算法,可以考虑二分查找,基本上和题目1是相同的,但是返回值有所不同,这里要返回的是需要插入的位置
解题步骤:
定义左右指针,初始化
进入循环,定义中间值指针,判断中间值与目标值,如果相等直接返回(因为题目中说如果相同直接返回下标值);中间值大于目标值,说明要插入到中间值以后的位置,调整左指针,反之调整右指针;如果循环结束没有找到目标值,说明要插入到最后。
最终都是返回left,即第一个大于等于目标值的元素。
解题代码:
class Solution(object):
def searchInsert(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
left,right = 0,len(nums)-1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
right = mid - 1
else:
left = mid + 1
return left