LeetCode 33. 搜索旋转排序数组 的改进。
出现了重复数字,需要在二分查找的预处理阶段进行一些特殊判断。
题目
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
进阶:
这是 搜索旋转排序数组 的延伸题目,本题中的 nums 可能包含重复元素。
这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?
影响:由于要去重,时间复杂度从 \(O(log N)\) 变成了 \(O(log N + N)\)。
示例
示例 1:
| 输入: nums = [2,5,6,0,0,1,2], target = 0 输出: true
|
示例 2:
| 输入: nums = [2,5,6,0,0,1,2], target = 3 输出: false
|
考察知识点
二分查找、数组
核心思想
方法一、预处理加二分查找
预处理:由于 nums
存在重复数字,然后又进行了旋转。所以预处理阶段,要找到旋转点,根据旋转点找到可能存在 target
且有序的那一部分 sub_list
。
二分查找:在 sub_list
利用二分查找寻找 target
。
方法二、直接二分查找
这道是之前那道 Search in Rotated Sorted Array 的延伸,现在数组中允许出现重复数字,这个也会影响我们选择哪半边继续搜索,由于之前那道题不存在相同值,我们在比较中间值和最右值时就完全符合之前所说的规律:
如果中间的数小于最右边的数,则右半段是有序的,。
若中间数大于最右边数,则左半段是有序的。
而如果可以有重复值,就会出现来面两种情况,[3 1 1]
和 [1 1 3 1]
,对于这两种情况,中间值等于最右值时,目标值3既可以在左边又可以在右边,那怎么办么,对于这种情况其实处理非常简单,只要把最右值向左一位即可继续循环,如果还相同则继续移,直到移到不同值为止,然后其他部分还采用 Search in Rotated Sorted Array 中的方法。
Python版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| from typing import List
class Solution: def search(self, nums: List[int], target: int) -> bool: if not len(nums) or (len(nums) == 1 and nums[0] != target): return False if len(nums) == 1 and nums[0] == target: return True _min = min(nums) if _min == target: return True min_index = nums.index(_min) if min_index == 0: if nums[-1] > nums[0]: pass else: while len(nums) >=2 and nums[-1] == nums[0]: nums = nums[:-1] elif min_index != 0 and target >= nums[0]: nums = nums[:min_index] else: nums = nums[min_index:] left = 0 right = len(nums) while left < right: mid = left + (right - left) // 2 if target == nums[mid]: return True elif target < nums[mid]: right = mid else: left = mid + 1 return False
Input = [[3,1,1], [1,3], [1], [1,1], [1], [1,3,1,1], [1,3], [2,5,6,0,0,1,2], [2,5,6,0,0,1,2]] Input1 = [3, 1, 1, 0, 0, 3, 3, 0, 3] Answer = [True, True, True, False, False, True, True, True, False]
if __name__ == "__main__": solution = Solution() for i in range(len(Input)): print("-"*50) reslut = solution.search(Input[i], Input1[i]) print(reslut == Answer[i])
|
时间复杂度:\(O(nlogn + n)\),二分查找的时间复杂度是 \(O(nlogn)\),而 nums = nums[:-1]
最坏情况下可能执行 n
次,所以最终的时间复杂度为 \(O(nlogn + n)\)。
空间复杂度:\(O(1)\),只是用了常数级别的额外空间。
执行用时 :44 ms, 在所有 Python3 提交中击败了80.02%的用户
内存消耗 :13.7 MB, 在所有 Python3 提交中击败了5.37%的用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution: def search(self, nums: List[int], target: int) -> bool: if not len(nums) or (len(nums) == 1 and nums[0] != target): return False if len(nums) == 1 and nums[0] == target: return True _min = min(nums)
n = len(nums) left,right = 1, n - 1 while left <= right: mid = (left + right) // 2 if nums[mid] == target: return True if nums[mid] < nums[right]: if nums[mid] < target and nums[right] >= target: left = mid + 1 else: right = mid -1 elif nums[mid] > nums[right]: if nums[left] <= target and nums[mid] > target: right = mid - 1 else: left = mid + 1 else: right -= 1 return False
|
258 / 275 个通过测试用例
参考连接
Grandyang