LeetCode 分类刷题:33. 搜索旋转排序数组
题目
整数数组 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
解析
一次二分
设 x=nums[mid] 是我们现在二分取到的数。
我们需要判断 x 和 target 的位置关系,谁在左边,谁在右边?
作者:灵茶山艾府
链接:https://leetcode.cn/problems/search-in-rotated-sorted-array/solutions/1987503/by-endlesscheng-auuh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
循环不变量:
left指向target左侧;right指向target或其右侧
二分范围:( -1, len(nums) - 1 )
判断条件:
怎么缩小target所在范围?
根据红蓝染色法,如果target在x左边,right = mid,染为蓝色;否则left = mid,染为红色
题目数据保证 nums 在预先未知的某个下标上进行了旋转,因此会有两个递增序列或者回到原升序序列(一个递增序列)。
判断 target 在 x 左边还是右边,
首先判断当前元素 x = nums[mid] 在第一序列还是第二序列:
如果 x > nums[-1],则x在第一序列,target在x左边,则target在第一序列,满足target > nums[-1] 且 target <= x;
否则 x 在第二序列,target在x左边,则target在第一序列或者target在第二序列中x的左边,即target > nums[-1] 或 target <= x.
答案
class Solution:def search(self, nums: List[int], target: int) -> int:def check(i: int) -> bool:# 判断 target 是否在 x 的左边,在左边则返回True,在右边则返回Falsex = nums[i]if x > nums[-1]:return target > nums[-1] and x >= targetreturn target > nums[-1] or x >= targetleft, right = -1, len(nums) - 1 # 开区间 (-1, n-1)while left + 1 < right: # 开区间不为空mid = (left + right) // 2if check(mid):right = midelse:left = midreturn right if nums[right] == target else -1# 作者:灵茶山艾府
# 链接:https://leetcode.cn/problems/search-in-rotated-sorted-array/solutions/1987503/by-endlesscheng-auuh/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析
时间复杂度:O(logn),其中 n 为 nums 的长度。
空间复杂度:O(1),仅用到若干额外变量。