这一节总结几道关于Rotated 数组的题目,涉及到LeetCode内四道相关题目:
33. Search in Rotated Sorted Array
81. Search in Rotated Sorted Array II
153. Find Minimum in Rotated Sorted Array
154. Find Minimum in Rotated Sorted Array II
数组在排序的基础上略做变换,但是仍然维持有序,下面从几个角度解析这一类问题。
153. Find Minimum in Rotated Sorted Array
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7]
might become [4,5,6,7,0,1,2]
).
Find the minimum element.
You may assume no duplicate exists in the array.
Example 1:
Input: [3,4,5,1,2]
Output: 1
题目解析:
我们先从这一题入手,在旋转数组中先找最小的那个数。
旋转数组的特点是,通过一个pivot将该数组分成了两部分,与第一个元素相比,左半部都比它大,右半部都比它小。
找最小的那个数,其实是找位置的一类题,这个pivot 具有这样的特点:左边的元素大,右边的元素也大,但是这样无法二分查找;因此还要在过程中与数组第一个元素相比来判断当前查找的位置在左右哪一部分,不断逼近pivot位置。需要注意的是,要处理一下数组没有被旋转的情况。代码如下:
class Solution:
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
l = len(nums)
if l == 0:
return None
if l == 1:
return nums[0]
p = nums[0]
if nums[-1] >= p:
return p
left, right = 0, l-1
while True:
mid = (left + right) // 2
if mid + 1 < l and nums[mid] > nums[mid+1]:
return nums[mid+1]
elif nums[mid+1] > p:
left = mid + 1
elif nums[mid] < p:
right = mid - 1
33. Search in Rotated Sorted Array
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7]
might become [4,5,6,7,0,1,2]
).
You are given a target value to search. If found in the array return its index, otherwise return -1
.
You may assume no duplicate exists in the array.
Your algorithm's runtime complexity must be in the order of O(log n).
Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0 Output: 4
题目解析:
此题有两大思路:
一,按照上一题的思路,先找到最小的数,即旋转的pivot及其位置,然后确定target所在部分(左部/右部),再二分查找。
二,直接二分查找套路,但是要判断 `target < nums[0]` 和`nums[mid]<nums[0]` ,二者同时成立时,按照普通的二分查找操
作;不同时成立,那么说明target和mid在不同部分,分情况处理。
两种方法代码如下,第一种方法看似复杂,实际上性能也是不错的,当我们确定了left和right后,直接套用格式,十分简洁;
第二种方法,在二分查找的格式上,再加一层判断,不理解原理会看的一头雾水。
class Solution:
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
l = len(nums)
if l == 0:
return -1
elif l == 1:
return 0 if target == nums[0] else -1
left, right = 0, l-1
pos = 0
while left <= right:
mid = (left + right) // 2
if mid+1 < l and nums[mid] > nums[mid+1]:
pos = mid+1
break
elif nums[0] <= nums[mid]:
left = mid+1
else:
right = mid-1
# print(pos)
if target == nums[0]:
return 0
elif target == nums[pos]:
return pos
elif pos > 0 and target >= nums[0] and target <= nums[pos-1]:
left, right = 0, pos-1
elif pos == 0 or target >= nums[pos] and target <= nums[l-1] :
left, right = pos, l-1
else:
return -1
while left <= right:
mid = (left + right) // 2
if target == nums[mid]:
return mid
elif target < nums[mid]:
right = mid-1
else:
left = mid+1
return -1
class Solution:
def search(self, nums: List[int], target: int) -> int:
l = len(nums)
if l == 0:
return -1
elif l == 1:
return 0 if nums[0] == target else -1
lt, rt = 0, l - 1
p = nums[0]
while lt <= rt:
mid = lt + (rt - lt) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
if (nums[mid] < p) == (target < p): # mid 和 target 在同一方
lt = mid + 1
else:
rt = mid - 1
else:
if (nums[mid] < p) == (target < p):
rt = mid - 1
else:
lt = mid + 1
return -1
154. Find Minimum in Rotated Sorted Array II
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7]
might become [4,5,6,7,0,1,2]
).
Find the minimum element.
The array may contain duplicates.
题目解析:
该题与81题背景一样,在上述旋转数组的基础上,数组中含有重复数字了。
此题重点是思考:
- Would allow duplicates affect the run-time complexity? How and why?
试想一个数组 [1,1,1,......,1,0,1,1,1,......1,1],我们如何找到最小的数,复杂度是怎样的?没错,O(N)了。
什么样的数组会造成这种结果呢? 即nums[0]==nums[-1],此时我们二分查找时,无法确定我们在旋转数组的左部还是右部(因为我们判断的依据就是与nums[0]元素比较),二分失效,只能用 `min()` 函数,暴力遍历了。
但是,除此情况以外,题目的复杂度仍是O(logn),需要注意的是最小的元素可能有多个,写判断条件时需要注意。代码如下:
class Solution:
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
l = len(nums)
if l == 0:
return False
elif l == 1:
return nums[0]
min_ = nums[0]
if nums[0] == nums[l-1]: # 此时只能O(n)解决
for i in range(l-1):
if nums[i] < min_:
min_ = nums[i]
return min_
elif nums[0] < nums[l-1]: # 数组没有旋转
return nums[0]
left, right = 0, l-1
while left <= right:
mid = (left + right) // 2
if mid+1 < l and nums[mid] > nums[mid+1]:
return nums[mid+1]
elif nums[0] <= nums[mid]:
left = mid+1
else:
right = mid-1
return nums[0]
81题在此基础上延伸,不再解析。
关于旋转数组的题目总结到此~