题意:给定节点数n和所有节点的深度总和d,问能否构造出这样的二叉树。能,则输出“YES”,并且输出n-1个节点的父节点(节点1为根节点)。
题解:n个节点构成的二叉树中,完全(满)二叉树的深度总和最小,单链树(左/右偏数)的深度总和最大。若d在这个范围内,则一定能构造出来;否则一定构造不出来。
1.初始构造一颗单链树,依次把底部的节点放入上面的层,直到满足深度总和为d
2.若当前深度总和sum > d,则先拿掉底端节点。
拿掉后,若sum依然比d大,就直接把底端节点放入有空位的最上层;
拿掉后sum <= d,dif = d - sum。
若dif >= 此时有空位的最上层深度,则深度为dif的层一定有空位,把底端节点放入该层,即可完成构造。
否则,依然把底端节点放入有空位的最上层,修改后的sum依旧比d大,继续循环即可。
3.退出循环后就完成了构造,获得了所求的树。
具体存储结构、表示方式和算法过程见代码(和注释):
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 /* 5 9 21 6 YES 7 1 1 2 2 4 4 6 8 8 9 22 9 YES 10 1 1 2 2 4 6 6 7 11 */ 12 int layer[5005], num[5005]; //layer[i]先存第i+1个点所在层的深度,num[i]是深度为i的层里的节点数 13 14 int main() { 15 int t, n, d; 16 scanf("%d", &t); 17 while (t--) { 18 scanf("%d%d", &n, &d); 19 memset(num, 0, sizeof num); 20 int sum = n * (n - 1) / 2, dep = 0, minn = 0; 21 num[0] = 1; //0深度层只有一个根节点 22 for (int i = 2; i <= n; i++) { 23 //i&(i - 1)的结果为把i二进制下最后一个1置0。i&(i - 1) == 0时,i为2的整数次幂 24 if ((i&(i - 1)) == 0)dep++; //第i+1层的第一个节点为2^i 25 minn += dep; //minn记录满二叉树时的深度总和 26 27 layer[i - 1] = i - 1; //单链树时,和为sum,layer[i]是第i+1个点所在层的深度 28 num[i - 1] = 1; //num[i]记录深度为i的层的节点总数 29 } 30 if (d<minn || d>sum) { 31 puts("NO"); 32 continue; 33 } 34 puts("YES"); 35 dep = 1; //当前有空位的最上层的深度 36 for (int i = n - 1; i > 0 && sum > d; i--) { 37 sum -= i; //拿掉底端顶点 38 num[i]--; 39 if (sum > d) { //拿掉之后,sum仍然比d大时;直接放最上面 40 layer[i] = dep; //第i+1个点现在的深度为dep 41 sum += dep; 42 43 if (++num[dep] == (1 << dep))dep++; //若最上面的层满了,修改为下一层 44 } 45 else { //拿掉之后,sum<=d时 46 int dif = d - sum; //看差值对应的层是否有空位 47 if (dif >= dep) { //有空位,则直接放到深度等于差值的那一层,构造成功 48 layer[i] = dif; 49 sum += dif; //写出来更好理解 50 num[dif]++; //该层节点数++ 51 break; 52 } 53 else { //无空位,只能放最上面dep层 54 layer[i] = dep; 55 sum += dep; //此时sum仍然 > d 56 if (++num[dep] == (1 << dep))dep++; //若最上面的层满了,修改为下一层 57 } 58 } 59 } 60 //构造成功。layer[i]是原来单链树中深度为i的点(第i+1个点) 现在的深度,num[i]是第i层的节点总数 61 //现只用num中的信息求解;layer中的信息只是辅助理解,现在用来存最终答案(即第i个节点的父节点编号) 62 int id(2), fid(1); //当前节点编号,上一层首个节点的编号 63 for (int i = 1; num[i]; i++) { // while(深度为i的层节点数不为0) 64 for (int j = 0; j < num[i]; j++) { 65 //深度为i的层的第j个节点,在完全二叉树中的编号为(1<<i)+j,上一层首个节点编号为1<<(i - 1) 66 //layer[id++] = fid + ((1 << i) + j) / 2 - (1 << (i - 1)); 直接算这个式子会溢出 67 layer[id++] = fid + j / 2; //简化后得出,也可以直接理解推出来 68 } 69 fid += num[i - 1]; 70 } 71 for (int i = 2; i < n; i++) 72 printf("%d ", layer[i]); 73 printf("%d ", layer[n]); 74 } 75 return 0; 76 }
附 完全二叉树编号:
1
2 3
4 5 6 7
8 9 10 11 12 13 14 15