真题题目
给定一个含n(n≥1)个整数的数组,请设计一个在时间上尽可能高效的算法,找出数组中未出现的最小正整数。例如,数组{-5, 3, 2, 3}中未出现的最小正整数是1;数组{1, 2, 3}中未出现的最小正整数是4。要求:
- 给出算法的基本设计思想。
- 根据设计思想,采用C、C++或 Java 语言描述算法,关键之处给出注释。
- 说明你所设计算法的时间复杂度和空间复杂度。
(本文重点关注算法实现思路,不含具体答题表述)
分析实现
通常“未出现xxx”一类的题目,我们会想到先记录下数组中出现的元素信息,然后进行分析。
对于本题,要求返回第一个未出现的正整数,又因为数组元素个数为n,故最小未出现正整数一定在区间
[
1
,
n
+
1
]
[1, n+1]
[1,n+1] 内。因此,可建立一个大小为n的布尔数组存储正整数
[
1
,
n
]
[1, n]
[1,n]的出现情况,倘若
[
1
,
n
]
[1, n]
[1,n]均出现,则最小未出现正整数为n+1。(实际上也可以直接建立n+1的布尔数组存储
[
1
,
n
+
1
]
[1, n+1]
[1,n+1]的出现情况)
具体实现如下:(为了简洁,这里新建了n+1的布尔数组,使得a[i]可以直接表示元素i的出现情况)
# include<vector>
int minAbsent(vector<int>& a){
int n=a.size();
// 布尔数组, 若a中有i, 则has[i]=true
vector<bool> has(n+1, 0);
for(auto &x:a){
if(x>0 && x<=n){
has[x]=1;
}
}
// 扫描has数组, 找到第一个未出现正整数
for(int i=1; i<=n; i++){
if(!has[i]){
return i;
}
}
// [1,n]均出现, 返回n+1
return n+1;
}
总结
以上就是运用一个数组记录元素出现情况,然后扫描该数组来求出数组中未出现的最小正整数的算法。
此外,抱着学习的态度(个人认为并不会出现这样的考题),如果对原题目进行两点修改:
- 数组内元素递增
- 空间复杂度为: O ( 1 ) O(1) O(1)
为达到新的目标,可以直接在该代码上进行一些改变——用原始数组中扫描过的元素存储正整数的出现情况,具体实现如下:
int minAbsent(vector<int>& a){
int n=a.size();
// 扫描到a[i]时, 最大有正整数i+1已经出现
// 故令: 将当前元素x的出现情况存储在扫描后的a[x-1]中
for(int i=0; i<n; i++){
int x=a[i];
a[i]=0;
if(x>=1 && x<=n){
a[x-1] = 1;
}
}
// 扫描a数组, 找到第一个未出现正整数
for(int i=0; i<n; i++){
if(!a[i]){
return i+1;
}
}
// [1,n]均出现, 返回n+1
return n+1;
}
但对应地,这种优化也有相应的代价——原数组内的元素被修改了,具体在实际问题中是否采用此策略还需要慎重考虑。