网络打印机选择——数据结构树练习题

目录

前言

题目

问题描述

 输入形式

输出形式

样例

样例说明

问题分析

代码实现


前言

学数据结构,品百味人生。大家好,学过树之后,大家一定对树的操作十分熟练了吧!今天就来分享一道树的练习题,相信大家一定会对树的构造、遍历等等操作有一个更熟练的掌握程度。本题来源于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号之前)。

问题分析

这属于一道树的经典题型,容易看出这道题考查树的构造和遍历水平,可以分为以下几个步骤:

  1. 建树。“要想富,先种树。”我们可以在结点node1中设置机器的类型、机器的编号、机器的八个接口(指针);随后根据每一行输入的内容,通过遍历找到父节点,再创建子节点连接;
  2. 前序遍历。既然题目中要求相同路径长度,优先考虑前序遍历中位置靠前的打印机,我们就可以先前序遍历整棵树,将打印机按照上述顺序排好序,放在数组printer中;
  3. 保存路径。这道题当中,可以看到我们需要以计算机为起点逆着找打印机,所以在第一步建树当中,我们还要在结点node1中增加父指针,也就是指向自己父节点的指针,以及添加自己所位于的接口序号。之后,补充第一步当中父子结点的连接。
    同时,我们为了保存每个计算机到打印机的路径,还要设置一个结构node2,里面有打印机的序号、路径当中交换机的数量,以及具体经过哪些交换机。
    这样,我们就可以设置一个递归函数。用数组来动态地存储从计算机开始走过的交换机路径,一旦找到打印机,就将相应的值保存到node2结构体中。
    注意这里的遍历,树中每个结点与结点间的树枝只能走一次,如果走多次就会造成死循环。因此,在向上访问自己父节点之后,父节点不能再向下访问自己的在这条树枝下的子节点;同样地,在向下访问自己的子节点之后,该子节点不能再向上访问自己的父节点。这也是在结点中保存接口序号的原因,能够防止同一路径遍历多次的情况。
  4. 排序。按照题意,路径中交换机少的打印机在前面,交换机数量相等时前序遍历在前面的打印机排在前面。所以,在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;
}

以上就是这道题的分享啦~在学习数据结构的路上还会有更多有意思的题,都会在论坛里发布~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值