Leetcode 704 二分查找
题目描述:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
代码:
class Solution:
def search(self, nums: List[int], target: int) -> int:
low,high=0,len(nums)-1
while low<=high:
mid=(high-low)//2+low
num=nums[mid]
if num==target:
return mid
elif num>target:
high=mid-1
else:
low=mid+1
return -1
关于二分查找的思考:
这是一道典型的二分查找题,由题目所述,给定的输入数组nums是有序的,我们需要从中查找一个给定的target值,当然,逐个遍历不妨是一种方法,但是时间过长,不推荐采用。
这里我们可以通过寻找low与high的中间值mid,并将其与要查找的target进行对比,如果nums[mid]==target,那么一次就可以找到,并且时间复杂度为O(1),如若不相等,则可以通过比较nums[mid]与target的大小,近一步改变相应的low,high的值(若num>target,则high=mid-1;若num<target,则high=low+1),逐步缩小查找的范围,直至找到target,输出其下标。
下面,讲一讲二分查找。
什么是二分查找?
二分查找是计算机科学中最基本,最有用的算法之一,它描述了再有序集合中搜索特定值的过程,是一种在有序数组中查找特定元素的搜索算法。
二分查找中使用的术语:
目标target——要查找的值
索引index——要查找的当前位置
左、右指示符Left、Right——用来维持查找空间的指标
中间指示符mid——用以确定下一步是向左查找还是向右查找
用于二分查找的代码大同小异,整体都差不多,重点就在于边界条件的处理,到底是 while(left < right)
还是 while(left <= right)
,到底是right = middle
呢,还是要right = middle - 1
呢?
下面介绍两个模板(具有重复元素的数组也可以使用):
模板一:
int lower_bound(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize-1;
while(left < right){ # 搜索区间左闭右开[first, last)不为空
#防溢出
int mid = left + right >> 1;
if(nums[mid] >= target) right = mid;
else left = mid + 1;
}
return left; // right也行,因为[left, right)为空的时候它们重合 }
mid=(left+right)>>1的含义:
右移运算符>>,运算结果正好能对应一个整数的二分之一值,正好能代替数学上的除2运算,但是比除2运算要快。
mid=(left+right)>>1相当于mid=(left+right)/2
如果中点大于等于 target
,表示目标在左侧,数组范围应该从 [left, right]
变成 [left, mid]
,否则,就从 [left, right]
变成 [mid+1, right]
。
模板二:
int lower_bound(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize-1;
while(left < right){ # 搜索区间[first, last)不为空
# 防溢出
int mid = left + right + 1 >> 1; # 防止死循环,mid加1
if(nums[mid] <= target)
left = mid;
else right = mid - 1;
}
return right; # right也行,因为[left, right)为空的时候它们重合 }
如果中点小于等于 target
,表示目标在右侧,数组范围应该从 [left, right]
变成 [mid, right]
,否则,就从 [left, right]
变成 [left, mid+1]
。
总结:
模板一:mid=(left+right)/2,if条件判断(nums[mid]>=target),区间 [left, right]
划分成 [left, mid]
和 [mid + 1, right]
时,其更新操作是 right = mid
或者 left = mid + 1;
,计算 mid
时不需要加 1
,更适用于寻找二分的左边界。
模板二:mid=((left+right)/2)+1,if条件判断(nums[mid]<=target),区间 [left, right]
划分成 [left, mid - 1]
和 [mid, right]
时,其更新操作是 r = mid - 1
或者 l = mid;
,此时为了防止死循环,计算 mid
时需要加 1
,更适用于寻找二分的右边界。