第五次作业
1. 树叶节点遍历(树-基础题)
从标准输入中输入一组整数,在输入过程中按照左子结点值小于根结点值、右子结点值大于等于根结点值的方式构造一棵二叉查找树,然后从左至右输出所有树中叶结点的值及高度(根结点的高度为1)。例如,若按照以下顺序输入一组整数:50、38、30、64、58、40、10、73、70、50、60、100、35,则生成下面的二叉查找树:
从左到右的叶子结点包括:10、35、40、50、60、70、100,叶结点40的高度为3,其它叶结点的高度都为4。
【输入形式】
先从标准输入读取整数的个数,然后从下一行开始输入各个整数,整数之间以一个空格分隔。
【输出形式】
按照从左到右的顺序分行输出叶结点的值及高度,值和高度之间以一个空格分隔。
【样例输入】
13
50 38 30 64 58 40 10 73 70 50 60 100 35
【样例输出】
10 4
35 4
40 3
50 4
60 4
70 4
100 4
【样例说明】
按照从左到右的顺序输出叶结点(即没有子树的结点)的值和高度,每行输出一个。
【评分标准】
该题要求输出所有叶结点的值和高度,提交程序名为:bst.c
【参考答案】
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int num;
int height;
struct node *left, *right;
} node, *bitree;
bitree insert(bitree root, int num, int height)
{
if (!root)
{
root = (bitree)malloc(sizeof(node));
root->num = num;
root->height = height;
root->left = root->right = NULL;
}
else if (num < root->num)
{
root->left = insert(root->left, num, height + 1);
}
else
{
root->right = insert(root->right, num, height + 1);
}
return root;
}
void dfs(bitree root)
{
if (!root->left && !root->right)
{
printf("%d %d\n", root->num, root->height);
return;
}
if (root->left)
{
dfs(root->left);
}
if (root->right)
{
dfs(root->right);
}
}
int main()
{
int n;
scanf("%d", &n);
bitree bst = NULL;
for (int i = 0; i < n; i++)
{
int num;
scanf("%d", &num);
bst = insert(bst, num, 1);
}
dfs(bst);
return 0;
}
2. 词频统计(树实现)
【问题描述】
编写程序统计一个英文文本文件中每个单词的出现次数(词频统计),并将统计结果按单词字典序输出到屏幕上。
要求:程序应用二叉排序树(BST)来存储和统计读入的单词。
注:在此单词为仅由字母组成的字符序列。包含大写字母的单词应将大写字母转换为小写字母后统计。在生成二叉排序树不做平衡处理。
【输入形式】
打开当前目录下文件article.txt,从中读取英文单词进行词频统计。
【输出形式】
程序应首先输出二叉排序树中根节点、根节点的右节点及根节点的右节点的右节点上的单词(即root、root->right、root->right->right节点上的单词),单词中间有一个空格分隔,最后一个单词后没有空格,直接为回车(若单词个数不足三个,则按实际数目输出)。
程序将单词统计结果按单词字典序输出到屏幕上,每行输出一个单词及其出现次数,单词和其出现次数间由一个空格分隔,出现次数后无空格,直接为回车。
【样例输入】
当前目录下文件article.txt内容如下:
“Do not take to heart every thing you hear.”
“Do not spend all that you have.”
“Do not sleep as long as you want;”
【样例输出】
do not take
all 1
as 2
do 3
every 1
have 1
hear 1
heart 1
long 1
not 3
sleep 1
spend 1
take 1
that 1
thing 1
to 1
want 1
you 3
【样例说明】
程序首先在屏幕上输出程序中二叉排序树上根节点、根节点的右子节点及根节点的右子节点的右子节点上的单词,分别为do not take,然后按单词字典序依次输出单词及其出现次数。
【评分标准】
通过全部测试点得满分
【参考答案】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct node
{
int freq;
char word[20];
struct node *left, *right;
} node, *bitree;
int getword(char *word, FILE *fp)
{
char ch = fgetc(fp);
while (!isalpha(ch))
{
if (ch == EOF)
{
return -1;
}
ch = fgetc(fp);
}
int i = 0;
while (isalpha(ch))
{
*(word + i++) = tolower(ch);
ch = fgetc(fp);
}
*(word + i) = 0;
return 0;
}
bitree insert(bitree root, char *word)
{
if (!root)
{
root = (bitree)malloc(sizeof(node));
root->freq = 1;
strcpy(root->word, word);
root->left = root->right = NULL;
}
else if (strcmp(root->word, word) == 0)
{
root->freq += 1;
}
else if (strcmp(root->word, word) > 0)
{
root->left = insert(root->left, word);
}
else
{
root->right = insert(root->right, word);
}
return root;
}
void dfs(bitree root)
{
if (!root)
{
return;
}
dfs(root->left);
printf("%s %d\n", root->word, root->freq);
dfs(root->right);
}
int main()
{
FILE *fp = fopen("article.txt", "r");
bitree bst = NULL;
char word[20];
while (~getword(word, fp))
{
bst = insert(bst, word);
}
if (bst)
{
printf("%s ", bst->word);
if (bst->right)
{
printf("%s ", bst->right->word);
if (bst->right->right)
{
printf("%s ", bst->right->right->word);
}
}
puts("");
}
dfs(bst);
return 0;
}
3. 计算器(表达式计算-表达式树实现)
【问题描述】
从标准输入中读入一个整数算术运算表达式,如24 / ( 1 + 2 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 )= ,计算表达式结果,并输出。
要求:
1、表达式运算符只有+、-、*、/,表达式末尾的=字符表示表达式输入结束,表达式中可能会出现空格;
2、表达式中会出现圆括号,括号可能嵌套,不会出现错误的表达式;
3、出现除号/时,以整数相除进行运算,结果仍为整数,例如:5/3结果应为1。
4、要求采用表达式树来实现表达式计算。
表达式树(expression tree):
我们已经知道了在计算机中用后缀表达式和栈来计算中缀表达式的值。在计算机中还有一种方式是利用表达式树来计算表达式的值。表达式树是这样一种树,其根节点为操作符,非根节点为操作数,对其进行后序遍历将计算表达式的值。由后缀表达式生成表达式树的方法如下:
-
读入一个符号:
-
如果是操作数,则建立一个单节点树并将指向他的指针推入栈中;
-
如果是运算符,就从栈中弹出指向两棵树T1和T2的指针(T1先弹出)并形成一棵新树,树根为该运算符,它的左、右子树分别指向T2和T1,然后将新树的指针压入栈中。
例如输入的后缀表达为:
ab+cde+**
则生成的表达式树为:
【输入形式】
从键盘输入一个以=结尾的整数算术运算表达式。操作符和操作数之间可以有空格分隔。
【输出形式】
首先在屏幕上输出表达式树根、左子节点及右子节点上的运算符或操作数,中间由一个空格分隔,最后有一个回车(如果无某节点,则该项不输出)。然后输出表达式计算结果。
【样例输入】
24 / ( 1 + 2 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 ) =
【样例输出】
* / /
18
【样例说明】
按照运算符及括号优先级依次计算表达式的值。在生成的表达树中,*是根节点的运算符,/ 是根节点的左子节点上运算符,/是根节点的右子节点上运算符,按题目要求要输出。
【评分标准】
通过所有测试点得满分。
【参考答案】
写了一个非常冗长的代码,但是还挺清晰的,希望能够有参考价值。可拓展性也不错,如果能够简化一下就好了。
总体来说就是先生成后缀表达式,然后建树。虽然这题看上去没什么意义,但是重点是让学习者能够通过这题学习表达式树的思想,在日后的课程中能够有一定的基础。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define IVAL 1
#define CVAL 2
typedef struct Elem
{
int tag;
union
{
int ival;
char cval;
};
} Elem;
typedef struct Tnode
{
Elem *ele;
struct Tnode *left;
struct Tnode *right;
} Tnode, *BiTree;
Elem *scanElem(char *exp, int *pos)
{
Elem *res = (Elem *)malloc(sizeof(Elem));
while (exp[*pos] == ' ')
{
(*pos)++;
}
if (isdigit(exp[*pos]))
{
res->tag = IVAL;
int num = 0;
while (isdigit(exp[*pos]))
{
num = num * 10 + exp[*pos] - '0';
(*pos)++;
}
res->ival = num;
}
else
{
res->tag = CVAL;
if (exp[*pos] == '=')
{
return NULL;
}
res->cval = exp[*pos];
(*pos)++;
}
return res;
}
int operate(int a, int b, char x)
{
switch (x)
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
}
return 0;
}
int compare(char a, char b)
{
switch (a)
{
case '+':
case '-':
switch (b)
{
case '+':
case '-':
case '=':
case ')':
return 1;
default:
return -1;
}
case '*':
case '/':
switch (b)
{
case '(':
return -1;
default:
return 1;
}
case '(':
switch (b)
{
case ')':
return 0;
default:
return -1;
}
case ')':
return 1;
case '=':
return -1;
}
return -2;
}
int merge(BiTree x)
{
if (x->ele->tag == IVAL)
{
return x->ele->ival;
}
return operate(merge(x->left), merge(x->right), x->ele->cval);
}
int main()
{
char exp[200];
fgets(exp, 200, stdin);
int pos = 0;
Elem *ele = NULL;
int top = -1;
Elem stack[100];
int pe = 0;
Elem e[100];
while ((ele = scanElem(exp, &pos)) != NULL)
{
if (ele->tag == IVAL)
{
// printf("%d\n", ele->ival);
e[pe++] = *ele;
}
else
{
// printf("%c\n", ele->cval);
while (top != -1 && compare(stack[top].cval, ele->cval) > 0)
{
e[pe++] = stack[top--];
}
if (compare(stack[top].cval, ele->cval) == 0)
{
top--;
continue;
}
stack[++top] = *ele;
}
}
while (top != -1)
{
e[pe++] = stack[top--];
}
BiTree TStack[100];
for (int i = 0; i < pe; i++)
{
BiTree p = (BiTree)malloc(sizeof(Tnode));
p->ele = &e[i];
if (e[i].tag == IVAL)
{
// printf("%d", e[i].ival);
p->left = p->right = NULL;
}
else
{
// printf("%c", e[i].cval);
p->right = TStack[top--];
p->left = TStack[top--];
}
TStack[++top] = p;
}
BiTree root = TStack[0];
if (root != NULL)
{
printf("%c ", root->ele->cval);
if (root->left)
{
if (root->left->ele->tag == IVAL)
{
printf("%d ", root->left->ele->ival);
}
else
{
printf("%c ", root->left->ele->cval);
}
}
if (root->right)
{
if (root->right->ele->tag == IVAL)
{
printf("%d ", root->right->ele->ival);
}
else
{
printf("%c ", root->right->ele->cval);
}
}
printf("\n%d", merge(root));
}
return 0;
}
4. 服务优化
【问题描述】
假设某机场所有登机口(Gate)呈树形排列(树的度为3),安检处为树的根,如下图所示。图中的分叉结点(编号大于等于100)表示分叉路口,登机口用小于100的编号表示(其一定是一个叶结点)。通过对机场所有出发航班的日志分析,得知每个登机口每天的平均发送旅客流量。作为提升机场服务水平的一个措施,在不改变所有航班相对关系的情况下(即:出发时间不变,原在同一登机口的航班不变),仅改变登机口(例如:将3号登机口改到5号登机口的位置),使得整体旅客到登机口的时间有所减少(即:从安检口到登机口所经过的分叉路口最少)。
编写程序模拟上述登机口的调整,登机口调整规则如下:
1)首先按照由大到小的顺序对输入的登机口流量进行排序,流量相同的按照登机口编号由小到大排序;
2)从上述登机口树的树根开始,将登机口按照从上到下(安检口在最上方)、从左到右的顺序,依次对应上面排序后将要调整的登机口。
例如上图的树中,若只考虑登机口,则从上到下有三层,第一层从左到右的顺序为:5、6、14、13,第二层从左到右的顺序为:7、8、9、10、1、2、18、17、16、15,第三层从左到右的顺序为:11、12、3、4、20、19。若按规则1排序后流量由大至小的前五个登机口为3、12、16、20、15,则将流量最大的3号登机口调整到最上层且最左边的位置(即:5号登机口的位置),12号调整到6号,16号调整到14号,20号调整到13号,15号调整到第二层最左边的位置(即7号登机口的位置)。
【输入形式】
1)首先按层次从根开始依次输入树结点之间的关系。其中分叉结点编号从数字100开始(树根结点编号为100,其它分叉结点编号没有规律但不会重复),登机口为编号小于100的数字(编号没有规律但不会重复,其一定是一个叶结点)。树中结点间关系用下面方式描述:
R S1 S2 S3 -1
其中R为分叉结点,从左至右S1,S2,S3分别为树叉R的子结点,其可为树叉或登机口,由于树的度为3,S1,S2,S3中至多可以2个为空,最后该行以-1和换行符结束。各项间以一个空格分隔。如:
100 101 102 103 -1
表明编号100的树根有三个子叉,编号分别为101、102和103,又如:
104 7 8 -1
表明树叉104上有2个编号分别为7和8的登机口。
假设分叉结点数不超过100个。分叉结点输入的顺序不确定,但可以确定:输入某个分叉结点信息时,其父结点的信息已经输入。
输入完所有树结点关系后,在新的一行上输入-1表示树结点关系输入完毕。
2)接下来输入登机口的流量信息,每个登机口流量信息分占一行,分别包括登机口编号(1~99之间的整数)和流量(大于0的整数),两整数间以一个空格分隔。登机口数目与前面构造树时的登机机口数目一致。
【输出形式】
按照上述调整规则中排序后的顺序(即按旅客流量由大到小,流量相同的按照登机口编号由小到大)依次分行输出每个登机口的调整结果:先输出调整前的登机口编号,然后输出字符串"->"(由英文减号字符与英文大于字符组成),再输出要调整到的登机口编号。
【样例输入】
100 101 102 103 -1
103 14 108 13 -1
101 5 104 6 -1
104 7 8 -1
102 105 106 107 -1
106 1 110 2 -1
108 16 15 -1
107 18 111 17 -1
110 3 4 -1
105 9 109 10 -1
111 20 19 -1
109 11 12 -1
-1
17 865
5 668
20 3000
13 1020
11 980
8 2202
15 1897
6 1001
14 922
7 2178
19 2189
1 1267
12 3281
2 980
18 1020
10 980
3 1876
9 1197
16 980
4 576
【样例输出】
12->5
20->6
8->14
19->13
7->7
15->8
3->9
1->10
9->1
13->2
18->18
6->17
2->16
10->15
11->11
16->12
14->3
17->4
5->20
4->19
【样例说明】
样例输入了12条树结点关系,形成了如上图的树。然后输入了20个登机口的流量,将这20个登机口按照上述调整规则1排序后形成的顺序为:12、20、8、19、7、15、3、1、9、13、18、6、2、10、11、16、14、17、5、4。最后按该顺序将所有登机口按照上述调整规则2进行调整,输出调整结果。
【评分标准】
该题要求计算并输出登机口的调整方法,提交程序名为adjust.c。
【参考答案】
照着题目意思写呗。
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int pos;
struct node *child[3];
} node, *specific;
typedef struct gate
{
int pos;
int nums;
} gate;
specific hashtable[205];
specific root = NULL, p = NULL;
int parent, tmp, cnt = 0;
gate g[100];
int ans[100][2], pa = 0;
int cmp(const void *a, const void *b)
{
gate *aa = (gate *)a;
gate *bb = (gate *)b;
if ((*bb).nums != (*aa).nums)
return (*bb).nums - (*aa).nums;
else
return (*aa).pos - (*bb).pos;
}
void leveltraversal()
{
specific queue[105];
int front = 0, rear = -1;
queue[++rear] = root;
while (front <= rear)
{
if (queue[front]->child[0])
queue[++rear] = queue[front]->child[0];
if (queue[front]->child[1])
queue[++rear] = queue[front]->child[1];
if (queue[front]->child[2])
queue[++rear] = queue[front]->child[2];
if (queue[front]->pos < 100)
ans[pa++][1] = queue[front]->pos;
front++;
}
}
int main()
{
root = (specific)malloc(sizeof(node));
root->pos = 100;
root->child[0] = root->child[1] = root->child[2] = NULL;
hashtable[100] = root;
scanf("%d", &parent);
while (~parent)
{
scanf("%d", &tmp);
int i = 0;
while (~tmp)
{
if (tmp != -1 && tmp < 100)
cnt++;
p = (specific)malloc(sizeof(node));
p->pos = tmp;
p->child[0] = p->child[1] = p->child[2] = NULL;
hashtable[parent]->child[i++] = p;
hashtable[tmp] = p;
scanf("%d", &tmp);
}
scanf("%d", &parent);
}
int j;
for (j = 0; j < cnt; j++)
scanf("%d%d", &g[j].pos, &g[j].nums);
qsort(g, cnt, sizeof(g[0]), cmp);
for (j = 0; j < cnt; j++)
{
ans[j][0] = g[j].pos;
}
leveltraversal();
for (j = 0; j < cnt; j++)
printf("%d->%d\n", ans[j][0], ans[j][1]);
return 0;
}
实验:树的构造与遍历
题目太长了,而且我也不知道题干有没有改过,就只贴上我以前的代码好了。
【参考答案】
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXSIZE 32
struct tnode
{
char c;
int weight;
struct tnode *left, *right;
};
int Ccount[128] = {0};
struct tnode *Root = NULL;
char HCode[128][MAXSIZE] = {{0}};
int Step = 0;
FILE *Src, *Obj;
void statCount();
void createHTree();
void makeHCode();
void atoHZIP();
void print1();
void print2(struct tnode *p);
void print3();
void print4();
int main()
{
if ((Src = fopen("input.txt", "r")) == NULL)
{
fprintf(stderr, "%s open failed!\n", "input.txt");
return 1;
}
if ((Obj = fopen("output.txt", "w")) == NULL)
{
fprintf(stderr, "%s open failed!\n", "output.txt");
return 1;
}
scanf("%d", &Step);
statCount();
(Step == 1) ? print1() : 1;
createHTree();
(Step == 2) ? print2(Root) : 2;
makeHCode();
(Step == 3) ? print3() : 3;
(Step >= 4) ? atoHZIP(), print4() : 4;
fclose(Src);
fclose(Obj);
return 0;
}
void statCount()
{
char cc = fgetc(Src);
while (cc != EOF)
{
if (cc != '\n')
Ccount[cc]++;
cc = fgetc(Src);
}
Ccount[0]++;
}
int cmp1(const void *a, const void *b)
{
struct tnode **aa = (struct tnode **)a;
struct tnode **bb = (struct tnode **)b;
if ((**bb).weight > (**aa).weight)
return 1;
if ((**bb).weight < (**aa).weight)
return -1;
if ((**bb).c > (**aa).c)
return 1;
if ((**bb).c < (**aa).c)
return -1;
}
void createHTree()
{
struct tnode *F[128], *p = NULL;
int nodecnt = 0, i;
for (i = 0; i < 128; i++)
{
if (Ccount[i] > 0)
{
p = (struct tnode *)malloc(sizeof(struct tnode));
p->c = i;
p->weight = Ccount[i];
p->left = p->right = NULL;
F[nodecnt++] = p;
}
}
qsort(F, nodecnt, sizeof(F[0]), cmp1);
while (nodecnt > 1)
{
p = (struct tnode *)malloc(sizeof(struct tnode));
p->left = F[nodecnt - 1];
p->right = F[nodecnt - 2];
p->weight = p->left->weight + p->right->weight;
F[nodecnt - 2] = p;
nodecnt--;
i = nodecnt;
while (i > 0 && F[i - 1]->weight <= p->weight)
{
F[i] = F[i - 1];
i--;
}
F[i] = p;
}
Root = F[0];
}
void preorder(struct tnode *p, char *path, int pos, char num)
{
path[pos] = num;
if (!p->left && !p->right)
{
path[pos + 1] = '\0';
strcpy(HCode[p->c], path);
}
else
{
preorder(p->left, path, pos + 1, '0');
preorder(p->right, path, pos + 1, '1');
}
}
void makeHCode()
{
char path[10];
preorder(Root->left, path, 0, '0');
preorder(Root->right, path, 0, '1');
}
void atoHZIP()
{
int l, i;
Src = fopen("input.txt", "r");
char contain[10000];
memset(contain, 0, sizeof(char) * 10000);
char cc = fgetc(Src);
while (cc != EOF)
{
if (cc != '\n')
strcat(contain, HCode[cc]);
else
{
l = strlen(contain);
contain[l - 1] = '\n';
contain[l] = '\0';
}
cc = fgetc(Src);
}
strcat(contain, HCode[0]);
l = strlen(contain);
for (i = 0; i < l; i += 8)
{
int a = 0, j;
for (j = 0; j < 8; j++)
a = a * 2 + (contain[i + j] - '0');
char res1, res2;
printf("%x", a);
fprintf(Obj, "%c", a);
}
}
void print1()
{
int i;
printf("NUL:1\n");
for (i = 1; i < 128; i++)
if (Ccount[i] > 0)
printf("%c:%d\n", i, Ccount[i]);
}
void print2(struct tnode *p)
{
if (p != NULL)
{
if ((p->left == NULL) && (p->right == NULL))
switch (p->c)
{
case 0:
printf("NUL ");
break;
case ' ':
printf("SP ");
break;
case '\t':
printf("TAB ");
break;
case '\n':
printf("CR ");
break;
default:
printf("%c ", p->c);
break;
}
print2(p->left);
print2(p->right);
}
}
void print3()
{
int i;
for (i = 0; i < 128; i++)
{
if (HCode[i][0] != 0)
{
switch (i)
{
case 0:
printf("NUL:");
break;
case ' ':
printf("SP:");
break;
case '\t':
printf("TAB:");
break;
case '\n':
printf("CR:");
break;
default:
printf("%c:", i);
break;
}
printf("%s\n", HCode[i]);
}
}
}
void print4()
{
long int in_size, out_size;
fseek(Src, 0, SEEK_END);
fseek(Obj, 0, SEEK_END);
in_size = ftell(Src);
out_size = ftell(Obj);
printf("\n原文件大小:%ldB\n", in_size);
printf("压缩后文件大小:%ldB\n", out_size);
printf("压缩率:%.2f%%\n", (float)(in_size - out_size) * 100 / in_size);
}