數組中未出現的最小正整數
題目來源《程序員代碼面試指南》第八章
題目描述
給定一個無序數組arr,找到數組中未出現的最小正整數
arr = [-1, 2, 3, 4]。返回1
arr = [1, 2, 3, 4]。返回5
[要求]
時間複雜度爲,空間複雜度爲
思路
理想情況:一個N長度的有序數組存放[1,2,...,N]
這樣的數組,要找出缺少的最小正整數只需要從頭開始遍歷,發現data[l] != l + 1
的即爲所求。
按照理想情況來推,在遍歷的過程中將數組恢復到[1,...,N]
的理想情況
設置兩個變量l
和r
。l
表示已經搜索到的正數範圍[1,l]
,r
表示理想情況下能搜索到的最大範圍[1,r]
。假設數組的長度爲N
l
初始化爲0,因爲剛開始沒有正數,r
初始化爲N,因爲一個N長度的數組,理想情況下存放[1,...,N]
這樣N個正數。
假設遍歷到data[l]
後,分情況討論:
data[l] == l + 1
,也就是按照理想情況這個數放置的位置是正確的,已知的正數範圍變爲[1,l]
,l++
data[l] <= l
,這種情況說明出現了數組裏已經出現過了該數或者負數和0,那麼r表示的範圍也需要縮小變爲r-1
data[l] > r
,這種情況說明數組裏出現了無效數據,理想情況下r
表示的是[1,r]
的範圍,但此時由於出現了無效數據(例如一個長度爲5的數組裏出現data[i]==79
,那麼該數組的理想情況表示範圍變成了[1,4]
),因此從原來的[1,r]
變爲[1, r-1]
。在減少r
之前,首先需要把data[r]
的數據拷貝到data[l]
上做一次新的判斷(否則就漏了一個數)- 如果
data[data[l]-1] == data[l]
,說明數組中出現了重複數據,那麼r
的表示範圍也需要縮小變爲r-1
- 上述情況都不屬於的情況下,說明這個數據是一個合理將
l
與data[l]-1
(理想情況下的位置)中的數據交換
迭代停止的條件爲l == r
,因此最後搜索到的能表示的正數範圍爲[1, l]
,所求的是未出現的最小正整數,即爲l+1
時間複雜度,空間複雜度
示例
示例1
輸入爲: 3 5 1 4 2
l = 0
,r = 5
數組 | l | r | data[l] | 狀態 |
---|---|---|---|---|
初始 [3, 5, 1, 4, 2] | 0 | 5 | ||
[1, 5, 3, 4, 2] | 0 | 5 | 3 | swap(0, 2) |
[1, 5, 3, 4, 2] | 1 | 5 | 1 | data[l] == l+1 |
[1, 2, 3, 4, 5] | 1 | 5 | 5 | swap(1, 4) |
[1, 2, 3, 4, 5] | 2 | 5 | 2 | data[l] == l+1 |
[1, 2, 3, 4, 5] | 3 | 5 | 3 | data[l] == l+1 |
[1, 2, 3, 4, 5] | 4 | 5 | 4 | data[l] == l+1 |
[1, 2, 3, 4, 5] | 5 | 5 | 5 | data[l] == l+1 |
示例2
輸入爲: [9, 1, 4, 4, -3, 8, 2]
l = 0
,r = 7
數組 | l | r | data[l] | 狀態 |
---|---|---|---|---|
初始 [9, 1, 4, 4, -3, 8, 2] | 0 | 7 | ||
[2, 1, 4, 4, -3, 8, 2, 2] | 0 | 6 | 9 | data[l] > r |
[1, 2, 4, 4, -3, 8, 2, 2] | 0 | 6 | 2 | swap(0, 1) |
[1, 2, 4, 4, -3, 8, 2, 2] | 1 | 6 | 1 | data[l] == l + 1 |
[1, 2, 4, 4, -3, 8, 2, 2] | 2 | 6 | 2 | data[l] == l + 1 |
[1, 2, -3, 4, 4, 8, 2, 2] | 2 | 6 | 4 | swap(2, 3) |
[1, 2, 8, 4, 4, 8, 2, 2] | 2 | 5 | -3 | data[l] <= l |
[1, 2, 4, 4, 4, 8, 2, 2] | 2 | 4 | 8 | data[l] > r |
[1, 2, 4, 4, 4, 8, 2, 2] | 2 | 3 | 4 | data[data[l] - 1] == data[l] |
[1, 2, 4, 4, 4, 8, 2, 2] | 2 | 2 | 4 | data[data[l] - 1] == data[l] |
實現
public static int disappearMinPositiveNum(int[] data) {
int l = 0, r = data.length;
while (l < r) {
if (data[l] == l + 1) {
l += 1;
} else if (data[l] <= l || data[l] > r || data[data[l] - 1] == data[l]) {
data[l] = data[--r];
} else {
swap(data, l, data[l] - 1);
}
}
return l + 1;
}
/**
* 交換
**/
public static void swap(int[] data, int x, int y) {
int tmp = data[x];
data[x] = data[y];
data[y] = tmp;
}