整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [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 。
你必须设计一个时间复杂度为 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
示例 3:输入:nums = [1], target = 0
输出:-1提示:
1 <= nums.length <= 5000
- 1 0 4 10^4 104 <= nums[i] <= 1 0 4 10^4 104
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
- 1 0 4 10^4 104 <= target <= 1 0 4 10^4 104
题目看完,感觉跟二分查找沾边,但是数组从中间某个位置调换了。
按照二分查找的思路,应该对比target跟nums[mid]的大小,但对比之后发现下一步如何移动跟mid所处位置有关,直接看代码:
class Solution:
def search(self, nums: list, target: int) -> int:
return self.search_helper(nums, target, 0, len(nums) - 1)
def search_helper(self, nums: list, target: int, left: int, right: int):
if left > right:
return -1
mid = (left + right) // 2
if nums[mid] == target:
return mid
if nums[left] == target:
return left
if nums[right] == target:
return right
#此时mid两侧必定是一边有序一边无序,测试左边如果有序那右边无序,否则右边有序
if nums[left] < nums[mid]:
if target < nums[mid] and target > nums[left]:
return self.search_helper(nums, target, left + 1, mid - 1)
else:
return self.search_helper(nums, target, mid + 1, right - 1)
else:
if target > nums[mid] and target < nums[right]:
return self.search_helper(nums, target, mid + 1, right - 1)
else:
return self.search_helper(nums, target, left + 1, mid - 1)
代码中的注释是关键,如果nums[mid]不等于target,那么mid两边一定有一边是有序而另一边一定无序,是否有序只要测试nums[left]与nums[mid](nums[mid]与nums[right]也行)就可以知道是否有序,当确定有序的一边后,就可以使用二分查找的条件了。17,18行(22,23行)表示需要先有序再二分。