旋轉數組的最小數

聲明:題目、程序來自《劍指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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章