数据结构期末复习·网络打印机选择

 温故而知新

上次写的打印机选择在代码风格和一些细节方面太粗糙了,今天再写一遍。

【问题描述】

某单位信息网络结构呈树型结构,网络中节点可为交换机、计算机和打印机三种设备,计算机和打印机只能位于树的叶节点上。如要从一台计算机上打印文档,请为它选择最近(即经过交换机最少)的打印机。

在该网络结构中,根交换机编号为0,其它设备编号可为任意有效正整数,每个交换机有8个端口(编号0-7)。当存在多个满足条件的打印机时,选择按树前序遍历序排在前面的打印机。

image.png

【输入形式】

首先从标准输入中输入两个整数,第一个整数表示当前网络中设备数目,第二个整数表示需要打印文档的计算机编号。两整数间以一个空格分隔。假设设备总数目不会超过300。

然后从当前目录下的in.txt读入相应设备配置表,该表每一行构成一个设备的属性,格式如下:

<设备ID> <类型> <设备父节点ID> <端口号>

<设备ID>为一个非负整数,表示设备编号;<类型>分为:0表示交换机、1表示计算机、2表示打印机;<设备父结点ID>为相应结点父结点编号,为一个有效非负整数;<端口号>为相应设备在父结点交换机中所处的端口编号,分别为0-7。由于设备配置表是按设备加入网络时的次序编排的,因此,表中第一行一定为根交换机(其属性为0 0 -1 -1);其它每个设备结点一定在其父设备结点之后输入。每行中设备属性间由一个空格分隔,最后一个属性后有换行符。

【输出形式】

向控制台输出所选择的打印机编号,及所经过的交换机的编号,顺序是从需要打印文档的计算机开始,编号间以一个空格分隔。

【样例输入】

37 19

in.txt中的信息如下:

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号之前)。

【评分标准】

按题目要求实现相关功能,提交程序文件名为print.c。

 这个题真的出的很有意义,考到了树里面常用的很多操作和算法(尤其是递归),大概步骤分为:

1·建树

2·寻找距离最短的打印机

3·输出路径

1·建树

由于文件给的是父节点的编号,所以需要先找到父节点。

typedef struct device_type
{
    int ID;
    int type;
    int father;
    int comID;
    struct device_type *child[9];
} node;
node *tree;

node *find_node(node *p, int id)
{
    if (p->ID == id)
        return p;
    for (int i = 0; i < 8; i++)
    {
        if (p->child[i] != NULL && find_node(p->child[i], id) != NULL)
            return find_node(p->child[i], id);
    }
    return NULL;
}

node *make_tree()
{
    FILE *src = fopen("in.txt", "r");
    //初始化根节点
    node *root = (node *)malloc(sizeof(node));
    for (int i = 0; i < 9; i++)
        root->child[i] = NULL;
    int id, ty, par, com;
    fscanf(src, "%d%d%d%d", &id, &ty, &par, &com);
    root->ID = id;
    root->type = ty;
    root->father = par;
    root->comID = com;
    //录入子节点
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n - 1; i++)
    {
        //生成子节点
        fscanf(src, "%d%d%d%d", &id, &ty, &par, &com);
        node *p = (node *)malloc(sizeof(node));
        for (int i = 0; i < 9; i++)
            p->child[i] = NULL;
        p->ID = id;
        p->type = ty;
        p->father = par;
        p->comID = com;
        //寻找父节点
        node *q = find_node(root, par);
        q->child[com] = p;
    }
    return root;
}

2·寻找最近的打印机

由于策略是在同距离的时候,前序遍历在前的优先级更高,所以我们先前序遍历,把打印机结点的指针放到一个数组里面存起来,然后遍历数组寻找距离最近的。

node *printer[100];
int pcnt = 0;
void get_printers(node *p)
{
    if (p->type == 2)
        printer[pcnt++] = p;
    for (int i = 0; i < 8; i++)
    {
        if (p->child[i] != NULL)
            get_printers(p->child[i]);
    }
}

在求打印机和计算机的距离时,不必要使用递归(因为这样不方便距离的计算),所以直接使用循环:

int dist(node *p, node *q)
{
    int d = 0;
    node *t = p;
    while (!is_ances(t, q))
    {
        d++;
        t = find_node(tree, t->father);
    } //寻找公共祖先
    while (t != q)
    {
        for (int i = 0; i < 8; i++)
        {
            if (t->child[i] != NULL && is_ances(t->child[i], q))
            {
                t = t->child[i];
                d++;
                break;
            }
        }
    }
    return d;
}

is_ances函数比较简单:

int is_ances(node *p, node *q)
{
    if (p == q)
        return 1;
    for (int i = 0; i < 8; i++)
    {
        if (p->child[i] != NULL && is_ances(p->child[i], q))
            return 1;
    }
    return 0;
}

最后,有了这两个函数,只需要遍历即可:

node *find_printer(node *cpt)
{
    node *p = printer[0];
    int sdist = dist(p, cpt);
    for (int i = 0; i < pcnt; i++)
    {
        if (dist(printer[i], cpt) < sdist)
        {
            sdist = dist(printer[i], cpt);
            p = printer[i];
        }
    }
    return p;
}

三·打印路径

其实打印路径和求距离基本上是一样的,只要把求距离函数的距离++改成打印结点就可以了,

注意到本题只要打印交换机结点,所以还需要判断一下是不是交换机

void show_path(node *p, node *q)
{
    node *t = p;
    while (!is_ances(t, q))
    {
        if (t->type == 0)
            printf("%d ", t->ID);
        t = find_node(tree, t->father);
    } //寻找公共祖先
    while (t != q)
    {
        for (int i = 0; i < 8; i++)
        {
            if (t->child[i] != NULL && is_ances(t->child[i], q))
            {
                if (t->type == 0)
                    printf("%d ", t->ID);
                t = t->child[i];
                break;
            }
        }
    }
}

完整代码如下:

#include <stdio.h>
#include <stdlib.h>

typedef struct device_type
{
    int ID;
    int type;
    int father;
    int comID;
    struct device_type *child[9];
} node;
node *tree;

node *find_node(node *p, int id)
{
    if (p->ID == id)
        return p;
    for (int i = 0; i < 8; i++)
    {
        if (p->child[i] != NULL && find_node(p->child[i], id) != NULL)
            return find_node(p->child[i], id);
    }
    return NULL;
}

node *make_tree()
{
    FILE *src = fopen("in.txt", "r");
    //初始化根节点
    node *root = (node *)malloc(sizeof(node));
    for (int i = 0; i < 9; i++)
        root->child[i] = NULL;
    int id, ty, par, com;
    fscanf(src, "%d%d%d%d", &id, &ty, &par, &com);
    root->ID = id;
    root->type = ty;
    root->father = par;
    root->comID = com;
    //录入子节点
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n - 1; i++)
    {
        //生成子节点
        fscanf(src, "%d%d%d%d", &id, &ty, &par, &com);
        node *p = (node *)malloc(sizeof(node));
        for (int i = 0; i < 9; i++)
            p->child[i] = NULL;
        p->ID = id;
        p->type = ty;
        p->father = par;
        p->comID = com;
        //寻找父节点
        node *q = find_node(root, par);
        q->child[com] = p;
    }
    return root;
}

void pre_traverse(node *p)
{
    printf("%d ", p->ID);
    for (int i = 0; i < 8; i++)
    {
        if (p->child[i])
            pre_traverse(p->child[i]);
    }
}

node *printer[100];
int pcnt = 0;
void get_printers(node *p)
{
    if (p->type == 2)
        printer[pcnt++] = p;
    for (int i = 0; i < 8; i++)
    {
        if (p->child[i] != NULL)
            get_printers(p->child[i]);
    }
}

int is_ances(node *p, node *q)
{
    if (p == q)
        return 1;
    for (int i = 0; i < 8; i++)
    {
        if (p->child[i] != NULL && is_ances(p->child[i], q))
            return 1;
    }
    return 0;
}

int dist(node *p, node *q)
{
    int d = 0;
    node *t = p;
    while (!is_ances(t, q))
    {
        d++;
        t = find_node(tree, t->father);
    } //寻找公共祖先
    while (t != q)
    {
        for (int i = 0; i < 8; i++)
        {
            if (t->child[i] != NULL && is_ances(t->child[i], q))
            {
                t = t->child[i];
                d++;
                break;
            }
        }
    }
    return d;
}

node *find_printer(node *cpt)
{
    node *p = printer[0];
    int sdist = dist(p, cpt);
    for (int i = 0; i < pcnt; i++)
    {
        if (dist(printer[i], cpt) < sdist)
        {
            sdist = dist(printer[i], cpt);
            p = printer[i];
        }
    }
    return p;
}

void show_path(node *p, node *q)
{
    node *t = p;
    while (!is_ances(t, q))
    {
        if (t->type == 0)
            printf("%d ", t->ID);
        t = find_node(tree, t->father);
    } //寻找公共祖先
    while (t != q)
    {
        for (int i = 0; i < 8; i++)
        {
            if (t->child[i] != NULL && is_ances(t->child[i], q))
            {
                if (t->type == 0)
                    printf("%d ", t->ID);
                t = t->child[i];
                break;
            }
        }
    }
}

int main()
{
    tree = make_tree();
    //pre_traverse(tree);
    get_printers(tree);
    //for(int i=0;i<pcnt;i++) printf("%d ",printer[i]->ID);
    int computerID;
    scanf("%d", &computerID);
    node *computer = find_node(tree, computerID);
    node *prt = find_printer(computer);
    printf("%d ", prt->ID);
    show_path(computer, prt);
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值