目录
前言
学数据结构,品百味人生。大家好,学过树之后,大家一定对树的操作十分熟练了吧!今天就来分享一道树的练习题,相信大家一定会对树的构造、遍历等等操作有一个更熟练的掌握程度。本题来源于BUAA北京学院数据结构18级第三题,同时感谢周恩申助教的收集!
题目
问题描述
某单位信息网络结构呈树型结构,网络中节点可为交换机、计算机和打印机三种设备,计算机和打印机只能位于树的叶节点上。如要从一台计算机上打印文档,请为它选择最近(即经过交换机最少)的打印机。
在该网络结构中,根交换机编号为0,其它设备编号可为任意有效正整数,每个交换机有8个端口(编号0-7)。当存在多个满足条件的打印机时,选择按树前序遍历序排在前面的打印机。
输入形式
首先从标准输入中输入两个整数,第一个整数表示当前网络中设备数目,第二个整数表示需要打印文档的计算机编号。两整数间以一个空格分隔。假设设备总数目不会超过300。
然后从键盘读入相应设备配置表,该表每一行构成一个设备的属性,格式如下:
<设备ID> <类型> <设备父节点ID> <端口号>
<设备ID>为一个非负整数,表示设备编号;<类型>分为:0表示交换机、1表示计算机、2表示打印机;<设备父结点ID>为相应结点父结点编号,为一个有效非负整数;<端口号>为相应设备在父结点交换机中所处的端口编号,分别为0-7。由于设备配置表是按设备加入网络时的次序编排的,因此,表中第一行一定为根交换机(其属性为0 0 -1 -1);其它每个设备结点一定在其父设备结点之后输入。每行中设备属性间由一个空格分隔,最后一个属性后有换行符。
输出形式
向控制台输出所选择的打印机编号,及所经过的交换机的编号,顺序是从需要打印文档的计算机开始,编号间以一个空格分隔。
样例
【样例输入】
37 19
0 0 -1 -1
1 0 0 0
2 0 1 2
3 1 1 5
4 0 0 1
5 1 4 0
6 2 2 2
7 0 4 2
8 0 0 4
9 0 2 0
10 0 9 0
11 2 10 3
12 0 9 2
13 0 7 0
14 0 13 0
15 2 7 3
16 0 8 1
17 0 16 0
18 1 17 5
19 1 9 5
20 0 12 1
21 1 14 1
22 1 14 2
23 1 13 2
24 1 12 5
25 0 20 1
26 1 20 2
27 0 14 7
28 0 16 1
29 1 4 3
30 0 16 7
31 0 28 0
32 2 31 0
33 1 30 2
34 1 31 2
35 0 31 5
36 1 35 3
【样例输出】
11 9 10
样例说明
样例输入中37表示当前网络共有37台设备,19表示编号为19的计算机要打印文档。in.txt设备表中第一行0 0 -1 -1表示根节点交换机设备,其设备编号为0 、设备类型为0(交换机)、父结点设备编号-1表示无父设备、端口-1表示无接入端口;设备表第二行1 0 0 0表示设备编号为1 、设备类型为0(交换机)、父结点设备编号0(根交换机)、端口0表示接入父结点端口0;设备表中行5 1 4 0表示设备编号为5 、设备类型为1(计算机)、父结点设备编号4、端口0表示接入4号交换机端口0;设备表中行6 2 2 2表示设备编号为6 、设备类型为2(打印机)、父结点设备编号2、端口2表示接入2号交换机端口2。
样例输出11 9 10表示选择设备编号为11的打印机打印文档,打印需要经过9号和10号交换机(尽管6号和11号打印机离19号计算机距离相同,但11号打印机按树前序遍历时排在6号之前)。
问题分析
这属于一道树的经典题型,容易看出这道题考查树的构造和遍历水平,可以分为以下几个步骤:
- 建树。“要想富,先种树。”我们可以在结点node1中设置机器的类型、机器的编号、机器的八个接口(指针);随后根据每一行输入的内容,通过遍历找到父节点,再创建子节点连接;
- 前序遍历。既然题目中要求相同路径长度,优先考虑前序遍历中位置靠前的打印机,我们就可以先前序遍历整棵树,将打印机按照上述顺序排好序,放在数组printer中;
- 保存路径。这道题当中,可以看到我们需要以计算机为起点逆着找打印机,所以在第一步建树当中,我们还要在结点node1中增加父指针,也就是指向自己父节点的指针,以及添加自己所位于的接口序号。之后,补充第一步当中父子结点的连接。
同时,我们为了保存每个计算机到打印机的路径,还要设置一个结构node2,里面有打印机的序号、路径当中交换机的数量,以及具体经过哪些交换机。
这样,我们就可以设置一个递归函数。用数组来动态地存储从计算机开始走过的交换机路径,一旦找到打印机,就将相应的值保存到node2结构体中。
注意这里的遍历,树中每个结点与结点间的树枝只能走一次,如果走多次就会造成死循环。因此,在向上访问自己父节点之后,父节点不能再向下访问自己的在这条树枝下的子节点;同样地,在向下访问自己的子节点之后,该子节点不能再向上访问自己的父节点。这也是在结点中保存接口序号的原因,能够防止同一路径遍历多次的情况。 - 排序。按照题意,路径中交换机少的打印机在前面,交换机数量相等时前序遍历在前面的打印机排在前面。所以,在qsort函数中设置两种判断方法,从而将这几种路径排序。排序后输出第一个结构体的相关值就会收获全部的AC!
代码实现
#include <stdio.h>
#include <stdlib.h>
typedef struct node1
{
int flag;
int id;
int childnum;
struct node1 * child[8],* parent;
}mac,* mach;
struct node2
{
int printerid;
int roadnum;
int road[105];
}prin[1005];//存储所有打印机路径,pri首位存打印机的id
mach root=NULL,res;
int idm,flagm,parentm,childm;
int n,comp,printer[1005],prnum;
int dom[105];//动态地存经过的交换机
int priN;//存储所有打印机路径,pri首位存打印机的id
void build(mach p)
{
mach q;
int i;
if (p!=NULL)
{
if (p->id==parentm)
{
q=(mach)malloc(sizeof(mac));
q->id=idm;
q->flag=flagm;
q->parent=p;
q->childnum=childm;
p->child[childm]=q;
for (i=0;i<8;i++)
{
q->child[i]=NULL;
}
if (q->id==comp) res=q;
}
else
{
for (i=0;i<8;i++)
{
build(p->child[i]);
}
}
}
}
void frontsearch(mach p)
{
int i;
if (p!=NULL)
{
if (p->flag==2)
{
printer[prnum++]=p->id;
}
else
{
for (i=0;i<8;i++)
{
frontsearch(p->child[i]);
}
}
}
}
void searchprinter(mach p,int domN,int ope)
{
int i;
if (p!=NULL)
{
if (p->flag==0)
{
dom[domN]=p->id;
}
if (p->flag==2)
{
prin[priN].printerid=p->id;
prin[priN].roadnum=domN;
for (i=0;i<domN;i++)
{
prin[priN].road[i]=dom[i];
}
priN++;
}
if (ope!=-2) searchprinter(p->parent,domN+1,p->childnum);
for (i=0;i<8;i++)
{
if (i==ope) continue;
searchprinter(p->child[i],domN+1,-2);
}
}
}
int cmp(const void * p1,const void * p2)
{
int i,j,k;
struct node2 * a=(struct node2 *)p1;
struct node2 * b=(struct node2 *)p2;
if (a->roadnum!=b->roadnum) return a->roadnum-b->roadnum;
else
{
for (k=0;k<prnum;k++)
{
if (printer[k]==a->printerid) i=k;
if (printer[k]==b->printerid) j=k;
}
return i-j;
}
}
int main()
{
int i,j;
scanf("%d%d",&n,&comp);
for (i=0;i<n;i++)
{
scanf("%d%d%d%d",&idm,&flagm,&parentm,&childm);
if (i==0)
{
root=(mach)malloc(sizeof(mac));
root->id=root->flag=0;
root->parent=NULL;
root->childnum=-1;
for (j=0;j<8;j++)
{
root->child[j]=NULL;
}
}
else
{
build(root);
}
}
frontsearch(root);
searchprinter(res,-1,-1);
qsort(prin,priN,sizeof(struct node2),cmp);
printf("%d ",prin[0].printerid);
for (i=0;i<prin[0].roadnum;i++)
{
printf("%d ",prin[0].road[i]);
}
return 0;
}
以上就是这道题的分享啦~在学习数据结构的路上还会有更多有意思的题,都会在论坛里发布~