假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
找断点,断点左右分别是有序的,然后就可以分别对左右两段用二分查找法(logn复杂度)了。
class Solution:
def search(self, nums: List[int], target: int) -> int:
# 找断点比较有意思:我们通过l 和 R 求 mid。
# 最右边的r永远是在递增序列中,
# 如果num[mid] < num[r],说明我们这个mid与r在同一个递增序列,
# 我们减少r,令r=mid,缩小范围去查找断点。
# 如果num[mid] > num[r],说明mid与r在不同的递增序列,增大L,令l= mid+1, 不断逼近断点。
# 其实也可以看做不用递归方法的二分查找
l = 0
r = len(nums) -1
while l < r:
mid = (l + r)//2
if nums[mid] > nums[r]:
l = mid + 1
else:
r = mid
# 找到断点
pol = l
# 然后在左段二分查找
ans = self.binary_search_2(nums[:pol], target)
if ans == -1:
# 如果没有找到,则一定在右段
ans = self.binary_search_2(nums[pol:], target)
if ans != -1:
ans += len(nums[:pol])
return ans
# 以下是二分查找法的两种实现,二分查找需要数组为有序排列
# 递归
def binary_search_1(self, nums, left, right, target):
if left < right:
return -1
mid = (lef + right) // 2
if nums[mid] == target:
return mid
elif target < nums[mid]:
right = mid - 1
else:
left = mid + 1
return binary_search_1(self, nums, left, right, target)
# 循环
def binary_search_2(self, nums, target):
index = -1
l = 0
r = len(nums) - 1
# 双指针移动一般用while
while l <= r:
mid = (l+r)//2
if nums[mid] < target:
l = mid + 1
elif nums[mid] > target:
r = mid - 1
else:
index = mid
break
return index