题目来自:陈利人老师的微博和微信!
给定数组A,大小为n,数组元素为1到n的数字,不过有的数字出现了多次,有的数字没有出现。请给出算法和程序,统计哪些数字没有出现,哪些数字出现了多少次。能够在O(n)的时间复杂度,O(1)的空间复杂度要求下完成么?
思路:
如果现在我们有一个树的表示如下
[-1,0,0,1,0,2,1,4,2,7,6,7,10]
可见树的根为0.而1的父亲结点为0,而0为根结点。更多:3的父亲结点是1,而1的父亲结点是0,而0是根节点。所以我们可以得到下面的代码:
#include <iostream>
#include <stdlib.h>
using namespace std;
int main(int argc, char *argv[])
{
int father[13] = {-1,0,0,1,0,2,1,4,2,7,6,7,10};
int len[13] = {0};
for(int i = 0; i < 13; i++){
int j = i;
while(father[j] != -1){
j = father[j];
len[i] ++;
}
}
for(int i = 0; i < 13; i++)
cout << len[i] << " ";
cout << endl;
system("PAUSE");
return 0;
}
得到的结果是:0 1 1 2 1 2 2 2 2 3 3 3 4
则进行一次排序可以得到结果。
反思路:如果某一数在原father数组没有出现,则该数所对应的结点为叶子结点,反过来求也是可以的。
那么是否能够继续改进呢?通过上面的计算过程,我们可以发现,在计算4->2->3->-1的时候,显然2->3->-1已经计算过了,不需要再浪费时间重新计算一遍。示例代码如下:
#include <iostream>
#include <stdlib.h>
using namespace std;
int father[13] = {-1,0,0,1,0,2,1,4,2,7,6,7,10};
int len[13] = {0};
int curlen(int i){
if(father[i] == -1)
return 0;
else{
if(len[father[i]] != 0)
return len[father[i]] + 1;
else
return curlen(father[i]) + 1;
}
}
int main(int argc, char *argv[])
{
for(int i = 0; i < 13; i++){
len[i] = curlen(i);
}
for(int i = 0; i < 13; i++){
cout << len[i] << " ";
}
cout << endl;
system("PAUSE");
return 0;
}
陈老师的代码: