题目:
整数数组nums按升序排列,数组中的值互不相同
在传递给函数之前,nums在预先未知的某个小标K上进行了旋转,使数组变为[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]],小标从0开始计数。例如[0,1,2,4,5,6,7]
在下标 3
处经旋转后可能变为 [4,5,6,7,0,1,2]
。
给定旋转后的数组nums和一个整数target,如果nums中存在这个目标值target,则返回它的小标,否则返回-1
方法一:二分查找
对于有序数组,可以使用二分查找的方法查找元素
这道题中,数组本身不是有序的,进行旋转后只保证了数组的局部是有序的
可以发现,将数组从中间分开成左右两部分,一定有一部分的数组是有序的,拿示例看,从 6
这个位置分开以后数组变成了 [4, 5, 6]
和 [7, 0, 1, 2]
两个部分,其中左边 [4, 5, 6]
这个部分的数组是有序的,其他也是如此。
这启示我们,可以在常规二分查找的时候查看当前mid为分割位置分割出来的两个部分[l, mid]
和 [mid + 1, r]
哪个部分是有序的,并根据有序的那个部分确定该如何改变二分查找的上下界,因为可以根据有序的部分判断出target在不在这个部分。
如果 [l, mid - 1]
是有序数组,且 target
的大小满足 [nums[l],nums[mid]),应该将搜索范围缩小至 [l, mid - 1]
,否则在 [mid + 1, r]
中寻找
如果 [mid, r] 是有序数组,且 target 的大小满足 (nums[mid+1],nums[r]],则我们应该将搜索范围缩小至 [mid + 1, r],否则在 [l, mid - 1] 中寻找。
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums: #如果输入数组为空,直接返回-1表示未找到
return -1
l,r=0,len(nums)-1 #初始化两个指针l(left)和r(right),分别指向数组的开始和结束位置
while l<=r: #开始二分查找循环,当左指针不超过右指针时继续循环
mid=(l+r)//2 #计算中间位置mid
if nums[mid]==target: #如果中间值正好等于目标值,直接返回中间索引
return mid
if nums[0]<=nums[mid]:#检查数组的左半部分是否有序(旋转点在mid右侧)
if nums[0]<=target<nums[mid]: #如果目标值在有序的左半部分范围内,将右指针移到mid左侧
r=mid-1
else:
l=mid+1 #否则目标值在右半部分,将左指针移到mid右侧
else: #如果左半部分无序(旋转点在mid左侧),则右半部分必定有序
if nums[mid]<target<=nums[len(nums)-1]:#如果目标值在有序的右半部分范围内,将左指针移到mid右侧
l=mid+1
else: #否则目标值在左半部分,将右指针移到mid左侧
r=mid-1
return -1
时间复杂度:O(logn),其中 n 为 nums 数组的大小。整个算法时间复杂度即为二分查找的时间复杂度 O(logn)
空间复杂度: O(1)
源自力扣官方题解