原题
给定数组A,大小为n,数组元素为1到n的数字,不过有的数字出现了多次,有的数字没有出现。请给出算法和程序,统计每个数字出现了多少次。能够在O(n)的时间复杂度,O(1)的空间复杂度要求下完成么?
在原文上做了点修改,原文链接:http://mp.weixin.qq.com/s?__biz=MjM5ODIzNDQ3Mw==&mid=200446711&idx=1&sn=09b2db28fcfa289cfb69d60f066d1d91#rd
分析
这个题目,是有一定技巧的。技巧是需要慢慢积累,待经验多了之后,可以灵感或者直觉,就产生了技巧。如果不知道技巧,那该怎么办呢?
在开始分析之前,说明两个问题:
-
原数组是没有排序的。如果排序了,很简单的。
-
O(1)的空间含义,可以使用变量,但不能开辟数组或者map等来计数。
这个题目,很直接的解法就是两层遍历,O(n^2)的复杂度,O(1)的空间。空间满足了,但是时间没有。
很多类似的题目,都会用XOR的方法(异或吗?),大家仔细想一下,这个题目,可以么?或者这个题目和可以用XOR的题目的差异在哪儿?最直接的就是,每一个数字的重复的次数是不同的。
还有就是以空间换时间的方法,例如用hash map或者数组来计数。时间满足了,但是空间没有满足。
那怎样才能有时间复杂度O(n),空间复杂度O(1)的算法呢?不能开辟新的空间,那么只剩下,重复利用数组A(因为数组中所有元素都不大于n,所以可以好好利用这一特点)。那么该如何利用数组A呢?
要知道每一个数字出现的次数,而且只能遍历一次,这种情况下当一个数字出现的时候,就要对它进行标记。比如考虑如下数组A[6],n=6:
0 | 1 | 2 | 3 | 4 | 5 |
2 | 3 | 1 | 4 | 4 | 2 |
代码如下:时间复杂度o(n),空间复杂度o(1)
void count(int [] A, int n){
for(int i=0;i<n;++i){
A[A[i]%n-1]+=n;
}
for(int i=0;i<n;++i){
cout<<"i+1出现了"<<A[i]/n<<"次\n";
}
}