目录
题目链接
81. Search in Rotated Sorted Array II
题目描述
在可能包含重复元素的旋转有序数组中搜索一个target,如果target在数组中被找到,返回True,否则返回False。
示例
输入:nums=[2,5,6,0,0,1,2], target = 0
输出:True
输入:nums=[2,5,6,0,0,1,2], target = 3
输出:False
解决思路一
1、在有重复元素的旋转有序数组中找到旋转点pivot,即第一个小于nums[-1]的元素(可参考【leetcode-Python】- 二分搜索 - 154 Find Minimum in Rotated Sorted Array II中的“进一步扩展”部分)。
2、比较target和nums[-1]的大小,如果target小于nums[-1],说明target在右排序数组中,下一步在[pivot,len(nums)-1]范围内搜索target;如果target大于nums[-1],说明target在左排序数组中,下一步在[0,pivot-1]范围内进行搜索,搜索过程可参考模板一【Leetcode-Python】-二分搜索模板汇总与相关题目。如果target等于nums[-1],说明target一定可以在nums中找到,因此直接返回True。
时间复杂度:最优情况下(数组中所有元素都不相等)时间复杂度为O(logN),最差情况下(数组元素都相等并且不等于target)时间复杂度为O(N)
空间复杂度:O(1)
解决思路一Python实现
class Solution:
def search(self, nums: List[int], target: int) -> bool:
if(len(nums) == 0):
return False
# get the pivot
left = 0
right = len(nums)-1
while(left < right):
mid = (left + right) // 2
if(nums[mid] > nums[right]): #说明pivot在mid的右侧,且mid不可能是pivot
left = mid + 1
elif(nums[mid] < nums[right]) : #pivot有可能是mid,也可能在mid 的左侧。
right = mid
else: # nums[mid] == nums[right] #pivot不确定在mid的左侧还是右侧。
if(nums[right] < nums[right - 1]): #在这个条件下nums[right]就是pivot,如果不做这一步判断直接通过right -= 1缩小范围的话有可能把pivot越过。
break #跳出循环
right -= 1 #如果nums[right]不是pivot,需要将right向左移动一位,缩小搜索范围。
pivot = right
if(target > nums[-1]): #target可能在左排序序列
left,right = 0,pivot-1
elif(target < nums[-1]): #target可能在右排序序列
left,right = pivot,len(nums)-1
else:
return True
while(left <= right): #这一部分参考模板一
mid = (left + right) // 2
if(nums[mid] == target):
return True
elif(nums[mid] < target):
left = mid + 1
else:
right = mid - 1
return False
解决思路二
总体上仍然采用二分搜索的思路,但和上一个思路还是有些差别:在这个方法中,直接根据nums[mid]和target在旋转有序数组中的位置(左排序序列还是右排序序列)来收缩搜索区间,不需要先判断旋转点的位置。考虑循环的维持条件、搜索区间收缩的条件和边界的更新方式这几个写二分搜索时要注意的关键点,由于是在数组中寻找某元素是否存在,优先考虑模板一,模板详见【Leetcode-Python】-二分搜索模板汇总与相关题目。循环的维持条件为left <= right,对应边界更新方式是left = mid + 1或right = mid - 1。搜索区间收缩条件根据nums[mid]和target在旋转有序数组中的位置确定,nums[mid]和target(如果存在)在旋转有序数组中的位置包括三种情况:
(1)nums[mid]在左排序序列(nums[mid] > nums[right]),target在右排序序列中存在(target <= nums[right])。这种情况下更新left = mid + 1。
(2)nums[mid]在右排序序列(nums[mid] < nums[right]),target在左排序序列中存在(target > nums[right])。这种情况下更新right = mid - 1。
(3)target和nums[mid]同时在左排序序列或右排序序列。 由于两个序列都是升序序列,这种情况下根据target和nums[mid]的大小比较结果来收缩搜索区间。
在情况(2)中,由于target > nums[right],因此target不可能在右排序序列中存在,那么如果target存在,只会在左排序序列中存在。其次认为target == nums[right]属于target在右排序序列的情况,不一定能够保证target在左排序序列中一定存在。因此在第二种情况中“target在左排序序列中存在”的代码表达为target > nums[right]。
当nums[mid] == nums[right]时不确定nums[mid]在左排序序列还是在右排序序列,此时令right -= 1缩小搜索范围,在不影响搜索结果的条件下继续循环。
时间复杂度:最优情况下(数组中所有元素都不相等)时间复杂度为O(logN),最差情况下(数组元素都相等并且不等于target)时间复杂度为O(N)
空间复杂度:O(1)
但是当nums中元素非常多时,解决思路二相较于解决思路一要快一些,因为解决思路一需要先寻找pivot再缩小搜索区间,解决思路二则略过了寻找pivot这一步。
解决思路二Python代码
class Solution:
def search(self, nums: List[int], target: int) -> bool:
if(len(nums) == 0):
return False
left = 0
right = len(nums)-1
while(left <= right):
mid = (left + right) // 2
if(nums[mid] == target):
return True
if(nums[mid] == nums[right]):#如果nums[mid] == nums[right],不确定nums[mid]在左排序区间还是右排序区间,在这种情况下让right左移一位以在不影响结果的前提下继续循环
right -= 1
continue
if(nums[mid] > nums[right] and target <= nums[right]):#nums[mid]在左排序序列 并且 target在右排序序列(如果target < nums[right],target在右排序序列,如果target == nums[right],target也在右排序序列,这里两种情况放到一起考虑,
#因为收缩搜索区间的方式都是left = mid + 1)
left = mid + 1
elif(nums[mid] < nums[right] and target > nums[right]): #nums[mid]在右排序序列,target在左排序序列
right = mid - 1
else: #target和nums[mid]同时在左排序序列或右排序序列,但两个子序列都是升序序列
if(target > nums[mid]):
left = mid + 1
else:
right = mid - 1
return False
相关题目
【leetcode-Python】- 二分搜索 - 154 Find Minimum in Rotated Sorted Array II