一、概述
这题目出的有点模糊,“rotated at some pivot”,你这个some,到底是一个转折点还是好几个啊,查了一下pivot,可数名词,单数,所以应该是一个。不然想一下极端情况,对于1234,调转二次变成2143,那这个有序性就被完全破坏掉了,这个条件就没卵子用。这样不行。所以只调转一次。
接下来看时间要求,logn,稳了,用二分肯定没错。
二、分析
传统的二分法是应用于有序数组的,中间的和target比,target大于mid就找后面,target小于mid就找前面。为什么这么自信?因为target大于mid就表明target大于左边最大值;同理小于。
现在问题来了,这个数组不是完全有序的,而是分段有序。那么如果我们分成[left,mid]和[mid,right]两个区间,就一定有一个区间完全有序,而另外一个部分有序。因为转折点只有一个,所以转折点所在的区间部分有序,转折点不在的区间完全有序。
对于完全有序的区间,可以判断target在不在该区间内,即用target与该区间的左右端点比较,小于左端点或大于右端点说明不在,否则就在。这样就可以确定target在左区间还是右区间了。
如何判断转折点在哪个区间呢?我们来看以下几个例子:
12345678,转折点位于8(从0开始,第8个,即不存在),left=1,mid=4,right=8;
23456781,转折点位于7,left=2,mid=5,right=1;
34567812,转折点位于6,left=3,mid=6,right=2;
45678123,转折点位于5,left=4,mid=7,right=3;
56781234,转折点位于4,left=5,mid=8,right=4;
67812345,转折点位于3,left=6,mid=1,right=5;
78123456,转折点位于2,left=7,mid=2,right=6;
81234567,转折点位于1,left=8,mid=3,right=7;
可以看出一个规律:转折点位于的区间,该区间的左端点值大于右端点。因此可以用这个来判断。
代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
int len=nums.size();
if(len==0)
return -1;
if(len==1)
if(nums[0]!=target)
return -1;
else
return 0;
int left=0,right=len-1;
int mid=(left+right)/2;
while(left!=right)
{
if(nums[mid]==target)
return mid;
if(nums[left]>nums[mid])//右侧正常
{
if(nums[mid]>target||nums[right]<target)
{
right=mid-1;
}
else
{
left=mid+1;
}
}
else//左侧正常
{
if(nums[left]>target||nums[mid]<target)
{
left=mid+1;
}
else
{
right=mid-1;
}
}
mid=(left+right)/2;
}
if(nums[left]==target)
return left;
else
return -1;
}
};
三、总结
稍微有点弯弯绕,知道用二分法就很简单了。