题意描述:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7]
可能变为 [4,5,6,7,0,1,2]
)。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n)
级别。
示例:
示例一:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例二:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
解题思路:
Alice: 给你一个 升序排序的数组,如何在 O(n * log n) 的时间复杂度内 查找一个元素呢 ?
Bob: 二分查找啊 ?
Alice: 那这道题怎么做呢 ?
Bob: 这个旋转排序数组不就是两个排序数组拼起来的吗,你把它在分成两个排序数组用两次二分查找不就好了吗。
Alice: 好像是的,不错不错。不过时间复杂度呢,怎么找到旋转的点,把两个排序数组分开呢 ?
Bob: 直接遍历呗,(ΩДΩ) 这样子时间复杂度应该就是O(n log n) + O (n) 了,不过应该也能过。
Alice: 肯定有直接 O(n log n)的写法,╭(╯^╰)╮
Bob: 那就应该是在 找旋转点的时候也用二分查找。。。😵
代码:
Python 方法一:二分查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
if len(nums) == 0:
return -1
elif len(nums) == 1:
if nums[0] == target:
return 0
else:
return -1
elif len(nums) == 2:
if nums[0] == target:
return 0
elif nums[1] == target:
return 1
else:
return -1
# 查找旋转的点
bound = 0
for x in range(1, len(nums)):
if nums[x] < nums[x-1]:
bound = x
break
# 根据旋转点,对数组进行两次二分查找
res1 = self.binarySearchAse(nums, 0, bound-1, target)
res2 = self.binarySearchAse(nums, bound, len(nums)-1, target)
if res1 == -1 and res2 == -1:
return -1
elif res1 == -1 and res2 != -1:
return res2
elif res1 != -1 and res2 == -1:
return res1
def binarySearchAse(self, nums: List[int], left: int, right: int, target: int) -> int:
# 升序数组的二分查找
while left <= right:
middle = (left + right) // 2
if nums[middle] == target:
return middle
elif nums[middle] < target:
left = middle + 1
else:
right = middle - 1
return -1
Java 方法一: 二分查找
class Solution {
public int search(int[] nums, int target) {
if(nums.length < 3){
// 对于边界输入,直接查找。当数组长度为2的时候,旋转后的数组可能直接变为递减的。
for(int i=0; i<nums.length; ++i){
if(nums[i] == target){
return i;
}
}
return -1;
}else{
int boundary = 0;
for(int i=1; i<nums.length; ++i){
// 查找旋转点
if(nums[i] < nums[i-1]){
boundary = i;
break;
}
}
int res1 = binarySearchAse(nums, 0, boundary-1, target);
int res2 = binarySearchAse(nums, boundary, nums.length-1, target);
int res = -1;
// 处理两次二分查找的结果
if(res1 == -1 && res2 != -1){
res = res2;
}else if(res1 != -1 && res2 == -1){
res = res1;
}
return res;
}
}
public int binarySearchAse(int[] nums, int left, int right, int target){
while(left <= right){
int middle = (left + right) / 2;
if(nums[middle] == target){
return middle;
}else if(nums[middle] < target){
left = middle + 1;
}else{
right = middle - 1;
}
}
return -1;
}
}
易错点:
- 一些边界值输入容易出错。
[3,1,2]
3
[3,5,1]
1
[4,5,6,7,0,1,2]
0
[4,5,6,7,0,1,2]
3
[1]
1
[1]
0
[1,2]
2
[1,2]
1
[3,1,2]
2
- 答案:
0
2
4
-1
0
-1
1
0
2
总结: