假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,0,1,2,2,5,6]
可能变为 [2,5,6,0,0,1,2]
)。编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
示例 1:
输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true
示例 2:
输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false
这道题相比于33 - 搜索旋转数组 - python来说,不同之处在于该旋转数组中包含有重复元素,所以在使用二分查找需要考虑到重复元素的存在。它主要表现在我们更新边界索引时要考虑下一个更新的位置是否和当前位置的元素相同,如果相同则需要继续往前或是往后。
AC code
class Solution:
def search(self, nums: List[int], target: int) -> int:
# 考虑特殊的输入情况
if nums == []: return False
if len(nums) == 1: return True if nums[0] == target else False
l = 0
r = len(nums)-1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return True
# 相比于33题主要不同之处
if nums[l] == nums[mid] == nums[r]:
l += 1
r -= 1
continue
# 当前左半部分升序
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 False
同样可以先使用二分查找找到旋转点的索引,但mid指向的元素可能是重复的,因此需要考虑它周围的情况。
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
if nums[mid] == nums[mid + 1]:
return mid
elif 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:
nums = sorted(list(set(nums)))
# 考虑特殊的输入情况
if nums == []: return False
if len(nums) == 1: return True if nums[0] == target else False
index = self.findIndex(nums)
if target == nums[index]: return True
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(nums) - 1
while l <=r:
mid = (l + r) // 2
if n[mid] == target:
return True
elif n[mid] < target:
l = mid + 1
else:
r = mid - 1
return False