本文已参与「新人创作礼」活动,一起开启掘金创作之路
一、二分查找 704
leetcode-cn.com/problems/bi… 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
1、分析
本题为最基本的二分查找问题,选择双闭区间,左右收缩区间,找到目标即可返回。具体细节见代码
2、代码
class Solution:
def search(self, nums: List[int], target: int) -> int:
left,right = 0,len(nums)-1 #双闭区间
while left<=right: #由于是双闭区间,决定了当退出循环的条件是left>right的时候,如果不带等号可能会遗漏
mid = left+(right-left)//2 #可以防止left+right过大导致加和溢出问题
if nums[mid]<target:
left = mid+1
elif nums[mid]>target:
right = mid - 1
elif nums[mid] == target: #找到目标即可返回
return mid
return -1
二、在排序数组中查找元素的第一个和最后一个位置
leetcode-cn.com/problems/fi… 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
1、分析
首先看到升序排列,事件复杂度 O(log n)想到二分查找。但是这里找的是一个左右边界。所以不是找到就立刻返回,而是要逐步收缩区间。 为和第一题统一,这里也采用了双闭区间。具体细节见代码及注释。
2、代码
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
left,right = 0,len(nums)-1 #这一部分是找左边界
res = []
while left<=right: #可以看到和第一题的基础查找的区别在于相等的时候
mid = left+(right-left)//2
if nums[mid]<target:
left = mid+1
elif nums[mid]>target:
right = mid-1
elif nums[mid] == target: #保证边界向左收缩
right = mid-1
if left>= len(nums) or nums[left]!=target: #判断是否越界
return [-1,-1]
res.append(left)
left,right = 0,len(nums)-1 #这部分是找右边界
while left<=right:
mid = left+(right-left)//2
if nums[mid]<target:
left = mid+1
elif nums[mid]>target:
right = mid-1
elif nums[mid] == target: #保证边界向右收缩
left = mid+1
if right<0 or nums[right]!=target: #越界判断
return [-1,-1]
res.append(right)
return res
三、总结
可以看见上述的模板,将搜索区间都统一成了左闭右闭区间,在循环条件处都是小于等于号,然后在比较目标值与nums[mid]的时候,只有在相等的时候语句有所变化。如果是找左边界,则保证整体向左收紧,若是找右区间,则保证整体区间向右收紧即可。
另外,在找左右区间的时候,在退出循环后需要进行越界判断,由于在循环中并没有判断最终的边界值是否为target,因此也要在后续一并判断。
二分查找的相关细节如上所诉,统一好区间,然后稍微修改相等时的语句即可!