假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [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
如果忽略题目对于时间复杂度的要求,这道题就没有什么可做性,直接使用线性遍历即可。
class Solution:
def search(self, nums: List[int], target: int) -> int:
if nums == []: return -1
return nums.index(target) if target in nums else -1
既然时间复杂度要求为,那么可以使用二分查找。我一开始想着使用最基本的二分查找来搜索有序数组,因为旋转后的数组并不是完全升序排列,所以首先使用sorted()函数将其进行排列,然后在使用二分查找。但是查阅资料发现sorted()的时间复杂度最坏为,这样算法整体的时间复杂度就成了,还是不符合题意,再想办法。
class Solution:
def search(self, nums: List[int], target: int) -> int:
if nums == []: return -1
sortNums = sorted(nums)
l = 0
r = len(sortNums) - 1
mid = (l + r) // 2
while l <= r:
mid = (l + r) // 2
if sortNums[mid] == target:
return nums.index(sortNums[mid])
elif sortNums[mid] < target:
l = mid + 1
else:
r = mid - 1
return -1
虽然旋转后的数组可能不是完全升序排列,但是在旋转点的两周都是升序排列的。例如[4,5,6,7,0,1,2]
的旋转点为0,[]4,5,6,7
和[0,1,2]
都是完全升序排列,我们可以在二分查找的过程中利用这个性质。
- 如果
nums[mid] == target
,那么直接返回mid
- 如果
nums[mid] != target
,那么首先看nums[l: mid]
是否升序排列,如果是,继续看nums[l] <= target < nums[mid]
是否成立
- 如果成立,说明
target
在nums[l: mid]
中,更新r继续查找 - 否则
nums[mid: r]
完成升序,target在后半部分,继续查找
- 如果
nums[mid] != target
,且nums[l: mid]
不是完全升序排列,那么后半部分必定完全升序排列。因此,先看nums[mid] < target <= nums[r]
是否成立:
- 如果成立,更新l,继续查找
- 否则更新r,继续查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums : return -1
l = 0
r = len(nums)-1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
# 当前左半部分升序
if nums[l] <= nums[mid]:
# 如果位于左半部分,则在左半部分查找
if nums[l] <= target < nums[mid]:
r = mid - 1
# 否则在右半部分查找
else:
l = mid + 1
else:
if nums[mid] < target <= nums[r]:
l = mid + 1
else:
r = mid - 1
return -1
另外,同样是使用二分查找,我们可以先使用二分查找找到旋转点的索引,然后在使用二分查找找target。具体过程和基本的二分查找区别不大,具体思路可见代码。
from typing import List
class Solution:
# 寻找旋转点的位置,即当前数组的最小元素
def findIndex(self, nums):
l, r = 0,len(nums) - 1
# 如果头小于尾表示数组升序,并无旋转
if nums[l] < nums[r]: return 0
# 二分查找
while l <= r:
mid = (l + r) // 2
if nums[mid] > nums[mid + 1]:
return mid + 1
else:
if nums[mid] < nums[l]:
r = mid - 1
else:
l = mid + 1
def search(self, nums: List[int], target: int) -> int:
# 考虑特殊的输入情况
if nums == []: return -1
if len(nums) == 1: return 0 if nums[0] == target else -1
index = self.findIndex(nums)
if target == nums[index]: return index
if index !=0 and target >= nums[0]:
n = nums[:index]
elif index != 0 and target <= nums[-1]:
n = nums[index:]
else:
n = nums
l, r = 0, len(n) - 1
while l <= r:
mid = (l + r) // 2
if n[mid] == target:
return nums.index(n[mid])
elif n[mid] < target:
l = mid + 1
else:
r = mid - 1
return -1