题目
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
暴力枚举
for循环遍历数组元素,挨个判断。时间复杂度O(n)
def search(nums, target) -> int:
for i,num in enumerate(nums):
if num == target: return i
return -1
二分法
二分法,适用于这种有序数组的查找。
二分法的思想,就是每次取中间的值与target比较,然后缩小范围再取中间的值...:
- 如果中间值<target,就收缩left
- 如果中间值>target,就收缩right
如果中间值=target,需要分情况讨论
- 如果数组是[1,2,3,4]这种有序且不重复,就直接找到了
- 如果数组是其他情况,比如有重复,部分有序,部分有序且有重复,就需要考虑左右边界,因为数组中可能有多个等于target的数,需要找最左侧的或是最右侧的
二分法时间复杂度O(logn),n/2^k=1,k=logn
标准二分,数组有序无重复元素
[1,2,3,4,5],数组有序且无重复元素
while循环实现
def search(nums, target) -> int:
left,right = 0, len(nums)-1
while left <= right:
mid = (left+right)//2
if nums[mid]==target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
递归实现
def search(nums,target) -> int:
def searchInternally(nums,target,left,right):
if left<=right:
mid = (left+right)//2
if nums[mid]==target:
return mid
elif nums[mid]<target:
return searchInternally(nums,target,mid+1,right)
else:
return searchInternally(nums,target,left,mid-1)
else:
return -1
return searchInternally(nums,target,0,len(nums)-1)
考虑边界
数组有重复元素:[1,2,2,2,3]
数组部分有序:[4,5,6,1,2,3]
# 查找左边界
def search(nums,target):
left,right = 0, len(nums)-1
while left<right:
mid = (left+right)//2
# 因为有重复元素,并且寻找左边界,所以当匹配到target后,收缩right,继续向左查找
if nums[mid]==target:
right = mid
if nums[mid] > target:
right = mid -1
if nums[mid] < target:
left = mid +1
return left if nums[left]==target else -1
# 查找右边界
def search(nums, target):
left, right = 0, len(nums) - 1
while left < right:
# 因为查找右边界,mid原本的计算是向下取整,导致靠左,所以+1靠右
mid = (left + right) // 2 + 1
if nums[mid] == target:
# 收缩left,继续向右查找
left = mid
if nums[mid] > target:
right = mid - 1
if nums[mid] < target:
left = mid + 1
return right if nums[right] == target else -1
数组部分有序,且重复:[1,2,2,3,1,2,2,3]
# 查找左边界
def search(nums, target):
left,right = 0, len(nums)-1
while left < right:
mid = (left+right)//2
if nums[mid]==target:
right = mid
if nums[mid]>target:
# 因为数组部分有序且重复,mid大于target时,
# 有可能mid左侧没有目标值,在右侧有,因此收缩right时只能一点一点收缩
right = right - 1
if nums[mid]<target:
left = mid + 1
return left if nums[left]==target else -1
二分法中间值溢出
(left+right)//2虽然能计算出中间值,但是这种简单的计算方式可能会存在整数溢出的可能。
虽然,在python3中,int不会溢出。
比如,假设当前整数的最大范围是20,如果left=10,right=20,此时left+right已经超过最大范围,就会溢出。
更科学的计算方法是:(right-left)//2+left
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。