#代码随想录
704 二分查找
题目描述:
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
- 你可以假设
nums
中的所有元素是不重复的。 n
将在[1, 10000]
之间。nums
的每个元素都将在[-9999, 9999]
之间。
本题思路很简单,标题已经写清楚了是二分查找,同时可以看到几个关键词: 升序 表明数组是已经排序好的;同时nums中所有的元素是不重复的,说明查找的对象只有一个。
符合二分查找的条件:
1.用于查找的内容逻辑上是需要有序的
2.查找的数量只能是一个而不是多个
明确了解题的算法,接下来就是实际操作部分
注意:二分查找有三种写法,与定义的区间有关
分别为左闭右闭、左闭右开、左开右闭。其中前两种用的比较多,第三种很少见
本文采取第一种写法,即将区间看作是左闭右闭的
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
middle = left + (right - left) // 2
if nums[middle] < target:
left = middle + 1
elif nums[middle] > target:
right = middle -1
else:
return middle
return -1
代码中有个小细节:
在计算middle的值时,没有使用:
middle = (left + right)//2
而是使用:
middle = left + (right - left) // 2
是为了防止出现int 类型相加出现的内存溢出 如两个特别大的数相加,就有可能超出int类型的存储范围,而使用(right - left)// 2 将两个特别大的数相减 用减法处理了内存溢出的可能性
使用左闭右闭的区间相对来说比较简单,而若是要使用左闭右开的区间代码如下:
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)
while left < right:
middle = left + (right - left) // 2
if nums[middle] < target:
left = middle + 1
elif nums[middle] > target:
right = middle
else:
return middle
return -1
只是修改了while循环中的判断条件 和 循环内部 left 和right 的更新条件
那么如何进行判断 到底是小于等于还是小于呢?重点在于 区间合法性 判断
举例: 第二种写法定义的是左闭右开区间 如 [ left ,right ) 若是小于等于 那么就存在left == right这样一种可能性 但实际上是不合法的
同理 在更新left 的取值时 由于middle 本身已经包含在区间中, 已经判断过不符合题意了, 故middle + 1 即可 , 但在right 的更新中 , middle 并不包含在right区间里(因为是右开区间)所以在下一次更新中还要再次判断middle是否是目标值 所以使用 right = middle
关于二分查找的解法就是上述内容
但实际上,本题对时间复杂度并没有硬性要求
使用O(n)的算法也是可以通过的:
class Solution:
def search(self, nums: List[int], target: int) -> int:
for i in nums:
if i == target:
return nums.index(i)
return -1
并且速度并没有慢太多,可能是由于数据集比较少