Suppose a sorted array 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.
这题是随机选取的一题,看起来Hard难度,但是做起来比上一题简单(Find the kth larger number)。这题的问题是在一个旋转了的有序数组中,查找一个数是不是在这数组中,并返回下标。
方法解析:
这题最直接的方法就是循环一遍,判断一下目标在不在数组中。但是这样做的话复杂度是O(N),如果多的话,肯定是超时的,试了一下真的超时了,因此我们应该要想一个更加快的算法。
这里的查找方法很明显会让人想到二分查找,但是二分查找需要有序数组,因此我们需要解决的问题是如何将旋转的有序数组转化为一个正常的有序数组。如果我们能知道该数组最小元素的下标q,那么我们就能转化成一个正常的有序数组了,(有序数组的元素下标+q)%数组长度=旋转后的数组元素下标。因此问题就在nums数组中扎到最小元素,这里使用二分法。
对于一个旋转数组a1,a2,......an,①如果最小元素在第一个位置,则a1<a(mid)<an,②如果最小元素在第一个位置和中间位置之间,则a1>a(mid)<an,③如果最小元素在中间位置,则a1>a(mid)<an,④如果最小元素在中间位置和最后位置之间,则a1<a(mid)>an,④否则就在最后一个位置。可以很容易知道二分之后的数组仍然是旋转的有序数组,因此二分之后数组的性质不变,所以可以用二分法解决该问题,只需要0(lgN)的复杂度,再加上查找也是O(lgN),因此总复杂度是O(lgN)
class Solution {
public:
int rotate_number(vector<int>& nums)
{
int begin=0,end=nums.size()-1;
while(begin!=end)
{
int mid=(begin+end)/2;
if(nums[mid]>=nums[begin]&&nums[mid]<nums[end])return begin;
if(nums[mid]<nums[begin]){end=mid;continue;}
if(nums[mid]<=nums[begin]&&nums[mid]<=nums[end])return mid;
if(nums[mid]>nums[end]){begin=mid+1;continue;}
return end;
}
return begin;
}
int search(vector<int>& nums, int target) {
int q=rotate_number(nums);
int begin=0,end=nums.size()-1;
while(begin!=end)
{
int mid=(begin+end)/2;
if(nums[(mid+q)%nums.size()]<target)begin=mid+1;
else end=mid;
}
if(nums[(end+q)%nums.size()]==target)
return (end+q)%nums.size();
else return -1;
}
};