数据结构 C语言 树形结构 简单目录管理系统

  1. 需求分析

在图书、操作系统文件夹、学生等管理系统中,目录管理是必要环节,如何高效搜寻与低复杂度存储是实现这一环节的关键性问题。本课程设计需完成一种基于多叉树结构简单目录管理系统,该系统能以交互式界面进行创建目录、删除目录、查找目录、修改目录、层次遍历目录、深度遍历目录及退出管理系统操作。

  1. 设计目的

(1)完成一种简单的目录管理系统,灵活运用自己所学的数据结构知识。

(2)系统的观点和软件开发一般规范进行软件开发,巩固、深化理论知识,提高编程水平,并在此过程中培养严谨的科学态度和良好的工作作风。

  1. 需求概述

完成一种简单的目录管理系统,进行高效搜寻与低复杂度存储,其具体功能需求如下:

  1. 交互性强:能够从终端进行人机交互。

  1. 创建目录:能够已一种高效的数据结构进行数据的存储。

  1. 删除目录:能够删除对应的目录信息。

  1. 查找目录:能够查找对应的目录信息,

  1. 修改目录:能够修改对应的目录信息。

  1. 层次遍历目录:能够层次遍历目录信息。

  1. 深度遍历目录:能够深度遍历目录信息。

  1. 退出管理系统:能够退出管理系统。

  1. 需求说明

完成一种简单的目录管理系统,进行高效搜寻与低复杂度存储。系统能够以交互式界面完成目录的查询、添加、修改、删除、排序等功能,主界面如下图1-1所示。

图1-1 主界面

  1. 概要设计

本节以上述需求明确具体数据结构类型,数据存储方式及功能函数封装。由目录管理系统的特性分析可知,一个父级目录下可以有多个子级目录,多个子级目录并没有顺序关系。因此,本课程设计采取了多叉树的数据结构及进行存储数据,以层次遍历显示所有文件,以深度遍历找到根目录到叶子目录的最长文件路径。

  1. 数据类型的定义

  1. 定义多叉树的节点结构体,代码如下表2-1所示。

表2-1 多叉树的节点结构体源码


typedef struct node_t
{
    char*  name;               // 节点名
    int   n_children;         // 子节点个数
    int   level;              // 记录该节点在多叉树中的层数
    struct  node_t** children; // 指向其自身的子节点,children一个数组,该数组中的元素时node_t*指针
} NODE; // 对结构体重命名
  1. 实现一个栈,用于输出目录时,以子目录到根目录的顺序进行输出,代码如下表2-2所示。

表2-2 栈结构体源码


typedef struct stack_t
{
    NODE**  array; // array是个数组,其元素为NODE*型指针
    int    index; // 指示栈顶元素
    int    size;   // 栈的大小
} STACK; // 重命名
  1. 实现一个队列,用于层次遍历及深度遍历进行输出,代码如下表2-3所示。

表2-3 队列结构体源码


typedef struct queue_t
{
    NODE**  array; // array是个数组,其内部元素为NODE*型指针
    int    head;   // 队列的头
    int    tail;     // 队列的尾
    int    num;    // 队列中元素的个数
    int    size;   // 队列的大小
} QUEUE;

  1. 功能模块结构图

根据需求分析,为满足用户的功能需求,将系统设计为以下三大模块:

  1. 显示模块:交互式界面显示。

(2) 功能模块:实现目录的管理,如创建、删除、查找、修改、遍历等功能。

系统结构如 图 2-4所示:

图2-4 系统结构图

  1. 功能模块

为了实现上述功能模块,分别在多叉树数据结构上定义了多个函数,本系统定义的函数和功能如下表2-5所示:

表2-5 函数与功能对应表

函数名称

功能说明

void* util_malloc(int size)

内存分配函数。

char* util_strdup(char* src)

字符串赋值函数。

FILE* util_fopen(char* name, char* access)

打开文件函数。

STACK* STACKinit(int size)

栈的初始化。

int STACKempty(STACK* sp)

判断栈是否为空。

int STACKpush(STACK* sp, NODE* data)

压栈操作。

int STACKpop(STACK* sp, NODE** data_ptr)

弹栈操作。

void STACKdestroy(STACK* sp)

栈的销毁。

QUEUE* QUEUEinit(int size)

队列初始化。

int QUEUEenqueue(QUEUE* qp, NODE* data)

入队操作。

int QUEUEdequeue(QUEUE* qp, NODE** data_ptr)

出队操作

int QUEUEempty(QUEUE* qp)

判断队列是否为空。

void QUEUEdestroy(QUEUE* qp)

队列的销毁。

NODE* create_node()

生成多叉树节点。

NODE* search_node_r(char name[M], NODE* head)

按照节点名字查找。

void read_file(NODE** head, char* filename)

从文件中读取多叉树,并建立多叉树。

void f1(NODE* head)

层次遍历。

void free_tree_r(NODE* head)

销毁树。

void f2(NODE* head, char* str, char** strBest, int level)

深度遍历。

void Welcome()

显示界面

void Create()

输入节点数据。

int main(int argc, char* argv[])

主界面。

  1. 详细设计

本节在概要设计的基础上,对每个模块内部逻辑处理部分进行详细设计。下面分别讲述核心模块具体实现方法及对应流程图。

  1. 主界面

本模块旨在以良好的交互式界面,提供给客户更好的操作体验。采取了while循环内部判断,根据按键判断以调用相应的处理函数,若未定义按键则需要进入等待直到按出指定按键才会进入下一次循环,具体代码如下表3-1所示。

表3-1 主界面源码


while(1){
                 system("cls");                                           // 清屏
                 Welcome();                                                        //  重新显示界面
                 int i, cho;
                 scanf("%d",  &i);
                 if(1 == i)  Create();
//              if(2 == i)  Delete();
//              if(3 == i)  Search();
//              if(4 == i)  Alter();
                 if(5 == i)  Traversal1();
                 if(6 == i)  Traversal2();
                 if(7 == i)  {
                                                  printf("\n欢迎您再次使用本系统\n\n");
                                                  break;
                                          } 
                 printf("\n返回请输入0\n");
                 begin:                                                                 //  goto 标志位
                         scanf("%d",  &cho);
                 if(0 == cho){
                         continue;
                 }
                 else{
                         printf("您的输入有误,请重新输入\n");
                         goto begin;                                                //  goto 到指定标志位 begin
                 }
        }
  1. 创建多叉树

本次设计通过外部文件与内部存储结合的方式构造了多叉树。需要在交互界面以父节点、子节点个数及子节点名称的顺序进行输入,输入后的数据存放在 data.txt文本中。通过调用data.txt文本数据来进行多叉树的构建,部 分源码如下表3-2所示,流程图如下图3-3所示。

表3-2 创建多叉树源码

图3-3 创建多叉树流程图

  1. 查找多叉树

方式一:通过对文本操作,搜寻指定字符串是否存在。

方式二:遍历多叉树,判断字符名是否相同,具体代码如下表3-4所示。

表3-4查找多叉树源码


NODE*  search_node_r(char name[M], NODE* head)
{
    NODE* temp = NULL;
    int i = 0;
 
    if (head != NULL)
    {
        if (strcmp(name, head->name) == 0)  // 如果名字匹配
        {
            temp = head;
        }
        else // 如果不匹配,则查找其子节点
        {
            for (i = 0; i <  head->n_children && temp == NULL/*如果temp不为空,则结束查找*/; ++i)
            {
                temp = search_node_r(name,  head->children[i]); // 递归查找子节点
            }
        }
    }
 
    return temp; // 将查找到的节点指针返回,也有可能没有找到,此时temp为NULL
}

  1. 删除多叉树

通过对文本操作,搜寻指定字符串是否存在后,删除对应节点。

  1. 修改多叉树

通过对文本操作,搜寻指定字符串是否存在,修改对应节点名称。

  1. 层次遍历多叉树

通过队列对父节点进行存储,若不为空出队操作,让其子节点入队,递归实现。为了良好的用户体验,采取的栈的存储结构,以子节点先输出、父节点后输出的顺序进行显示,具体源码如下表3-5所示。

表3-5层次遍历多叉树源码


void  f1(NODE* head)
{
    NODE* p = NULL;
    QUEUE* q = NULL; // 定义一个队列
    STACK* s = NULL; // 定义一个栈
    int i = 0;
 
    q = QUEUEinit(100); // 将队列初始化大小为100
    s = STACKinit(100); // 将栈初始化大小为100
    
    head->level = 0; // 根节点的深度为0
    
    // 将根节点入队列
    QUEUEenqueue(q, head);
 
    // 对多叉树中的节点的深度值level进行赋值
    // 采用层次优先遍历方法,借助于队列
    while (QUEUEempty(q) == 0) // 如果队列q不为空
    {
        QUEUEdequeue(q, &p); // 出队列
        for (i = 0; i < p->n_children;  ++i)
        {
            p->children[i]->level =  p->level + 1; // 对子节点深度进行赋值:父节点深度加1
            QUEUEenqueue(q,  p->children[i]);      // 将子节点入队列
        }
        STACKpush(s, p); // 将p入栈
    }
 
    while (STACKempty(s) == 0) // 不为空
    {
        STACKpop(s, &p); // 弹栈
        fprintf(stdout, "第%d层 目录名为:%s\n",p->level, p->name);
    }
 
    QUEUEdestroy(q); // 消毁队列
    STACKdestroy(s); // 消毁栈
}
  1. 深度遍历多叉树

具体代码如下表3-6所示。

表3-6 深度遍历多叉树源码


voidf2(NODE* head, char* str, char** strBest, int level)
{
    int  i = 0; 
    char* tmp = NULL;
    if (head == NULL){
        return;
    }
    tmp = (char*)util_malloc((strlen(str) +strlen(head->name) + 1/*原程序中未加1*/) * sizeof(char));
    sprintf(tmp, "%s  %s", str, head->name);
    if (head->n_children == 0){
        if (*strBest == NULL || strlen(tmp)> strlen(*strBest)){
            free(*strBest);
            *strBest = util_strdup(tmp);
        }
    }
    for (i = 0; i < head->n_children;++i){
        f2(head->children[i], tmp, strBest,level + 1);
    }
    free(tmp);
}
 

4运行结果

这里将展示部分功能的运行结果。

4.1 主界面

主界面如下图4-1所示。

图4-1 主界面

4.2 主菜单界面

添加节点界面如下图4-2所示。

图4-2 添加节点界面

4.3 层次遍历界面

层次遍历界面如下图4-3所示。

图4-3 层次遍历界面

4.4 深度遍历界面

深度遍历界面如下图4-4所示。

图4-4 深度遍历界面

代码内容

源码可私信摸鱼哥获取(点个关注私信即可)

  • 21
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
家谱管理系统可以使用树形结构进行存储和管理家谱信息。在C语言中,可以使用结构体来定义家谱节点,结构体的成员可以包括该节点的姓名、性别、出生日期、父亲节点和子节点等信息。具体实现可以参考以下代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NAME_LEN 20 // 家谱节点结构体 typedef struct family_tree_node { char name[MAX_NAME_LEN]; // 姓名 char gender; // 性别 char birthday[11]; // 出生日期,格式为yyyy-mm-dd struct family_tree_node *father; // 父亲节点 struct family_tree_node *child; // 子节点 struct family_tree_node *sibling; // 兄弟节点 } FamilyTreeNode; // 创建家谱节点 FamilyTreeNode *createFamilyTreeNode(char *name, char gender, char *birthday) { FamilyTreeNode *node = (FamilyTreeNode *)malloc(sizeof(FamilyTreeNode)); if (node == NULL) { printf("Error: createFamilyTreeNode failed, out of memory.\n"); return NULL; } strcpy(node->name, name); node->gender = gender; strcpy(node->birthday, birthday); node->father = NULL; node->child = NULL; node->sibling = NULL; return node; } // 添加子节点 void addChild(FamilyTreeNode *parent, FamilyTreeNode *child) { if (parent == NULL || child == NULL) { printf("Error: addChild failed, invalid arguments.\n"); return; } if (parent->child == NULL) { parent->child = child; } else { FamilyTreeNode *sibling = parent->child; while (sibling->sibling != NULL) { sibling = sibling->sibling; } sibling->sibling = child; } child->father = parent; } // 输出家谱信息 void printFamilyTree(FamilyTreeNode *root) { if (root == NULL) { return; } printf("%s %c %s\n", root->name, root->gender, root->birthday); FamilyTreeNode *child = root->child; while (child != NULL) { printFamilyTree(child); child = child->sibling; } } int main() { // 创建家谱 FamilyTreeNode *root = createFamilyTreeNode("张三", 'M', "1980-01-01"); FamilyTreeNode *child1 = createFamilyTreeNode("张四", 'M', "2000-01-01"); FamilyTreeNode *child2 = createFamilyTreeNode("张五", 'F', "2002-01-01"); addChild(root, child1); addChild(root, child2); FamilyTreeNode *grandChild1 = createFamilyTreeNode("张六", 'M', "2020-01-01"); addChild(child1, grandChild1); FamilyTreeNode *grandChild2 = createFamilyTreeNode("张七", 'F', "2022-01-01"); addChild(child1, grandChild2); // 输出家谱信息 printFamilyTree(root); // 释放内存 free(grandChild2); free(grandChild1); free(child2); free(child1); free(root); return 0; } ``` 在上面的代码中,我们定义了一个FamilyTreeNode结构体来表示家谱节点,其中包含姓名、性别、出生日期、父亲节点和子节点等信息。使用createFamilyTreeNode函数可以创建一个家谱节点,使用addChild函数可以将一个节点添加到另一个节点的子节点列表中。最后,使用printFamilyTree函数可以输出整个家谱的信息。需要注意的是,释放内存的操作也需要在程序结束时进行,以避免内存泄漏。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摸鱼哥myg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值