聲明:題目、程序來自《劍指offer》,註釋、分析爲自己所寫備忘,侵刪
打算把劍指offer上的題整理一下,發上來備忘。
題目描述:
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小
元素。例如,數組{3, 4, 5, 1, 2}爲{1,2,3,4,5}的一個旋轉,該數組的最小
值爲1。
分析:數組中存儲的是有序數。最先想到的是比較,先第一個和最後一個比較,第一個比最後一個大,再從後往前比較相鄰的兩個,如果碰到逆序的兩個,則右邊的是最小的。移動了幾個數需要對比幾次,未移動則需要對比所有。
想加快搜索速度,那就隔一個對比一次,即不對比相鄰的,而是對比相隔一個的,最後處理中間夾的那一個。
既然能隔一步,那就能隔兩個,那麼最優的情況就是二分法查找了,對比頭尾中三個數,一步步縮小區間。
設置頭的index、尾的index,將中間mid的index初始化爲頭的index(因爲如果沒有移動,那麼直接返回mid),條件設置爲頭大於等於尾(如果頭比尾小,那麼一定是搜到了最小的,其實設置爲頭與尾相鄰更通用),判斷條件有四種,頭比中大,最小在前半區域,頭比中小,在後半區域。尾比中大,最小在前半區域,尾比中小,最小在後半區域。任選兩種不同區域的。
這裏有個特例:類似{0 1 1 1 1}、{1 1 1 0 1},如果你要對比的頭尾中是相等的,你也無法得知最小的在哪個區間,這就需要遍歷區間中所有了。
所以最終代碼如下:來自何海濤《劍指offer》
int Min(int* numbers, int length) { if(numbers == NULL || length <= 0) throw new std::exception("Invalid parameters"); int index1 = 0; int index2 = length - 1; int indexMid = index1; while(numbers[index1] >= numbers[index2]) { // 如果index1和index2指向相鄰的兩個數, // 則index1指向第一個遞增子數組的最後一個數字, // index2指向第二個子數組的第一個數字,也就是數組中的最小數字 if(index2 - index1 == 1) { indexMid = index2; break; } // 如果下標爲index1、index2和indexMid指向的三個數字相等, // 則只能順序查找 indexMid = (index1 + index2) / 2; if(numbers[index1] == numbers[index2] && numbers[indexMid] == numbers[index1]) return MinInOrder(numbers, index1, index2); // 縮小查找範圍 if(numbers[indexMid] >= numbers[index1]) index1 = indexMid; else if(numbers[indexMid] <= numbers[index2]) index2 = indexMid; } return numbers[indexMid]; } int MinInOrder(int* numbers, int index1, int index2) { int result = numbers[index1]; for(int i = index1 + 1; i <= index2; ++i) { if(result > numbers[i]) result = numbers[i]; } return result; }