1.7.课设实验-数据结构-二叉树-文件夹创建系统

一.题目:

利用二叉树简单理解文件夹创建的过程。

二.代码:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>

//二叉树结构体 
typedef struct BiTNode
{
	//1.结点即文件夹名称
	char nameFileNode[50];
	//2.左、右孩子指针即子目录 
	struct BiTNode *lchild;
	struct BiTNode *rchild;
	//3.父指针即父级目录 
	struct BiTNode *parent; 
}BiTNode,*BiTree;

//单个文件夹在顺序表中的数据 
typedef struct
{
	//1.文件夹名称
	char nameFile[50];
	//2.文件夹的父级路径的索引
	int parentIndex; 
}FileData; 

//存储二叉树即文件夹的顺序表->长度采用动态变化 
typedef struct 
{
	//1.指向动态数组的指针->表示连续存储的空间 
    FileData *data;
	//2.顺序表中当前文件夹个数    
    int numFile; 
	//3.动态数组当前数组最大容量        
    int capacity;      
}FileList;

/*---------------------------------------赵旭文---------------------------------------*/
//求文件夹二叉树的树高的函数
int treeDepth(BiTree T)
{
	if(T==NULL)
	{
		return 0;
	}
	else
	{
		int l = treeDepth(T->lchild);
		int r = treeDepth(T->rchild);
		return l > r ? l+1 : r+1;
	}
} 

//查找父级目录在文件夹顺序表中的位置的函数
/*形参需要文件夹顺序表、父指针指向的文件夹*/ 
/*返回父级目录在文件夹顺序表中的索引*/ 
int findParent(FileList list,BiTNode *parent)
{
	//特殊情况即根结点->只有根结点的父指针为NULL 
	if(parent == NULL) 
	{
		return -1;  // 根节点的父节点索引为-1 
	}
	//1.遍历文件夹顺序表查找是否有与父指针指向的文件夹同名的文件夹
	for( int i=0 ; i<list.capacity ; i++ )
	{
		//2.判断当前索引上的文件夹与父指针指向的文件夹是否同名
		int result = strcmp( list.data[i].nameFile , parent->nameFileNode );
		//3.结果
		if(result==0)
		{
			//4.查找成功,返回索引
			return i; 
		} 
	} 
	//5.查找失败,返回-1
	return -1; 
}
/*---------------------------------------赵旭文---------------------------------------*/

/*---------------------------------------黄黄---------------------------------------*/
//添加文件夹到文件夹顺序表的函数
/*形参包括文件夹顺序表、插入的文件夹名称(插入到顺序表)、文件夹二叉树(用来求扩容常量)、插入的文件夹(找父级路径)*/
void InsertFileData(FileList &list,char name[50],BiTree T,BiTNode file)
{
	//1.把文件夹放入文件夹顺序表的最后一位(刚好就是list.numFile,比如0个文件夹时新添加一个就在0索引)
	strcpy( list.data[ list.numFile ].nameFile , name );  
	//2.补全刚放入的文件夹的父级目录的索引
	/*2.1.调用函数findParent查找父级目录在文件夹顺序表中的索引*/
	int index= findParent( list , file.parent );
	/*2.2.赋值*/
	list.data[ list.numFile ].parentIndex = index;
	//3.文件夹顺序表的文件夹数量加1 
	list.numFile++;
	//4.文件夹顺序表扩容
	/*4.1.求文件夹二叉树的高度*/
	int height=treeDepth(T);
	/*4.2.得出扩容结果*/
	//list.capacity = list.capacity + pow(2,height);这里只改变了capacity,但没有重新分配内存(会出现主函数崩溃),正确如下:
	//得出新的扩容结果并记录
	int newCapacity = list.capacity + pow(2, height); 
	/*调用realloc函数,第一个参数就是先前获得的内存块,第二个参数就是新扩容的内存块,
	如果需要还会复制旧内存块的内容*/   
    FileData *newData = (FileData*)realloc( list.data, sizeof(FileData) * newCapacity );
	//判断 
    if(newData!=NULL) 
    {
    	//此时成功分配 
    	list.data = newData; //记录新的扩容结果 
        list.capacity = newCapacity; //记录扩容后的长度 
	} 
} 	 

// 创建文件夹(结点)即插入结点到二叉树的函数
/*形参需要文件夹二叉树、新插入的文件夹名称、文件夹顺序表*/
void creatFile(BiTree &T, char inputFile[50],FileList &list)
{
	//1.判空 
    if(T == NULL) return;
    //2.在文件夹二叉树中找到要插入的位置,判断是否创建在当前文件夹的子目录下
    printf("当前文件夹:%s \n", T->nameFileNode);
    printf("请选择创建位置(1.左子目录 2.右子目录 3.取消创建):");
    int result;
    scanf("%d", &result);  
    //3.判断 
    if(result == 1) 
	{
        //4.创建在左目录下
        if(T->lchild == NULL) //左子目录为空,可直接创建 
		{
            //4.1.先给T的左孩子分配内存空间
            T->lchild = (BiTree)malloc(sizeof(BiTNode));
            //4.2.判断 
            if( T->lchild == NULL) /*内存分配失败*/
			{
                printf("内存分配失败!\n");
                return;
            }
            else /*内存分配成功*/
            {
                strcpy(T->lchild->nameFileNode, inputFile); //初始化新节点
                T->lchild->lchild = NULL; //设置左指针 
                T->lchild->rchild = NULL; //设置右指针 
                T->lchild->parent = T;  //设置父指针
                printf("文件夹 '%s' 创建成功!\n", inputFile);
                InsertFileData(list, inputFile, T , *(T->lchild)); //添加到顺序表中.第四个形参是插入的文件夹即左孩子上 
			}
        } 
		else //左子目录不为空,需递归 
		{
            printf("左子目录已存在,递归创建...\n");
            creatFile(T->lchild, inputFile , list);
        }
    } 
    else if(result == 2) 
	{
        //5.创建在右目录下
        if(T->rchild == NULL) //右子目录为空,可直接创建
		{
            //5.1.先给T的右孩子分配内存空间
            T->rchild = (BiTree)malloc(sizeof(BiTNode));
            //5.2.判断 
            if(T->rchild == NULL) /*内存分配失败*/
			{
                printf("内存分配失败!\n");
                return;
            }
            else /*内存分配成功*/
            {
            	
                strcpy(T->rchild->nameFileNode, inputFile); // 初始化新节点
                T->rchild->lchild = NULL; //设置左指针
                T->rchild->rchild = NULL; //设置右指针
                T->rchild->parent = T;  // 设置父指针
                printf("文件夹 '%s' 创建成功!\n", inputFile);
                InsertFileData(list, inputFile, T , *(T->rchild)); // 添加到顺序表中
		    }
        } 
		else //右子目录不为空,需递归 
		{
            printf("右子目录已存在,递归创建...\n");
            creatFile(T->rchild, inputFile, list);
        }
    }
    else 
	{
		//6.取消创建 
        printf("取消创建操作。\n");
        return;
    }
}
/*---------------------------------------黄黄---------------------------------------*/


/*---------------------------------------青格勒---------------------------------------*/
//删除文件夹(结点)即删除二叉树中结点的函数->1次只删1个文件夹,删两个会出现父级路径和子文件夹都删了  
/*形参需要文件夹二叉树、要删除的文件夹名称、文件夹顺序表*/
void deleteFile(BiTree &T, char deleteFileName[50], FileList &list)
{
    // 1.首先在顺序表中查找要删除的文件夹
    int deleteIndex = -1;
    for(int i = 0; i < list.numFile; i++)
    {
        if( strcmp(list.data[i].nameFile, deleteFileName) == 0)
        {
        	//代表查找成功 
            deleteIndex = i;
            break;
        }
    }
    // 2.如果未找到,直接返回
    if(deleteIndex == -1)
    {
        printf("删除失败,文件夹 '%s' 不存在!\n", deleteFileName);
        return;
    }
    //3.在二叉树中查找要删除的结点 
    BiTree deleteNode = NULL; //要删除的结点即要删除的文件夹 
    BiTree parentNode = NULL; //要删除的文件夹的父级路径 
    int isLeftChild = 0; // 0:不是左孩子 1:是左孩子
    //3.1.使用层次遍历查找要删除的节点->使用到了队列->类似线性查找 
    BiTree queue[100]; //队列->长度为100,类型为文件夹二叉树 
    int front = 0, rear = 0; //front为头指针,rear为尾指针 
    queue[rear++] = T; //初始时访问根结点,根结点T入队->注:是先赋值,再rear++(rear++得出的rear就指向了下一个插入的位置) 
    //3.2.循环遍历文件夹二叉树->只要头指针小于尾指针,就说明队列里有文件夹,就需要继续处理 
    while(front < rear)
    {
    	//3.2.先处理头指针,把头指针设为当前处理的文件夹current,然后front++(代表下次处理后一个文件夹) 
        BiTree current = queue[front++];
        //3.3.对比当前处理的文件夹与要删除的文件夹是否同名 
        if( strcmp(current->nameFileNode, deleteFileName) == 0)
        {
        	//3.4.找到了要删除的文件夹,赋值给deleteNode,并跳出循环 
            deleteNode = current;
            break;
        }
        //3.4.此时意味着没有找到要删除的文件夹,让其左子树、右子树依次入队列进行查找 
        if(current->lchild != NULL)
        {
        	//先赋值,再rear++ 
            queue[rear++] = current->lchild;
        }
        if(current->rchild != NULL)
        {
        	//先赋值,再rear++ 
            queue[rear++] = current->rchild;
        }
    }
    //3.5.判空 
    if(deleteNode == NULL)
    {
        printf("删除失败,在二叉树中未找到文件夹 '%s'!\n", deleteFileName);
        return;
    }
    //4.获取父节点信息
    parentNode = deleteNode->parent;
    //4.1.判空 
    if(parentNode != NULL)
    {
    	//4.2.父结点不为空 
        if(parentNode->lchild == deleteNode)
        {
        	//4.3.意味着要删除的文件夹deleteNode在其父级路径的左孩子上 
            isLeftChild = 1;
        }
        else
        {
        	//4.4.意味着要删除的文件夹deleteNode在其父级路径的右孩子上
            isLeftChild = 0;
        }
    }
    //5.检查要删除的节点是否有子节点
    if(deleteNode->lchild != NULL || deleteNode->rchild != NULL)
    {
        printf("删除失败,文件夹 '%s' 包含子文件夹,请先删除子文件夹!\n", deleteFileName);
        return;
    }
    //6.从二叉树中删除节点
    //6.1.删除的是根节点
    if(parentNode == NULL) 
    {
        printf("警告:正在删除根节点!\n");
        free(T);
        T = NULL;
    }
    else //6.2.删除非根节点
    {
        if(isLeftChild)
        {
        	//6.3.此时说明要删除是父级路径的左子树 
            parentNode->lchild = NULL;
        }
        else
        {
        	//6.4.此时说明要删除是父级路径的右子树 
            parentNode->rchild = NULL;
        }
        //6.5.清空 
        free(deleteNode);
    }
    //7.从顺序表中删除对应的文件夹数据
    //7.1 首先删除所有以该文件夹为父节点的子文件夹
    for(int i = 0; i < list.numFile; i++)
    {
    	//7.2.找到了孤立的文件夹 
        if(list.data[i].parentIndex == deleteIndex)
        {
            printf("警告:发现孤立的子文件夹 '%s',正在清理...\n", list.data[i].nameFile);
        }
    }
    //7.3.移动顺序表中的文件夹,覆盖要删除的元素(删除最后1个文件夹时无需移动)
    for(int i = deleteIndex; i < list.numFile - 1; i++)
    {
    	//7.4.把后一个文件夹移动到当前文件夹 
        strcpy(list.data[i].nameFile, list.data[i + 1].nameFile);
        //7.5.更改父级路径索引 
        list.data[i].parentIndex = list.data[i + 1].parentIndex;
        //7.6.更新父索引:如果父索引大于删除索引,需要减1->减1才能移动指向前1位(因为删除了1个文件夹) 
        if(list.data[i].parentIndex > deleteIndex)
        {
            list.data[i].parentIndex--;
        }
    }
    // 7.3 更新顺序表计数即顺序表中文件夹个数减1 
    list.numFile--;
    // 8.更新所有父索引指向被删除节点的子节点->遍历查找是否有父索引等于删除索引的
    for(int i = 0; i < list.numFile; i++)
    {
    	//8.1.找到了父索引指向删除索引的 
        if(list.data[i].parentIndex == deleteIndex)
        {
        	//8.2.设置为根节点或无父节点
            list.data[i].parentIndex = -1; 
            printf("警告:文件夹 '%s' 的父文件夹已被删除,已将其父索引重置\n", list.data[i].nameFile);
        }
    }
    printf("文件夹 '%s' 删除成功!\n", deleteFileName);
    // 9.打印删除后的顺序表状态(用于调试)
    printf("删除后顺序表状态:\n");
    for(int i = 0; i < list.numFile; i++)
    {
        printf("索引%d: 名称为%s, 父索引为%d\n", i, list.data[i].nameFile, list.data[i].parentIndex);
    }
}

//带交互的删除函数包装器->进入删除操作的函数 
void deleteFileInteractive(BiTree &T, FileList &list)
{
	//1.判空 
    if(list.numFile == 0)
    {
        printf("当前没有文件夹可删除!\n");
        return;
    }
    //2.此时文件夹顺序表不为空,打印 
    printf("当前所有文件夹:\n");
    for(int i = 0; i < list.numFile; i++)
    {
    	//2.1.打印当前文件夹名称 
        printf("%d. %s", i + 1, list.data[i].nameFile);
        //2.2.打印当前文件夹的父级路径->根结点无需打印父级路径 
        if(list.data[i].parentIndex != -1)
        {
            printf(" (父文件夹: %s)", list.data[list.data[i].parentIndex].nameFile);
        }
        printf("\n");
    }
    //3.输入要删除的文件夹的名称 
    printf("请输入要删除的文件夹名称:");
    char deleteFileName[50];
    scanf("%s", deleteFileName);
    //4.调用deleteFile函数进行删除 
    deleteFile(T, deleteFileName, list);
}
/*---------------------------------------青格勒---------------------------------------*/

/*---------------------------------------吴佳峻---------------------------------------*/
//查找文件夹(结点)的函数
/*形参需要文件夹顺序表、要查找的文件夹的名称*/
void findFile(FileList list,char findFileName[50])
{
	//1.遍历顺序表查找是否存在 
    for( int i = 0; i < list.numFile; i++ )
    {
        //2.记录对比结果 
        int result = strcmp( list.data[i].nameFile , findFileName);
        //3.判断 
        if(result == 0) /*查找成功->找出当前文件夹完整路径*/
        {
            printf("找到文件夹 '%s'\n", findFileName);
            // 3.1.构建路径栈->打印完整路径,用栈,因为最后找到磁盘,但磁盘先打印,为栈 
            char pathStack[100][50]; // 存储路径组件
            int stackTop = 0;
            // 3.2.当前文件夹入栈
            strcpy( pathStack[stackTop], list.data[i].nameFile);
            stackTop++;
            // 3.3.回溯父路径
            int fatherIndex = list.data[i].parentIndex; //记录父级文件夹索引 
            while(fatherIndex != -1) //等于-1时意味着找到根目录,停止循环
            {
                strcpy( pathStack[stackTop], list.data[fatherIndex].nameFile);
                stackTop++;
                fatherIndex = list.data[fatherIndex].parentIndex;
            }
            // 3.4.从根节点开始拼接完整路径
            printf("完整路径: ");
            for(int j = stackTop - 1; j >= 0; j--) //从栈顶开始 
            {
                printf("%s", pathStack[j]);
                if(j > 0) printf("\\");
            }
            printf("\n");
            return;
        }
    }
    // 4.查找失败
    printf("查找失败,该文件夹 '%s' 不存在\n", findFileName);
} 

//初始化文件夹顺序表的函数
void initFileList(FileList *list, int capacity) 
{
	//1.创建连续的存储空间 
    list->data = (FileData*)malloc(sizeof(FileData) * capacity);
    //2.初始时顺序表没有任何文件夹 
    list->numFile = 0;
    //3.初始动态数组当前最大容量  
    list->capacity = capacity;
}

//初始化文件夹二叉树的函数
void initBiTree(BiTree &T,FileList &list) //不是BiTree *T,而是BiTree &T,传地址 
{
	//1.申请根结点存储空间->根结点为结点,用BiTNode申请空间,最终要是BiTree型即树 
	T = (BiTree)malloc( sizeof(BiTNode) );
	//2.根结点赋值
	strcpy( T->nameFileNode , "D盘" );
	//3.一开始只有根结点,根结点的初始左、右孩子指针都为NULL
	T->lchild = NULL;
	T->rchild = NULL;
	//4.根结点的父指针指向NULL
	T->parent = NULL; 
	//5.把根结点放入文件夹顺序表
	/*对于根结点需要特殊处理,在InsertFileData函数中第四个参数BiTNode parent是BiTNode型,
	  因此传第四个参数时不能直接传T,因为T是BiTree型,由于此时是根结点,父指针必为NULL,所以
	  可以定义一个BiTNode类型的变量,让其父指针为NULL*/
	BiTNode parentT;
	parentT.parent = NULL; 
	InsertFileData(list,T->nameFileNode,T,parentT);
} 
/*---------------------------------------吴佳峻---------------------------------------*/

int main()
{
	//1.创建文件夹顺序表->可以是全局变量,也可以是局部变量 
	FileList list; /*注:全局变量必须有初值*/
	//2.初始化文件夹顺序表->函数调用必须在main函数里 
    initFileList(&list,1); /*初始化容量为1*/
    /*
    list->data:指向一块可以容纳1个FileNode的连续内存空间
    list->numFile = 0:当前实际存储的文件夹个数,初始时为0
    list->capacity = 1:数组的最大容量,表示当前最多可以存储1个FileNode
    */
    //3.创建文件夹二叉树->初始为一个空树 
	BiTree root=NULL; 
	//4.初始化文件夹二叉树
	initBiTree(root,list); 
	//界面 
	printf("------------------------------------------- \n");
	printf("    1.创建文件夹           2.删除文件夹 \n");
	printf("    3.查找文件夹           4.退出 \n");
	printf("------------------------------------------- \n");
	while(true)
	{
		printf("请输入接下来的操作编号:");
		int choose;
	    scanf("%d",&choose);
	    printf("------------------------------------------- \n");
		switch(choose)
	    {
		    case 1:
		    	{
		    		//输入信息
					printf("请输入新创建的文件夹名称:");
					char inputFile[50];
					scanf("%s",inputFile); 
		    		creatFile(root,inputFile,list);
		    		printf("------------------------------------------- \n");
		    		break;
				}
		    case 2:
		        {
		        	// 修改这里的调用
                    deleteFileInteractive(root, list);
                    printf("------------------------------------------- \n");
		    	    break;
			    }
		    case 3:
		        {
		        	//输入信息
					printf("请输入要查找的文件夹:"); 
		        	char findFileName[50];
		        	scanf("%s",findFileName);
		    	    findFile(list,findFileName);
		    	    printf("------------------------------------------- \n");
		    	    break;
			    }		
		    case 4:
		        {
		    	    exit(0);
			    }
		    default:
		        {
		    	    printf("该操作不存在 \n");
		    	    printf("------------------------------------------- \n");
		    	    break;
			    }		
	}
	}
	return 0;
}
相关推荐
sigd4 小时前
排队选人-2024年秋招-小米集团-软件开发岗-第二批笔试
数据结构·算法
NEU-UUN4 小时前
C语言 . 第三章第三节 . 变参函数
c语言·开发语言
第七序章4 小时前
【C + +】C + + 11(中)——Lambda 表达式 + 可变参数模板
c语言·c++·算法·1024程序员节
Shylock_Mister4 小时前
FreeRTOS事件组全解析:多任务同步核心技巧
c语言·单片机·物联网
散峰而望14 小时前
C++入门(一)(算法竞赛)
c语言·开发语言·c++·编辑器·github
l1t15 小时前
利用DeepSeek辅助修改luadbi-duckdb读取DuckDB decimal数据类型
c语言·数据库·单元测试·lua·duckdb
GOATLong16 小时前
git使用
大数据·c语言·c++·git·elasticsearch
学习路上_write18 小时前
STM32回调函数使用/定时器/GPIO/串口/
c语言·单片机·嵌入式硬件
立志成为大牛的小牛18 小时前
数据结构——三十七、关键路径(王道408)
数据结构·笔记·程序人生·考研·算法