路径数组变为统计数组
题目描述
给定一个路径统计数组paths,表示一张图。paths[i] == j代表城市i连向城市j,如果paths[i] == i,则表示i城市是首都,一张图里只会有一个首都且图中除首都指向自己之外不会有环。例如,paths=[9,1,4,9,0,4,8,9,0,1],代表的图如图9-14所示。
由数组表示的图可以知道,城市1是首都,所以距离为0,离首都距离为1的城市只有城市9,离首都距离为2的城市有城市0、3和7,离首都距离为3的城市有城市4和8,离首都距离为4的城市有城市2、5和6。所以距离为0的城市有1座,距离为1的城市有1座,距离为2的城市有3座,距离为3的城市有2座,距离为4的城市有3座。那么统计数组为nums=[1,1,3,2,3,0,0,0,0,0]
[要求]
时间复杂度为 O ( n ) O(n) O(n),额外空间复杂度为 O ( 1 ) O(1) O(1)
输入描述:
第一行有一个整数N,表示节点个数。
接下来一行有N个整数表示paths。其中第i个整数表示第i-1个节点的祖先
输出描述:
输出N个整数表示nums。
示例1
输入
10
9 1 4 9 0 4 8 9 0 1
输出
1 1 3 2 3 0 0 0 0 0
备注:
N
⩽
2
∗
1
0
5
N \leqslant 2∗10^5
N⩽2∗105
保证输入合法。
题解:
解法一:
若不考虑空间要求,可以重新建图,然后 BFS 遍历即可。。。
解法二:
题目已经给出了图的层次信息,我们可以按照给的信息迭代(老麻烦了):
- 首先对 paths 数组进行改写,paths[i] == j 表示城市 i 距离首都的距离为 j 的绝对值,paths中的值设置为负数
- 在改写 paths 数组后,在 paths 数组上迭代修改成个数。。。
paths 数组改写:
- 从左到右遍历paths,先遍历到位置0:
paths[0] == 9,首先令paths[0] == -1(这是为了标记起跳的城市),因为城市0指向城市9,所以跳到城市9.
跳到城市9之后,paths[9] == 1,说明下一个城市是1,因为城市9是由城市0跳过来的,所以先令paths[9] = 0,然后跳向城市1。
跳到城市1之后,paths[1] == 1,说明城市1是首都。现在开始往回跳,城市1是由城市9跳过来的,所以跳回城市9。
根据之前设置的paths[9] == 0,我们知道城市9是由城市0跳过来的,在回跳之前先设置paths[9] = -1,表示城市9到首都的距离为1,之后回跳到0。
根据之前的设置paths[0] == -1,我们知道城市0是起跳位置,所以不再回跳,令paths[0] == -2,表示到首都的距离为2。
以上在跳向首都的过程中,paths数组有一个路径反指的过程,这是为了保证找到首都之后,能够完全跳回来。在跳回来的过程中,设置好这一路所跳的城市 即可。 - 对于其他位置,跳跃的过程同上,但是跳跃终止的条件可以不是跳到首都,当我们跳到下一个位置发现它的值是负数,说明这个位置已经计算出了到首都的距离,我们只要在这个基础上来设置距离即可。
- 首都我们单独处理即可,找到首都的位置然后将它的值设为0,表示距离为0。
paths 数组改写后的迭代:
得到距离数组后,数组中的距离值都用负数表示。接下来我们根据距离数组来计算得到统计数组,该过程也是一个跳跃的过程。
从左到右遍历数组,假设遍历到为 i,paths[i] == -j,那么我们可以知道城市 i 距离首都的距离是 j ,先令paths[i] == 0,表示此位置不再代表距离,然后跳到位置 j 处,如果位置 j 处的值为负数 -k,我们令 paths[j] = 1,表示我们已经找到一个距离为 j 的城市(城市 i )。然后根据 k 继续往下跳。如果位置 j 处的值为正数,说明该位置已经是距离为 j 的城市的数量统计,直接加1即可。
解法二代码:
#include <cstdio>
using namespace std;
const int N = 200000;
int n;
int paths[N];
void pathsToDis() {
int cap = -1;
for ( int i = 0; i < n; ++i ) {
if ( paths[i] == i ) cap = i;
else if ( paths[i] > -1 ) {
int cur = paths[i];
int pre = i;
paths[i] = -1;
while ( paths[cur] != cur ) {
if ( paths[cur] > -1 ) {
int nxt = paths[cur];
paths[cur] = pre;
pre = cur;
cur = nxt;
} else break;
}
int val = paths[cur] == cur ? 0 : paths[cur];
while ( paths[pre] != -1 ) {
int ppre = paths[pre];
paths[pre] = --val;
pre = ppre;
}
paths[pre] = --val;
}
}
paths[cap] = 0;
}
void disToNums() {
for ( int i = 0; i < n; ++i ) {
int idx = paths[i];
if ( idx < 0 ) {
paths[i] = 0;
while ( true ) {
idx = -idx;
if ( paths[idx] > -1 ) {
++paths[idx];
break;
} else {
int nxt = paths[idx];
paths[idx] = 1;
idx = nxt;
}
}
}
}
paths[0] = 1;
}
int main(void) {
scanf("%d", &n);
for ( int i = 0; i < n; ++i ) scanf("%d", paths + i);
pathsToDis();
disToNums();
for ( int i = 0; i < n; ++i )
printf("%d%c", paths[i], " \n"[i == n - 1]);
return 0;
}