旋轉數組中的最小數字
若是用直接查找的方法也就是一個一個比的方法(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 }