深信服入职前编码训练21题--02
题目描述:
有一个节点数组,需要创建一棵最优二叉树,即每个节点的权值乘以节点在树中的长度,然后相加得到的值最小。以下图一为例,节点数组的[A,B,C,D,E]的权值分别为[15,7,6,6,5],构建好的最优二叉树见下图。
相关代码如下,请补充缺失部分。
```
struct node {
int left, right, parent;
int val;
};
int build_tree(struct node arr[], int cnt)
{
while (1) {
int i;
int min1 = -1; //权值最小的节点编号
int min2 = -1; //权值第二小的节点编号
int root_node = 0; //根节点(没有父节点)的个数
for (i = 0; i < cnt; ++i) {
if (arr[i].____________ >= 0)
continue;
++root_node;
if (min1 < 0) {
min1 = i;
} else if (arr[i].val < ____________) {
min2 = min1;
min1 = i;
} else if (min2 < 0) {
min2 = i;
} else if (arr[i].val < ____________) {
min2 = i;
}
}
if (root_node < ____________)
break;
arr[cnt].left = min2;
arr[cnt].right = min1;
arr[cnt].val = arr[min1].val + ____________;
arr[cnt].parent = -1;
arr[min1].parent = cnt;
arr[min2].parent = cnt;
++cnt;
}
return cnt;
}
```
输入描述:
第一行为数据个数 第二行为权值(整数)
输出描述:
构建的二叉树(用于绘图软件生成对应的二叉树图形)
示例1
输入
5
15 7 6 6 5
输出
```mermaid
graph TD
n0[n0:15]
n0 --> n8
n1[n1:7]
n1 --> n6
n2[n2:6]
n2 --> n5
n3[n3:6]
n3 --> n6
n4[n4:5]
n4 --> n5
n5((11))
n5 --> n7
n6((13))
n6 --> n7
n7((24))
n7 --> n8
n8((39))
```
说明
1.grath TD下面的输出都是\t开头
2.n0 ---> n8 的意思是n0的父节点是n8
分析:这道题是典型的哈夫曼树的构造,只需补充完树的构建部分即可。
解答:
1 #include <stdio.h>
2 #include <limits.h>
3 #include <assert.h>
4 #include <malloc.h>
5
6 struct node {
7 int left, right, parent;
8 int val;
9 };
10
11 void tree_print(const struct node arr[], int cnt)
12 {
13 int i;
14 for (i = 0; i < cnt; ++i) {
15 fprintf(stderr, "%d: {left:%d,right:%d,parent:%d,val:%d}\n"
16 , i, arr[i].left, arr[i].right, arr[i].parent, arr[i].val);
17 }
18 }
19
20 void tree_output(FILE *fp, const struct node arr[], int old_cnt, int cnt)
21 {
22 int i;
23 fprintf(fp, "```mermaid\n");
24 fprintf(fp, "graph TD\n");
25 for (i = 0; i < cnt; ++i) {
26 if (i < old_cnt)
27 fprintf(fp, "\tn%d[n%d:%d]\n", i, i, arr[i].val);
28 else
29 fprintf(fp, "\tn%d((%d))\n", i, arr[i].val);
30
31 if (arr[i].parent >= 0) {
32 fprintf(fp, "\tn%d --> n%d\n", i, arr[i].parent);
33 }
34 }
35 fprintf(fp, "```\n");
36 }
37
38 int build_tree(struct node arr[], int cnt);
39
40 static int input(int **arr, int *size)
41 {
42 int i;
43 int ret;
44
45 ret = fscanf(stdin, "%d\n", size);
46 if (ret != 1)
47 return -1;
48 if (*size <= 0)
49 return -1;
50 *arr = (int *)malloc(sizeof(int) * (*size));
51 for (i = 0; i < *size; ++i) {
52 fscanf(stdin, "%d ", &(*arr)[i]);
53 }
54 return 0;
55 }
56 int main(int argc, char *argv[])
57 {
58 int *vals = NULL;
59 int cnt = 0;
60 struct node *arr;
61 int i;
62
63 if (input(&vals, &cnt) < 0) {
64 fprintf(stderr, "input error\n");
65 return 0;
66 }
67 arr = (struct node *)malloc(sizeof(struct node) * cnt * 3);
68
69 for (i = 0; i < cnt; ++i) {
70 arr[i].left = -1;
71 arr[i].right = -1;
72 arr[i].parent = -1;
73 arr[i].val = vals[i];
74 }
75
76 int newcnt = build_tree(arr, cnt);
77 tree_output(stdout, arr, cnt, newcnt);
78 free(vals);
79 free(arr);
80 return 0;
81 }
82
83 // 建树,返回树的根节点
84 int build_tree(struct node arr[], int cnt)
85 {
86 while (1) {
87 int i;
88 int min1 = -1; //权值最小的节点编号
89 int min2 = -1; //权值第二小的节点编号
90 int root_node = 0; //根节点(没有父节点)的个数
91
92 for (i = 0; i < cnt; ++i) {
93 if (arr[i].parent>= 0) //拥有父节点,则跳过这个结点
94 continue;
95 ++root_node; //根节点加1
96 if (min1 < 0) {
97 min1 = i;
98 } else if (arr[i].val < arr[min1].val) { //出现比第一更小的,更新
99 min2 = min1;
100 min1 = i;
101 } else if (min2 < 0) {
102 min2 = i;
103 } else if (arr[i].val < arr[min2].val) { //当前节点权值比第二小节点更小
104 min2 = i;
105 }
106 }
107 if (root_node < 2)
108 break;
109 arr[cnt].left = min2;
110 arr[cnt].right = min1;
111 arr[cnt].val = arr[min1].val + arr[min2].val;
112 arr[cnt].parent = -1;
113 arr[min1].parent = cnt;
114 arr[min2].parent = cnt;
115 ++cnt;
116 }
117 return cnt;
118 }