劍指offer11. 旋轉陣列的最小數字
阿新 • • 發佈:2020-12-23
把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。輸入一個遞增排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。例如,陣列 [3,4,5,1,2] 為 [1,2,3,4,5] 的一個旋轉,該陣列的最小值為1。
輸入:[3,4,5,1,2]
輸出:1
思路
這道題簡單的地方在於一眼能看出來是二分,難點在於特殊情況的考慮。
旋轉操作讓這個陣列可以分成兩個遞增的陣列,並且右邊那個要更小。
最簡單的情況,假設沒有重複元素,左邊陣列的開頭設為start, 那麼我們始終有:
e
l
e
m
e
n
t
[
r
i
g
h
t
]
<
s
t
a
r
t
element[right] < start
element[right]<start
我們要找的拐點就是小於start的第一個數。這直接套二分查詢就行。
特殊情況1:
搬了所有元素或沒有搬任何元素,這樣不存在兩個子陣列,這種情況,最右會大於最左(在沒有重複元素時)。
特殊情況2:
存在相等元素:
- 相等元素在非切割點,這種氣勢不需要管,因為不影響我們最初的假設即 element[right] < start,譬如[3,4,5,5,1,2]
- 切割旋轉點本身就在一系列相等元素中。譬如[1,2,2,2,4]–>[2,2,4,1,2]
- 這種情況下,不符合
e
l
e
m
e
n
t
[
r
i
g
h
t
]
<
s
t
a
r
t
element[right] < start
- 我對於這種情況的處理採取去重,也就是這個重複的元素k在最開始讓它在左側陣列先消失。這對於找最小元素是沒有影響的,反正右陣列依然保留了這個k。 如果k是最小,可以在右陣列找到;如果k不是最小,可以在有陣列找到比k小的存在。如下所示,上下兩個找旋轉點是等價的。
[1,2,2,2,4] --> 旋轉–> [2,2,4,1,2]
[1,2,4] --> 旋轉 -->[4,1,2]
- 這種情況下,不符合
e
l
e
m
e
n
t
[
r
i
g
h
t
]
<
s
t
a
r
t
element[right] < start
程式碼:
lass Solution { public: int minArray(vector<int>& numbers) { int left = 0; int right =numbers.size()-1; // 去除左邊和最右重複的元素 while(left < right && numbers[left] == numbers[right]) left ++; int start = numbers[left]; //最小值在旋轉部分的開頭,說明沒有旋轉 if(numbers[right]>numbers[left]) return start; while(left < right){ int mid = (left + right)/2; int val = numbers[mid]; if(mid-1>=0 && val < numbers[mid-1]) // 確定找到拐點 return val; if(val>=start){ left = mid+1; } else if(val < start){ right = mid-1; } } return numbers[left]; } };