1. 程式人生 > 其它 >在有序旋轉陣列中找到最小值

在有序旋轉陣列中找到最小值

技術標籤:程式設計師程式碼面試指南

在有序旋轉陣列中找到最小值

題目描述

有序陣列arr可能經過一次旋轉處理,也可能沒有,且arr可能存在重複的數。例如,有序陣列[1, 2, 3, 4, 5, 6, 7],可以旋轉處理成[4, 5, 6, 7, 1, 2, 3]等。給定一個可能旋轉過的有序陣列arr,返回arr中的最小值。

[要求]

期望複雜度為 O ( log ⁡ n ) O(\log n) O(logn)

輸入描述:

第一行一個整數N表示陣列大小。
接下來N個數表示陣列內的數。

輸出描述:

輸出一個整數表示答案

示例1
輸入
7
1 2 3 4 5 6 7
輸出
1
示例2
輸入
7
4 5 6 7 1 2 3
輸出
1
備註:

1 ⩽ N ⩽ 1 0 6 1 \leqslant N \leqslant 10^6 1N106
1 ⩽ a r r i ⩽ 1 0 9 1 \leqslant arr_i \leqslant 10^9 1arri109


題解:

二分。注意:二分不一定必須要有單調性,二分的本質是尋找某種性質的分界點。只要找到某種性質,可以確定目標在區間的前半部分還是後半部分,就可以用二分找到這個分界點。

為了便於分析,將陣列中的元素畫在二維座標系中,橫軸代表下標,縱軸代表值。

在這裡插入圖片描述
水平的實線表示相同的元素。可以發現,除了黑色水平那段,其餘部分均滿足二分性質:豎直的虛線左邊的元素均滿足 a r r [ i ] > = a r r [ 0 ] arr[i]>=arr[0]

arr[i]>=arr[0],而虛線右邊(去除黑色線段部分)的元素均滿足 a r r [ i ] < a r r [ 0 ] arr[i]<arr[0] arr[i]<arr[0]

首先,我們需要把黑色的水平段刪除,至於為什麼刪除呢?考慮一種情況: a r r [ m i d ] = = a [ 0 ] arr[mid] == a[0] arr[mid]==a[0],這種情況下,根本不知道往哪邊走,所以只能刪除。

還需要處理陣列未旋轉的情況。當刪除黑色水平段後,若剩下的最後一個元素大於等於arr[0],說明陣列完全單調。但最壞情況:元素均相等情況下的時間複雜度為 O ( n ) O(n)

O(n)

剩下的就是普通二分了:

  • m = l + r >> 1
  • 若 a[m] >= a[0],l = m + 1
  • 若 a[m] < a[0],r = m
程式碼:
#include <cstdio>

using namespace std;

const int N = 1000000;

int n;
int a[N];

int main(void) {
    scanf("%d", &n);
    for ( int i = 0; i < n; ++i ) scanf("%d", a + i);
    n -= 1;
    if ( a[0] < a[n] ) return 0 * printf("%d\n", a[0]);
    while ( n && a[n] >= a[0] ) --n;
    if ( a[n] >= a[0] ) return 0 * printf("%d\n", a[0]);
    int l = 0, r = n - 1;
    while ( l < r ) {
        int m = l + r >> 1;
        if ( a[m] >= a[0] ) l = m + 1;
        else r = m;
    }
    return 0 * printf("%d\n", a[l]);
}