若是用直接查找的方法也就是一个一个比的方法(O(n)级别),虽然很容易解决,但显然根据旋转数组的规律,希望有更好时间效率的算法来解决。
显然旋转数组可以用二分查找的方法来实现,考虑上面的例子,旋转数组中的第一个数一定是大于最后一个数的,然后要找的最小的数一定是两个递增序列的分界线(此数的左边递增,右边也递增),利用二分查找的思想,设置三个指针分别指向数组的开始(begin),结尾(end),和中间(mid),然后分析过程如下:
但是注意要考虑到的几种特殊条件:
1、若初始数组移动的是前0个元素,这也符合旋转数组的要求,即这种旋转数组本来就是排好序的数组,第一个数就是最小的元素。那么刚开始只需检测第一个数是否大于最后一个数,若小于,则直接返回第一个数就好。
2、考虑如下的数:
也就是说在这种情况下,当中间的数,最后一个数,第一个数都相等时,你是无法确定到底该往那边缩小查找范围,这种情况下只能利用O(n)级别的直接查找的方法。
故综合上边的讨论可以写出的最终代码如下:
1 #include<iostream> 2 using namespace std; 3 int findOrder(int elements[],int begin,int end)//特殊情况二的顺序查找 4 { 5 int key = elements[begin]; 6 for (int i = begin; i <= end; ++i) 7 { 8 if (elements[i] < key) 9 return elements[i]; 10 } 11 } 12 int findInSpArray(int elements[], int length) 13 { 14 if (elements == nullptr || length <= 0)//若输入不符合,直接返回 15 return 0; 16 int begin = 0; 17 int end = length - 1; 18 int mid = begin;//便于出现特例一时直接返回第一个数 19 while (elements[begin] >= elements[end])//每次缩小范围后不停的检测 20 { 21 if (end - begin == 1){//若范围减小到两个数,即end指向的一定是最小的数 22 mid = end; 23 break; 24 } 25 mid = (end + begin) / 2; 26 if (elements[begin] == elements[end] && elements[begin] == elements[end])//若出现特例二 27 { 28 return findOrder(elements, begin, end); 29 } 30 if (elements[begin] < elements[mid])//最小数在begin之后 31 begin = mid; 32 else if (elements[end] > elements[mid])//最小数在end之前 33 end = mid; 34 } 35 return elements[mid];//返回最小数 36 } 37 int main() 38 { 39 int a[5] = { 3, 4, 5, 1, 2 }; 40 cout << findInSpArray(a, 5) << endl; 41 int b[5] = { 1, 2, 3, 4, 5 }; 42 cout << findInSpArray(b, 5) << endl; 43 int c[5] = { 1, 0, 1, 1, 1 }; 44 cout << findInSpArray(c, 5) << endl; 45 system("pause"); 46 return 0; 47 }