在有序旋轉陣列中找到最小值
技術標籤:程式設計師程式碼面試指南
在有序旋轉陣列中找到最小值
題目描述
有序陣列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
1⩽N⩽106
1
⩽
a
r
r
i
⩽
1
0
9
1 \leqslant arr_i \leqslant 10^9
1⩽arri⩽109
題解:
二分。注意:二分不一定必須要有單調性,二分的本質是尋找某種性質的分界點。只要找到某種性質,可以確定目標在區間的前半部分還是後半部分,就可以用二分找到這個分界點。
為了便於分析,將陣列中的元素畫在二維座標系中,橫軸代表下標,縱軸代表值。
水平的實線表示相同的元素。可以發現,除了黑色水平那段,其餘部分均滿足二分性質:豎直的虛線左邊的元素均滿足
a
r
r
[
i
]
>
=
a
r
r
[
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)
剩下的就是普通二分了:
- 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]);
}