假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [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

既然时间复杂度要求为有一个数组 用python 旋转90度 旋转数组中找某一个值python_升序,那么可以使用二分查找。我一开始想着使用最基本的二分查找来搜索有序数组,因为旋转后的数组并不是完全升序排列,所以首先使用sorted()函数将其进行排列,然后在使用二分查找。但是查阅资料发现sorted()的时间复杂度最坏为有一个数组 用python 旋转90度 旋转数组中找某一个值python_升序_02,这样算法整体的时间复杂度就成了有一个数组 用python 旋转90度 旋转数组中找某一个值python_升序_02,还是不符合题意,再想办法。

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]是否成立
  • 如果成立,说明targetnums[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