Letcode33. 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
Example 2:
Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1
解法一
1.根据首尾判断是否旋转
2.若未旋转,直接二分查找(注意:可以直接判断target是否在首尾之内,否则直接返回-1)
3.若旋转,先找旋转点(以找到最大值为例)
(1)情况一:target
在[0,index_max]
之内:此范围二分查找
(2)情况二:target
在[index_max+1,len(nums)-1]
之内:此范围二分查找
(3)情况三:若不在以上两种情况则直接返回-1
解法二
将题目中的数组分成两段,一段是[4,5,6,7],另一段[0,1,2](特殊情况[1,2,3,4]可以分为[1,2,3,4]和[])。
对于target
不在的那一段,我们将它设置为无穷大,这样整个数组有序,可以用正常的二分法去找 target
了,例如
[ 4 5 6 7 1 2 3] ,如果 target = 5,那么数组可以看做 [ 4 5 6 7 inf inf inf ]。
[ 4 5 6 7 1 2 3] ,如果 target = 2,那么数组可以看做 [ -inf -inf - inf -inf 1 2 3]。
每次只关心 mid
的值,所以 mid 的取值为{nums [ mid ], inf, -inf}
。
- 判断
nums [ mid ]
和target
不在同一段:把nums [ mid ]
和target
同时与nums [ 0 ]
比较,如果它俩都大于nums [ 0 ]
或者都小于nums [ 0 ]
,那么就代表它俩在同一段。 mid = nums[mid]
:nums [ mid ]
和target
在同一段里边。mid = inf
:nums [ mid ]
和target
不在同一段里边,且target
<nums[0]
(例如[4,5,6,7,1,2,3], target = 2)mid = inf
:nums [ mid ]
和target
不在同一段里边,且target
>nums[0]
(例如[5,6,7,1,2,3,4], target = 6)
时间复杂度:
空间复杂度:
解法三
算法基于一个事实,数组从任意位置劈开后,至少有一半是有序的。我们可以先找到哪一段是有序的 (只要判断端点即可),然后看 target
在不在这一段里,如果在,那么就把另一半丢弃。如果不在,那么就把这一段丢弃。
比如 [ 4 5 6 7 1 2 3] ,从 7 劈开,左边是 [ 4 5 6 7] 右边是 [ 7 1 2 3],左边是有序的。
时间复杂度:
空间复杂度:
Java
public int search(int[] nums, int target) {//解法二
int lo = 0, hi = nums.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
int num = nums[mid];
//nums [ mid ] 和 target 在同一段
if ((nums[mid] < nums[0]) == (target < nums[0])) {
num = nums[mid];
//nums [ mid ] 和 target 不在同一段,同时还要考虑下变成 -inf 还是 inf。
} else {
num = target < nums[0] ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
//正常二分
if (num < target)
lo = mid + 1;
else if (num > target)
hi = mid - 1;
else
return mid;
}
return -1;
}
public int search(int[] nums, int target) {//解法三
int start = 0;
int end = nums.length - 1;
while (start <= end) {
int mid = (start + end) / 2;
if (target == nums[mid]) {
return mid;
}
//左半段是有序的
if (nums[start] <= nums[mid]) {
//target 在这段里
if (target >= nums[start] && target < nums[mid]) {
end = mid - 1;
} else {
start = mid + 1;
}
//右半段是有序的
} else {
if (target > nums[mid] && target <= nums[end]) {
start = mid + 1;
} else {
end = mid - 1;
}
}
}
return -1;
}
C++
int search(vector<int>& nums, int target) {//解法二
int lo = 0, hi = nums.size();//左右各一个指针
while (lo < hi) {
int mid = (lo + hi) / 2;//找出中间点
double num = (nums[mid] < nums[0]) == (target < nums[0])//如果nums[mid]和target在nums[0]的同侧
? nums[mid]
: target < nums[0] ? -INFINITY : INFINITY;
if (num < target)
lo = mid + 1;
else if (num > target)
hi = mid;
else
return mid;
}
return -1;
}
Python
class Solution: #解法一
def search(self, nums: List[int], target: int) -> int:
if len(nums)<1:return -1
#二分查找
def two_find(m,n):
if m>n:return -1
mid = (m+n)//2
if nums[mid]==target:
return mid
elif nums[mid]>target:
return two_find(m,mid-1)
else:return two_find(mid+1,n)
#找旋转点,返回最大值处
def find_max(m,n):
mid = (m+n)//2
if nums[mid]>nums[mid+1]:return mid
elif nums[mid]>=nums[0]:return find_max(mid+1,n)
else: return find_max(m,mid-1)
if nums[0]<=nums[-1]:#如果数组没有旋转!("="是为了在nums长度为1时也能算进此)
if target>=nums[0] and target<=nums[-1]:return two_find(0,len(nums)-1)
else:return -1
else:
index_max = find_max(0,len(nums)-1)
if target>=nums[0] and target<=nums[index_max]:return two_find(0,index_max)
elif target>=nums[index_max+1] and target<=nums[len(nums)-1]:return two_find(index_max+1,len(nums)-1)
else:return -1
参考
leetcode题解