LeetCode-33. 搜索旋转排序数组【数组 二分查找】
- 题目描述:
- 解题思路一:二分查找。1.找哨兵节点(nums[0]或nums[-1])可以确定nums[mid]位于前一段或后一段有序数组中。2. 就是边界left和right的变换,具体看代码。
- 解题思路二:二分查找,注意在闭区间[0, n-2]上面查找!
- 解题思路三:蓝色说明是nums[mid] > target 应该改变right = mid
题目描述:
整数数组 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
-104 <= nums[i] <= 104
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-104 <= target <= 104
解题思路一:二分查找。1.找哨兵节点(nums[0]或nums[-1])可以确定nums[mid]位于前一段或后一段有序数组中。2. 就是边界left和right的变换,具体看代码。
需要注意的是,二分的写法有很多种,所以在判断 target 大小与有序部分的关系的时候可能会出现细节上的差别。
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
if nums[0] <= nums[mid]:
if nums[0] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target <= nums[len(nums) - 1]:
l = mid + 1
else:
r = mid - 1
return -1
时间复杂度:O(logn)
空间复杂度:O(1)
解题思路二:二分查找,注意在闭区间[0, n-2]上面查找!
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = -1, len(nums) - 1 # 开区间 (-1, n-1)
while left + 1 < right: # 开区间不为空
mid = (left + right) // 2
if nums[mid] < nums[-1]: # 蓝色
right = mid
else: # 红色
left = mid
return right
def lower_bound(self, nums: List[int], left: int, right: int, target: int) -> int:
while left + 1 < right: # 开区间不为空
mid = (left + right) // 2
# 循环不变量:
# nums[left] < target
# nums[right] >= target
if nums[mid] < target:
left = mid # 范围缩小到 (mid, right)
else:
right = mid # 范围缩小到 (left, mid)
return right if nums[right] == target else -1
def search(self, nums: List[int], target: int) -> int:
i = self.findMin(nums)
if target > nums[-1]:
left, right = -1, i # 左段
else:
left, right = i - 1, len(nums) # 右段
return self.lower_bound(nums, left, right, target)
时间复杂度:O(logn)
空间复杂度:O(1)
解题思路三:蓝色说明是nums[mid] > target 应该改变right = mid
class Solution:
def search(self, nums: List[int], target: int) -> int:
def is_blue(i: int) -> bool:
end = nums[-1]
if nums[i] > end:
return target > end and nums[i] >= target
else:
return target > end or nums[i] >= target
left, right = -1, len(nums) - 1 # 开区间 (-1, n-1)
while left + 1 < right: # 开区间不为空
mid = (left + right) // 2
if is_blue(mid): # 蓝色
right = mid
else: # 红色
left = mid
return right if nums[right] == target else -1
时间复杂度:O(logn)
空间复杂度:O(1)