题目:
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
题目大意:一个有序数组在某个点处旋转了,给一个target,要求出target在数组里面的位置。没有的话,返回-1。
因为时间复杂度要求O(logn),所以不能暴力求解。我们应该想到二分查找,但是这里并不是一个有序数组啊。没关系,我们来试试看。
对于数组nums,我们取它的中间位置mid,相对于数组的旋转点的位置,可以分为以下三种情况:
1.mid在旋转点左边:此时由mid往左的数组是一个升序数组,由mid+1往右的数组是一个经过旋转的有序数组(和nums类似),我们称其为乱序数组
2.mid恰好是旋转点的位置:此时左右两边都是升序数组
3.mid在旋转点右边:此时mid+1往右是有序数组,mid往左是乱序数组
可以自己举个例子就很明了了。
针对以上三种情况,如果target落入某个有序数组中,那么直接二分查找,可以得到结果;如果可能在某个乱序数组中,那么需要递归调用自身函数。
代码:
class Solution {
public:
int search(vector<int>& nums, int target) {
if (nums.empty()) return -1;
return search(nums, 0, nums.size()-1, target);
}
private:
int search(const vector<int>& nums, int lo, int hi, int target) {
if (lo > hi) return -1;
if (lo == hi) {
if (nums[lo] == target)
return lo;
else return -1;
}
int mid = (lo+hi)/2;
if (nums[lo] <= nums[mid]) {//第一段升序
if (target >= nums[lo] && target <= nums[mid])//target在第一段中
return binarySearch(nums, lo, mid, target);
else if (nums[mid+1] > nums[hi]) //第二段乱序
return search(nums, mid+1, hi, target);
else if (target >= nums[mid+1] && target <= nums[hi]) //第二段升序,且target在其中
return binarySearch(nums, mid+1, hi, target);
else return -1;//第二段升序且target不在其中
}
else {//第一段乱序,说明第二段升序
if (target >= nums[mid+1] && target <= nums[hi]) //在第二段当中
return binarySearch(nums, mid+1, hi, target);
else return search(nums, lo, mid, target);
}
}
int binarySearch(const vector<int>& nums, int lo, int hi, int target) {
if (lo > hi) return -1;
if (lo == hi) {
if (nums[lo] == target)
return lo;
else return -1;
}
int mid = (lo+hi)/2;
if (nums[mid] == target)
return mid;
else if (target >= nums[lo] && target < nums[mid])
return binarySearch(nums, lo, mid, target);
else return binarySearch(nums, mid+1, hi, target);
}
};
上面是我自己的方法,然后我看评论区,找到了一个简洁的方法,下面介绍:
数组可以分为左边和右边,以旋转的那个点为界
可以将一个数与nums.back()相比较来得知它落在哪一边
如果nums[mid]和target在同一边,那么可以根据nums[mid]和target的大小关系来调整范围(和二分查找相同)
如果在不同边,那么我们就会想要进入target所在的那一边,那么就需要比较target和nums.back()的大小了
如果target > nums.back(),说明在比nums.back()大的那一边;否则,在比nums.back()小的那一边
代码:
class Solution {
public:
int search(vector<int>& nums, int target) {
if (nums.empty()) return -1;
int lo = 0, hi = nums.size()-1;
while (lo < hi) {//每次lo~hi范围都至少会减小1,最终会lo == hi
int mid = lo+(hi-lo)/2;
if ((nums[mid]-nums.back()) * (target-nums.back()) > 0) {
if (nums[mid] < target)
lo = mid+1;
else
hi = mid;
}
else if (target > nums.back()) hi = mid;
else lo = mid+1;//target == nums.back()的情况也得在这
}
if (nums[lo] == target)
return lo;
else
return -1;
}
};