百度移动终端研发笔试:
对已排好序的数组A,一般来说可用二分查找可以很快找到。现有一特殊数组A[],它是循环递增的,如A[]={ 17 19 20 25 1 4 7 9},试在这样的数组中找一元素x,看看是否存在。
解题思路:第一眼看到题目,想到的就是二分查找,但是对于该题目不能直接运用,但通过观察可发现,用类似二分查找的方法可以找到数组中的最小值的索引值,思路很简单,就不写方法了,其时间复杂度为log(b)
找到最小的值后,就会发现我们可以用二分查找的方法找了,因为我们可以通过该最小值得索引将循环递增数组的每一项的索引映射到递增数组中,设数组的大小为size,通过查找可得到的最小值得索引minIndex,当前索引为index则:
映射后的索引为:(index-minIndex>0)? (index-minIndex) ?(index-minIndex+size)
如上述数组的minIndex=4,size=8,25的当前索引为3,则映射后的索引为3-4+8=7,而最小值索引1映射后的索引为4-4=0
可见通过映射可将循环递增数组映射为递增数组,然后通过二分查找寻找,可以发现,对于递增数组,此方法同样可以用
易知该算法时间复杂度为log(n),参考代码如下:
#include <iostream>
using namespace std;
//返回找到最小值的索引
int findMin(int a[], int size);
//通过最小值的索引将一个一个循环递增数组的索引映射到一个递增数组的索引
int mapIndex(int index, int size, int minIndex);
//在循环递增数组中查找k
int reBinSearch(int a[], int size, int k);
int main()
{
int a[]={8,9,12,2,3,5,6,7};
cout<<findMin(a, sizeof(a)/sizeof(int))<<endl;
for(int i=0; i<sizeof(a)/sizeof(int); i++)
cout<<reBinSearch(a, sizeof(a)/sizeof(int), a[i])<<endl;
return 0;
}
int reBinSearch(int a[], int size, int k)
{
int minIndex = findMin(a, size);
int b = minIndex; //最小值索引
int e = (b-1)>0? b-1 : b+size-1; //最大值索引
int mid, tb, te;
//类二分查找
while(1)
{
tb = mapIndex(b, size, minIndex);
te = mapIndex(e, size, minIndex);
if(tb>=te)break;
mid = (tb+te)/2;
//将mid映射回循环数组
mid = (mid+minIndex)%size;
if(a[mid]==k) return mid;
if(a[mid]<k)
b = (mid+1)%size;
else
e = ((mid-1)>=0 ? mid-1 : mid+size-1);
}
return a[b]==k ? b : -1;
}
int findMin(int a[], int size)
{
//若该数组位递增数列
if(a[size-1]>a[0] || size==1)
return 0;
int mid, b = 0, e = size-1;
while(b<e)
{
mid = (b+e)/2;
if(a[mid]>a[e])
b = mid+1;
else
e= mid - 1;
}
return b;
}
int mapIndex(int index, int size, int minIndex)
{
index -= minIndex;
return (index>=0) ? index : (index+size);
}