题目地址:
https://leetcode.com/problems/first-missing-positive/
给定一个长为 n n n的数组,返回从 1 , 2 , 3 , . . . 1,2,3,... 1,2,3,...中第一个未出现在数组里的数。基本思路是,遍历数组时,如果遍历到的数字 k k k且 k ∈ { 1 , 2 , . . . , n } k\in\{1,2,...,n\} k∈{1,2,...,n},就把 k k k给swap到 n u m s [ k − 1 ] nums[k-1] nums[k−1],直到无法满足swap条件时,再遍历下一个数字。遍历完成之后,只需返回数组从左到右第一个 i + 1 i+1 i+1使得 n u m s [ i ] = i + 1 nums[i]=i+1 nums[i]=i+1即可。例如对于 ( 3 , 4 , − 1 , 1 ) (3,4,-1,1) (3,4,−1,1)进行下列交换: ( -1 , 4 , 3 , 1 ) (\textbf{-1},4,\textbf{3},1) (-1,4,3,1) ( − 1 , 1 , 3 , 4 ) (-1,\textbf{1},3,\textbf{4}) (−1,1,3,4) ( 1 , -1 , 3 , 4 ) (\textbf{1},\textbf{-1},3,4) (1,-1,3,4)最后得到的是: ( 1 , − 1 , 3 , 4 ) (1,-1,3,4) (1,−1,3,4)可以看出,凡是在 1 ∼ n 1\sim n 1∼n之间的数都已经归位了,接下来只需要从左向右遍历,返回第一个未出现的正整数就行了。
class Solution {
public:
int firstMissingPositive(vector<int>& a) {
int n = a.size();
for (int i = 0; i < n; i++)
while (1 <= a[i] && a[i] <= n && a[a[i] - 1] != a[i])
swap(a[i], a[a[i] - 1]);
for (int i = 0; i < n; i++)
if (a[i] != i + 1) return i + 1;
return n + 1;
}
};
时间复杂度 O ( n ) O(n) O(n)。
算法正确性证明:
显然,对于
{
1
,
2
,
3
,
.
.
.
}
\{1,2,3,...\}
{1,2,3,...}里第一个不属于数组的数
r
r
r,
∀
k
∈
{
1
,
2
,
.
.
.
,
r
−
1
}
\forall k\in \{1,2,...,r-1\}
∀k∈{1,2,...,r−1},在数组遍历完后一定放在了数组的第
k
k
k个位置,也就是
n
u
m
s
[
k
−
1
]
nums[k-1]
nums[k−1]。所以第二个for循环可以把
r
r
r找出来,算法正确性证明完毕。
算法复杂度证明:
只需证明swap的执行次数不超过
n
n
n即可。可以理解为,每次进行swap(nums, nums[i] - 1, i)
的时候,就对数组的下标
n
u
m
s
[
i
]
−
1
nums[i] - 1
nums[i]−1进行染色。显然同一个位置最多染色一次,并且染色次数不会超过
n
n
n,所以swap执行次数不超过
n
n
n。