假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。你的算法时间复杂度必须是 O(log n) 级别。
可以用二分法查找,但是边界处理需要多一些判断。假设数组为nums,元素中最小值索引为k(即下面图中0的位置),则根据k所在的位置,可以分为三种情况:第一种就是k即是在数组的开始位置,这种情况下直接用正常的二分法即可,判断数组中间值nums[m]与target选择左边继续二分还是右边继续二分;第二种如图1所示,此时k在m和e之间,即起始点(最小值)在右半部分的情况,这个时候左半边一定是有序即nums[s]<=nums[m],则我们只需要判断target是否在nums[s]和nums[m]之间即可知道target是属于左半边还是右半边,然后再对左半边或右半边进行同样操作即可;第三种如图2所示,此时k在s和m之间,即起始点在左半边的情况,这个时候右半边一定是有序即nums[m]<=nums[e],则我们可以判断target是否在nums[m]和nums[e]之间即可知道target是属于左半边还是右半边了,剩下的操作和第二种情况差不多。


实际上我们是不知道也不需要知道k的位置的,k位置的假设只是让我们更容易的分清三种情况。
实现代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
int s = 0, m = 0, e = nums.size() - 1;
while (s <= e) {
m = s + (e - s) / 2;
if (nums[m] == target) {
return m;
}
if (nums[s] > nums[m]) {
// 起始点在中分点左边
if (nums[m] < target && nums[e] >= target) {
s = m + 1;
}
else {
e = m - 1;
}
}
else if (nums[m] > nums[e]) {
// 起始点在中分点右边
if (nums[m] > target && nums[s] <= target) {
e = m - 1;
}
else {
s = m + 1;
}
}
else {
// 当前分段没有起始点,或起始点就在s位置
if (nums[m] < target) {
s = m + 1;
}
else {
e = m - 1;
}
}
}
return -1;
}
};