题目描述
给定一个随机的整数数组,含有0 和 非0的整数。
要求:将0放到数组前面,非0整数放到后面,且非0整数的相对次序不能改变。空间复杂度要求T(n),换言之不能新建辅助数组,时间复杂度要求O(n)。
例:
输入:1, 0, 2, 0, 3, 0, 0, 4, 5, 0, 6, 7
输出:0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7
前言
我同学参加cvte校招的电话面试时被问到的一题算法题,然后跟我讨论了一下。大概题意,如上面所描述。不过面试官只要求了空间复杂度为T(n),并没有对时间复杂度作出明确要求。可能就是考考思维吧。同学给我讲了题意后,我自己拿笔在草稿本上瞎比划,首先很自然想到的是有点类似于“冒泡”的操作,但显然时间复杂度为O(n^2)。
后来发现了可以有O(n)的做法。首先统计出数组中0的个数cnt,设两个指针,前指针p从下标0开始,后指针q从下标cnt开始。通过一定规则的交换(参见代码),遍历一遍数组就可以达到题目要求的结果。时间复杂度为O(2n),也就是O(n)。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
// 时间复杂度O(2*n),也是O(n)
void solve(int num[], int n) {
int cnt=0; // 0的个数
for(int i=0; i<n; i++) {
if(num[i]==0) {
cnt++;
}
}
int p=0; // 前指针
int q=cnt; // 后指针
while(p<cnt) {
if(num[p]==0) {
p++;
} else { // num[p]!=0 有两种情况
if(num[q]==0) {
swap(num[p], num[q]);
p++;
q++;
} else {
swap(num[p], num[q]);
q++; // num[q]已经是正确结果,所以指向下一位
// 而num[p]是暂存了原本的num[q]的数,还不是正确结果,所以不移动指针
}
}
}
}
// O(n^2)
void bubble(int num[], int n) {
int flag;
do {
flag=0;
for(int i=1; i<n; i++) {
if(num[i-1]!=0 && num[i]==0) {
flag=1;
swap(num[i-1], num[i]);
}
}
} while(flag);
}
int main() {
int num[] = { 1, 0, 2, 0, 3, 0, 0, 4, 5, 0, 6, 7 };
int n=12;
printf("操作之前:\n");
for(int i=0; i<n; i++) {
printf("%d ", num[i]);
}
// O(n)
solve(num, n);
// O(n^2)
//bubble(num, n);
printf("\n操作之后:\n");
for(int i=0; i<n; i++) {
printf("%d ", num[i]);
}
return 0;
}