BUAA数据结构期末考试(19级)

BUAA数据结构期末考试(19级)

1. 空闲空间合并

题目

问题描述

⼀个内存空间块可⽤⼀对起始地址和结束地址表示,如[0,100]。对于两个内存空间块:[0,100]和 [101,200],这两个空间是相邻的,所以可以合并成⼀个空间块:[0,200]。编写程序,输⼊⼀组空闲内 存空间,对相邻空间进⾏合并,然后按照空间块的起始地址由⼩到⼤输出。注意:内存空间块不会存在 重叠的情况。 算法之⼀提示:可先将输⼊的空间块按起始地址排序,然后进⾏合并。

输入形式

从控制台输⼊内存空间块的个数(⼤于等于1,⼩于等于100),然后分⾏输⼊每个内存空间块的起始地 址和结束地址(地址⽤⼤于等于0,并且⼩于等于100000的整数表示),两地址间以⼀个空格分隔。

输出形式

将合并后的内存空间块按照空间块的起始地址由⼩到⼤输出,每个空间块独占⼀⾏,先输出起始地址, 再输出结束地址,两地址间以⼀个空格分隔。

样例输入

10

48 99

0 39

1024 2047

100 479

4000 5999

600 799

40 47

2048 3047

840 859

8000 8999

样例输出

0 479

600 799

840 859

1024 3047

4000 5999

8000 8999

样例说明

输⼊了10个内存空间块,其中:[0,39]、[40,47]、[48,99]和[100,479]相邻,可以合并成⼀个空间块: [0,479];[1024,2047]和[2048,3047]相邻,可以合并成[1024,3047]。合并后还有6个内存空间块,按照 空间块的起始地址由⼩到⼤输出。

评分标准

该题要求输出合并后的内存空间,提交程序名为space.c。

问题分析

本题的读入与排序都没有难度,唯一需要处理的地方就是排序后如何合并,我是选择用两个标志分别记录当前的内存空间起始与结束位置,当判断下一个快块可以合并时,就更新当前块的结尾变量;否则,把上一个块输出,然后更新启示与终止的两个标识。

具体处理过程

首先声明结构体类型,创建内存块集合:

typedef struct
{
    int begin;
    int end;
} memory_block;

memory_block gather[1000]; // 内存块集合

然后在主函数中读入,排序:

// 主函数中
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++)
    {
        scanf("%d%d", &gather[i].begin, &gather[i].end);
    }
    qsort(gather, n, sizeof(memory_block), cmp);

其中cmp函数设计如下:

int cmp(const void *a, const void *b)
{
    return *((int *)a) - *((int *)b);
}

下面就把集合遍历一遍,按照上面的思路来就可以:

// 主函数中
   for (int i = 1; i < n; i++) // 注意从1开始遍历了
    {
        if (gather[i].begin == now_end + 1)
        { // 可以合并
            now_end = gather[i].end;
        }
        else
        { // 否则,输出前面的内存块
            printf("%d %d\n", now_begin, now_end);
            // 更新新的标志变量
            now_begin = gather[i].begin;
            now_end = gather[i].end;
        }
    }
    // 把最后的一个块输出
    printf("%d %d\n", now_begin, now_end);

完整代码

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

typedef struct
{
    int begin;
    int end;
} memory_block;

memory_block gather[1000]; // 内存块集合

int cmp(const void *a, const void *b);

int main()
{
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++)
    {
        scanf("%d%d", &gather[i].begin, &gather[i].end);
    }
    qsort(gather, n, sizeof(memory_block), cmp);

    int now_begin = gather[0].begin, now_end = gather[0].end;

    for (int i = 1; i < n; i++) // 注意从1开始遍历了
    {
        if (gather[i].begin == now_end + 1)
        { // 可以合并
            now_end = gather[i].end;
        }
        else
        { // 否则,输出前面的内存块
            printf("%d %d\n", now_begin, now_end);
            // 更新新的标志变量
            now_begin = gather[i].begin;
            now_end = gather[i].end;
        }
    }
    // 把最后的一个块输出
    printf("%d %d\n", now_begin, now_end);

    return 0;
}

int cmp(const void *a, const void *b)
{
    return *((int *)a) - *((int *)b);
}

2. ⽕⻋货运调度模拟

题目

问题描述

某⽕⻋货场由A、B、C三段组成,⻅下图。现有⼀列货⻋停在A段上,由多个货物⻋厢组成,每节⻋厢 信息包括编号和货物发往的⽬的地(⻋厢编号是唯⼀的,各节⻋厢发往的⽬的地可以相同,也可以不 同)。当前停在A段的货⻋⻋厢发往⽬的地编组是乱的,需要按⽬的地由远⾄近进⾏重新编组,即⻋厢 离⻋头越近其⽬的地越远,这样⽅便到达⽬的地时卸货(卸下相关⻋厢)。编组过程中⻋厢只能依次从 A中移动⾄B或C中,或从B或C中移⾄A中,从B中不能直接移⾄C中,从C中也不能直接移⾄B中。编写⼀ 程序模拟货运⻋厢编组。

在这里插入图片描述

假设A、B、C三段交叉路⼝为各段的顶端,编组规则如下:

  1. 初始时所有⻋厢位于A中且均未完成编组,⻋头距离顶端最远;⾸先将其所有⻋厢从顶开始依次推 进⾄B中。
  2. 从B中找到发往地最远的⻋厢中离顶最近的⻋厢,假设其为M。将M⾄顶的所⻋厢依次推进⾄A 中,此时M⼀定位于A中顶端。
  3. 若A中M之后(即:M与⻋头之间)不存在未完成编组的⻋厢,则⻋厢M的编组完成了;若A中M之 后存在未完成编组的⻋厢,则将M推进⾄ C中,将A中M之后所有未完成编组的⻋厢依次推进⾄B 中,再将M由C中推进⾄A中,这样就完成了⻋厢M的编组;
  4. 重复步骤2-3,直到B中⽆⻋厢。
输入形式

先从控制台输⼊⽬的地个数(⼤于等于1,⼩于等于50),然后分⾏输⼊⼀组由地名(⽤不含空⽩符的 英⽂字符串表示,字符个数不超过20)和⾥程(⽤⼤于等于1⼩于等于10000的整数表示)构成的发往 ⽬的地(由近⾄远序)⾥程表,地名和⾥程之间以⼀个空格分隔;在⾥程表之后输⼊⻋厢个数(⼤于等 于1,⼩于等于50),然后分⾏输⼊⼀组由⻋厢编号(由四位数字组成)和发往⽬的地(⽬的地肯定在 上述⾥程表中出现)构成的⻋厢信息,⻋厢编号和⽬的地之间以⼀个空格分隔,并且是从⻋头处开始依 次输⼊每节⻋厢信息。

输出形式

假设⼀节⻋厢从某段推出称为⼀次pop操作,推进某段称为⼀次push操作。程序运⾏输出第⼀⾏为从⻋ 头开始编好组的⻋厢编号,中间⽤⼀个空格隔开,最后⼀个⻋厢编号后也有⼀个空格。第⼆⾏输出编组 过程中A段中push操作的次数。

样例输入

10
shijiazhuang 280
xingtai 390
xinxiang 610
zhengzhou 689
wuchang 1221
chibi 1339
yueyang 1434
changsha 1559
shaoguan 2057
guangzhou 2273
12
0039 guangzhou
5217 xingtai
0262 yueyang
7205 wuchang
3211 guangzhou
4893 shijiazhuang
2283 shaoguan
0890 guangzhou
8729 wuchang
6839 shijiazhuang
2122 changsha
3280 wuchang

样例输出

0039 3211 0890 2283 2122 0262 7205 8729 3280 5217 4893 6839

45

样例说明

⾸先输⼊由10个地名及其⾥程组成的⾥程表,然后输⼊了12节需要编组的⻋厢的编号和发往的⽬的地, 即初始时处于A中的⻋厢,其中0039编号的⻋厢离⻋头最近。按照上⾯编组规则,⾸先将所有⻋厢推进 ⾄B中,这时0039⻋厢位于B的顶端,3280⻋厢位于底端,B中所有⻋厢中最远⽬的地为guangzhou、 且离顶最近的⻋厢为0039,将其推进⾄A中,这时A中只有其⼀节⻋厢,其后没有未编组的⻋厢,0039 ⻋厢编组完成(即完成第⼀节⻋厢编组),此时A的push操作次数为1;接下来,B中剩余的11个⻋厢 中,最远⽬的地依然为guangzhou,其离顶最近的⻋厢为3211,将该⻋厢及其上的3节⻋厢依次推进到 A中,此时,3211⻋厢与⽕⻋头之间有三节⻋厢未完成编组,于是按规则将3211推进⾄C中,将7205、 0262和5217依次推进⾄B中,再将C中的3211推进⾄A中,这时3211⻋厢编组完成(即完成第⼆节⻋厢 编组),此时A的push操作次数为6;再依次按照上述步骤对B中剩余的⻋厢完成编组,直到B中⽆⻋ 厢。最后从⻋头开始将所有完成编组的⻋厢编号依次输出,由此得到⼀组发往⽬的地距离(从⻋头开 始)由远⾄近的⻋厢序列。完成编组过程中A的push操作次数共有45次。

评分标准

该题要求编程模拟⽕⻋⻋厢编组操作,提交程序名为:train.c。

问题分析

本题看似是三个栈来回倒腾,我找到的往年题文件里附的参考代码也确实是这么写的,但其实仔细想一想,本题到底是如何排序的呢?可以这么看:先把所有数据都放到B里,然后每次从B中挑选一个最大的数据放到A的顶部,而保证了其他数据序列不变——有没有感觉这和选择排序特别像!没错,其实本题实质上就是选择排序问题!

具体处理过程

首先要读入目的地——里程对应关系表,而我们在读每节车厢的目的地时,应当直接把目的地转化为对应的里程数(查这个表),故声明两种结构如下:

typedef struct {
    char place[30];
    int dis;
} target;  // 目的地类型


typedef struct {
    char number[30];  // 车厢编号
    int tar_dis;  // 到目的地的距离
} carriage;  // 车厢类型

target gather[60];  // 目的地——里程数对应表
carriage stack[60];  // B段的栈

主函数中读入第一段数据,构建目的地——里程对应表:

// 主函数中
    int target_num;  // 目的地个数
    scanf("%d", &target_num);

    for (int i = 0; i < target_num; i++) {
        scanf("%s%d", &gather[i].place, &gather[i].dis);
    }  

然后读入每节车厢的信息,注意在这里直接把目的地的字符串转化为里程数存储:

    int car_num;  // 车厢个数
    scanf("%d", &car_num);
    char tmp_place[30];
    for (int i = car_num - 1; i >= 0; i--) {  // 我这里倒序读入了,其实正序也完全OK
        scanf("%s %s", stack[i].number, tmp_place);
        for (int j = 0; j < target_num; j++) {  // 找到对应的距离
            if (strcmp(gather[j].place, tmp_place) == 0) {
                stack[i].tar_dis = gather[j].dis;
                break;
            }
        }
    }

然后进行选择排序过程,这里有一个难点就是每次如何加A段的push次数,要想到有两种情况:选择的数据为B中栈顶时push次数就是1,否则除了第一次把上面的元素都push外,还要从C栈 push一次,代码实现如下(注意读入是倒序时遍历也是倒序开始遍历):

// 主函数中
   int ans = 0; // 记录比较次数
    for (int i = car_num - 1; i >= 0; i--)
    {
        int now_max = 0, location;
        for (int j = i; j >= 0; j--)
        {
            if (stack[j].tar_dis > now_max)
            {
                now_max = stack[j].tar_dis;
                location = j;
            }
        }
        if (i - location == 0)
        {
            ans += 1;
        }
        else
        {
            ans += i - location + 2;
        }

        carriage tmp = stack[location];
        for (int j = location; j < i; j++)
        {
            stack[j] = stack[j + 1];
        } // 拷贝后面的车厢信息
        stack[i] = tmp;
    }

最后输出结果:

    // 输出
    for (int i = car_num - 1; i >= 0; i--)
    {
        printf("%s ", stack[i].number);
    }
    printf("\n%d", ans);

完整代码

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

typedef struct
{
    char place[30];
    int dis;
} target; // 目的地类型

typedef struct
{
    char number[30]; // 车厢编号
    int tar_dis;     // 到目的地的距离
} carriage;          // 车厢类型

target gather[60];  // 目的地——里程数对应表
carriage stack[60]; // B段的栈

int main()
{
    int target_num; // 目的地个数
    scanf("%d", &target_num);

    for (int i = 0; i < target_num; i++)
    {
        scanf("%s%d", &gather[i].place, &gather[i].dis);
    }

    int car_num; // 车厢个数
    scanf("%d", &car_num);
    char tmp_place[30];
    for (int i = car_num - 1; i >= 0; i--)
    { // 我这里倒序读入了,其实正序也完全OK
        scanf("%s %s", stack[i].number, tmp_place);
        for (int j = 0; j < target_num; j++)
        { // 找到对应的距离
            if (strcmp(gather[j].place, tmp_place) == 0)
            {
                stack[i].tar_dis = gather[j].dis;
                break;
            }
        }
    }

    int ans = 0; // 记录比较次数
    for (int i = car_num - 1; i >= 0; i--)
    {
        int now_max = 0, location;
        for (int j = i; j >= 0; j--)
        {
            if (stack[j].tar_dis > now_max)
            {
                now_max = stack[j].tar_dis;
                location = j;
            }
        }
        if (i - location == 0)
        {
            ans += 1;
        }
        else
        {
            ans += i - location + 2;
        }

        carriage tmp = stack[location];
        for (int j = location; j < i; j++)
        {
            stack[j] = stack[j + 1];
        } // 拷贝后面的车厢信息
        stack[i] = tmp;
    }

    // 输出
    for (int i = car_num - 1; i >= 0; i--)
    {
        printf("%s ", stack[i].number);
    }
    printf("\n%d", ans);

    return 0;
}

3. 查找同名文件

题目

问题描述

在操作系统中⽬录及⽂件是按树形式组织和管理的,从根⽬录开始,每⼀层⽬录下可以包含⼦⽬录和⽂ 件。假设⼀⽂件系统,从根⽬录开始,每个⽬录下可包含最多不超过100个⽂件及⼦⽬录,⽂件名及⽬ 录名是⼀字符串(不包含空⽩符,最多包含20个字符),每个⽂件和⽬录包含修改时间属性(仅由⽇期 组成的字符串,例如:20190927表示2019年9⽉27⽇),在同⼀个⽬录下不会存在同名⽂件,⽽且所 有的⽬录都不会重名。例如,下图是⼀给定的⽂件结构示意:

在这里插入图片描述

给定⼀⽂件名,请在给定⽂件结构中查找同名⽂件,并按修改时间由近⾄远排列输出其包含全路径名的 ⽂件名,即从根⽬录开始的⽬录及⽂件名(包含的字符个数不会超过200);修改时间相同时,从根⽬ 录开始按层次由⼩到⼤的顺序输出;修改时间和层次都相同的,按照由上到下的顺序输出。假设找到的 同名⽂件个数不会超过100。

输入形式

从控制台输⼊⽂件结构中⽬录及⽂件的总个数和待查找的⽂件名,中间由⼀个空格分隔;然后从当前⽬ 录下⽂件files.txt中分⾏读⼊每个⽬录及⽂件的信息,其格式如下:

name parentName type date

name是⼀字符串,表示⽬录或⽂件的名字,其中不包含空⽩符,最多包含20个字符;parentName表 示该⽬录或⽂件的上级⽬录名字,根⽬录没有上级⽬录,⽤字符"-”表示;type为0或1,⽤于区分当前输 ⼊的是⽂件还是⽬录:0表示⽂件,1表示⽬录;date为⼀⽇期字符串,表示⽬录或⽂件的修改时间,例 如:20190927表示2019年9⽉27⽇。 读⼊的⽬录或⽂件遵循如下原则:

读⼊当前⽬录或⽂件时,其上级⽬录已经读⼊;对于同⼀⽬录下的⽂ 件或⼦⽬录,先读⼊的位于前⾯(显示在上⽅)。

输出形式

按修改时间由近⾄远排列分⾏输出找到的⽂件:包括其从根⽬录开始的各级⽬录及⽂件名,⽬录及⽂件 之间以⼀个英⽂字符““分隔;修改时间相同时,按从根⽬录开始按层次由⼩到⼤的顺序输出;修改时间 和层次都相同的,按照由上到下的顺序输出。注意:由于根⽬录⼀般为盘符,输出时要加英⽂字符”:", 例如若根⽬录名为D,则输出时应为 D:。

样例输入

49 test.c

当前⽬录下files.txt⽂件中内容如下:

D - 1 20190101
MyDoc D 1 20190101
win D 1 20190101
test.c D 0 20190901
work MyDoc 1 20190101
2018 work 1 20190102
plan.doc 2018 0 20190202
date.doc 2018 0 20190305
2019 work 1 20190103
test.c work 0 20190901
test.obj work 0 20190902
sun.jpg 2019 0 20190405
report.ppt 2018 0 20190306
test MyDoc 1 20190103
music MyDoc 1 20190103
temp MyDoc 1 20190103
moon.mp3 music 0 20190607
thesky.mp3 music 0 20190701
moon.jpg 2019 0 20190506
test.c 2019 0 20190901
apologize.mp3 music 0 20190702
test.c 2018 0 20190901
natural.mp3 music 0 20190702
world.exe win 0 20190101
dev.exe win 0 20190102
course temp 1 20190505
mon.exl temp 0 20190505
wel.ppt temp 0 20190605
math course 1 20190506
lang course 1 20190607
one math 1 20190507
test.c lang 0 20190925
graph.c lang 0 20190926
two math 1 20190508
three math 1 20190508
test.c math 0 20190926
prog one 1 20190609
data one 1 20190610
sum three 1 20190509
test.c three 0 20190901
in.dat three 0 20190902
input.txt prog 0 20190809
output.txt prog 0 20190827
test.c prog 0 20190901
test.exe prog 0 20190901
save data 1 20190611
test.exe data 0 20190612
in.txt data 0 20190612
out.dat three 0 20190902D - 1 20190101

样例输出

D:\MyDoc\temp\course\math\test.c
D:\MyDoc\temp\course\lang\test.c
D:\test.c
D:\MyDoc\work\test.c
D:\MyDoc\work\2018\test.c
D:\MyDoc\work\2019\test.c
D:\MyDoc\temp\course\math\three\test.c
D:\MyDoc\temp\course\math\one\prog\test.c

样例说明

files.txt中有49个⽬录和⽂件,形成如上图所示的⽂件系统。待查找的⽂件名为test.c。可以在该⽂件系 统中找到8个名为test.c的⽂件,其中D:\MyDoc\temp\course\math⽬录下的test.c最新,所以先输出, 然后是D:\MyDoc\temp\course\lang⽬录下的test.c⽂件。剩下六个test.c⽂件的修改⽇期相同,按照从 根⽬录D:开始层次由⼩到⼤顺序输出。其中:⽬录D:\MyDoc\work\2018和D:\MyDoc\work\2019下的 test.c⽂件所处层次相同,则按照由上⾄下的顺序输出。

评分标准

该题要求查找⽂件所在的⽬录,提交⽂件名为find.c。

问题分析

本题的要求比较多,简直是一道裁缝题!但大体可以分成这三个步骤:

  1. 按照题目要求建树,并记录下必要的数据;
  2. 遍历树,记录下所有满足输出要求的结点,并记录下它们三个排序关键字:修改时间、树中的层次、以及题目说的“从上到下的顺序”——这个其实翻译过来的话,就是要比较它的双亲结点是它的“双亲结点的双亲结点“的第几个孩子。
  3. 对满足要求的结点排序,并打印路径。

具体处理过程

本题的逻辑还是比较复杂的,所以我选择一点一点去完善程序,先实现的是建树(最基本的)的工作。首先要声明结点类型:

typedef struct file
{
    char name[25];
    int type;
    char date[10];
    struct file *children[100];
    int child_num; // 有多少个孩子
} file;

然后主函数的建树逻辑应该是这样的:

int main()
{
    int n;           // ⽂件结构中⽬录及⽂件的总个数
    char target_name[30]; // 目标文件名
    scanf("%d", &n);
    gets(target_name);
    freopen("files.txt", "r", stdin);

    char tmp_name[30];
    char tmp_parent_name[30];
    int tmp_type;
    char tmp_date[10];

    file *root = (file *)malloc(sizeof(file));
    root->child_num = 0; // 初始化根结点

    scanf("%s %s %d %s", root->name, tmp_parent_name, &(root->type), root->date); // 先把根目录读进来
    for (int i = 1; i < n; i++)
    {
        scanf("%s %s %d %s", tmp_name, tmp_parent_name, &tmp_type, tmp_date);
        insert(root, tmp_name, tmp_parent_name, tmp_type, tmp_date);
    }
    return 0;
}

其中insert函数实现在树中搜索要插入的结点的双亲结点,并把它插入,实现如下:

void insert(file *root, char *name, char *parent_name, int type, char *date)
{
    if (strcmp(parent_name, root->name) == 0) // 找到了目标的双亲结点
    {
        file *tmp = (file *)malloc(sizeof(file));
        tmp->child_num = 0;
        strcpy(tmp->name, name);
        tmp->type = type;
        strcpy(tmp->date, date);
        root->children[root->child_num] = tmp;
        root->child_num++;
        return;
    }
    if (root->type == 0)
        return; // 搜到文件了,终止

    for (int i = 0; i < root->child_num; i++)
    {
        insert(root->children[i], name, parent_name, type, date);
    }
}

现在可以试着读入数据,打印一些数据,发现树确实是建立起来了,但是我们用的是相当于二叉树的二叉链表的建树方法,而最后却要获取一个结点的路径,这当然可以实现,但三叉链表建立起来的树去实现这个功能要简便很多,所以我打算用三叉链表去改写一下现在的代码。同时,根据最开始的分析,我们要把每一个结点的层次数,以及“它是其双亲结点的第几个孩子”记录下来,所以声明的结构体里还要增加两个域:

// 结构体声明
typedef struct file{
    char name[25];
    int type;
    char date[10];
    struct file *children[100];
    int child_num;  // 有多少个孩子

    struct file *parent;  // 双亲结点地址

    int level;  // 层次
    int which_child;  // 是其双亲结点的第几个孩子
} file;

// insert函数改写
void insert(file *root, char *name, char *parent_name,int type, char *date, int height)
{
    if (strcmp(parent_name, root -> name) == 0) {
        file* tmp = (file*) malloc(sizeof(file));
        tmp -> child_num = 0;
        strcpy(tmp -> name, name);
        tmp -> type = type;
        strcpy(tmp -> date, date);
        tmp -> parent = root;
        tmp -> which_child = root -> child_num;
        tmp -> level = height + 1;
        root -> children[root -> child_num] = tmp;
        root -> child_num++;
        return;
    }
    if (root -> type == 0) return;  // 搜到文件了,终止

    for (int i = 0; i < root -> child_num; i++) {
        insert(root -> children[i], name, parent_name, type, date, height + 1);
    }

}

在主函数中调用现在的insert函数时,要多传一个height参数,由于每次都从根结点开始查,所以传0即可:

// 主函数中调用insert建树
    for (int i = 1; i < n; i++) {
        scanf("%s %s %d %s", tmp_name, tmp_parent_name, &tmp_type, tmp_date);
        insert(root, tmp_name, tmp_parent_name, tmp_type, tmp_date, 0);
    }

而后按照某种遍历方式进行遍历即可(由于我们已经记录下来了层次数,所以不一定非要用层次遍历,但是为了尊重题目,我还是写了层次遍历),开如下几个全局变量:

file *stack[1000]; // 层次遍历的栈
file *result[1000]; // 记录满足要求的结点地址
int res_num; // 上面那个数组的元素个数

然后把树遍历一遍:

void layerorder(file *root)
// 层次遍历
{
    file *p;
    int front = 0, rear = 1;
    stack[0] = root;
    while (front < rear)
    {
        p = stack[front++];

        if (p->type == 0 && strcmp(p->name, target_name) == 0)
        {
            result[res_num++] = p;
        }

        for (int i = 0; i < p->child_num; i++)
        {
            stack[rear++] = p->children[i];
        }
    }
}

之后要按照要求对result数组排序,设计cmp函数如下:

int cmp(const void *a, const void *b)
{
    file *e1 = *((file **)a); // 二级指针!
    file *e2 = *((file **)b);

    if (strcmp(e1->date, e2->date) != 0)
    {
        return strcmp(e2->date, e1->date);
    }

    if (e1->level != e2->level)
    {
        return e1->level - e2->level;
    }

    return e1->parent->which_child - e2->parent->which_child;
}

接下来要输出路径,主函数中调用如下:

    for (int i = 0; i < res_num; i++)
    {
        printf("%s:", root->name);
        print_path(result[i], root);
        printf("\n");
    }

print_path就是打印树中根结点到当前结点的函数——递归调用即可:

void print_path(file *target, file *root)
{
    if (target != root)
    {
        print_path(target->parent, root);
        printf("\\%s", target->name);
    }
}

完整代码

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

typedef struct file
{
    char name[25];
    int type;
    char date[10];
    struct file *children[100];
    int child_num; // 有多少个孩子

    struct file *parent; // 双亲结点地址

    int level;       // 层次
    int which_child; // 是其双亲结点的第几个孩子
} file;

char target_name[30]; // 目标文件名
file *stack[1000];    // 层次遍历的栈
file *result[1000];   // 记录满足要求的结点地址
int res_num;          // 上面那个数组的元素个数

void insert(file *root, char *name, char *parent_name, int type, char *date, int height);
void layerorder(file *root);
int cmp(const void *a, const void *b);
void print_path(file *target, file *root);

int main()
{
    int n; // ⽂件结构中⽬录及⽂件的总个数
    scanf("%d", &n);
    getchar();
    gets(target_name);
    // freopen("files.txt", "r", stdin);

    char tmp_name[30];
    char tmp_parent_name[30];
    int tmp_type;
    char tmp_date[10];

    file *root = (file *)malloc(sizeof(file));
    root->child_num = 0;
    root->parent = NULL;

    scanf("%s %s %d %s", root->name, tmp_parent_name, &(root->type), root->date); // 先把根目录读进来
    for (int i = 1; i < n; i++)
    {
        scanf("%s %s %d %s", tmp_name, tmp_parent_name, &tmp_type, tmp_date);
        insert(root, tmp_name, tmp_parent_name, tmp_type, tmp_date, 0);
    }
    layerorder(root);

    qsort(result, res_num, sizeof(result[0]), cmp);

    for (int i = 0; i < res_num; i++)
    {
        printf("%s:", root->name);
        print_path(result[i], root);
        printf("\n");
    }
    return 0;
}

void insert(file *root, char *name, char *parent_name, int type, char *date, int height)
{
    if (strcmp(parent_name, root->name) == 0)
    {
        file *tmp = (file *)malloc(sizeof(file));
        tmp->child_num = 0;
        strcpy(tmp->name, name);
        tmp->type = type;
        strcpy(tmp->date, date);
        tmp->parent = root;
        tmp->which_child = root->child_num;
        tmp->level = height + 1;
        root->children[root->child_num] = tmp;
        root->child_num++;
        return;
    }
    if (root->type == 0)
        return; // 搜到文件了,终止

    for (int i = 0; i < root->child_num; i++)
    {
        insert(root->children[i], name, parent_name, type, date, height + 1);
    }
}

void layerorder(file *root)
// 层次遍历
{
    file *p;
    int front = 0, rear = 1;
    stack[0] = root;
    while (front < rear)
    {
        p = stack[front++];

        if (p->type == 0 && strcmp(p->name, target_name) == 0)
        {
            result[res_num++] = p;
        }

        for (int i = 0; i < p->child_num; i++)
        {
            stack[rear++] = p->children[i];
        }
    }
}

int cmp(const void *a, const void *b)
{
    file *e1 = *((file **)a); // 二级指针!
    file *e2 = *((file **)b);

    if (strcmp(e1->date, e2->date) != 0)
    {
        return strcmp(e2->date, e1->date);
    }

    if (e1->level != e2->level)
    {
        return e1->level - e2->level;
    }

    return e1->parent->which_child - e2->parent->which_child;
}

void print_path(file *target, file *root)
{
    if (target != root)
    {
        print_path(target->parent, root);
        printf("\\%s", target->name);
    }
}
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值