一、要解决的问题
给出某一个操作系统下目录和文件信息,输入的数据第一行为根目录节点。若是目录节点,那么它的孩子节点将在第二行中被列出,同时用一对圆括号“()”界定。同样,如果这些孩子节点中某一个也是目录的话,那么这个目录所包含的内容将在随后的一行中列出,由一对圆括号“()”界定。目录的输入输入格式为:*name size,文件的输入输入格式为:name size。Name为一串不超过10个字符组成,并且字符串中不能有‘(’,‘)’,‘[‘,’]’和’*’。Size是该文件/目录的大小,文件的size输入值为该文件的大小,目录的size输入值都为1。树结构最多10层,每一层最多2个文件/目录。
要求编程实现将其排列成一棵有一定缩进的树,输出要求:第d层的文件/目录名前面需要缩进8*d个空格,兄弟节点要在同一列上。并计算每一个目录大小,目录大小为所包含的所有子目录和文件大小以及自身大小的总和。
例如输入:
*/usr 1
(*mark 1 *alex 1)
(hw.c 3 *course 1) (hw.c 5)
(aa.txt 12)
输出
|_*/usr[24]
|_*mark[17]
| |_hw.c[3]
| |_*course[13]
| |_aa.txt[12]
|_*alex[6]
|_hw.c[3]
二、算法基本思想描述:
采用孩子兄弟双亲链表的数据存储结构建立二叉树,再先序遍历该二叉树输出所有节点。输出时,通过parent节点的第一个孩子是否有兄弟节点控制缩进输出” | ”或” ”;目录的大小为该目录左子树(以其第一个孩子为根的树)所有节点的size和加上它本身大小。
三、设计
1. 数据结构的设计和说明
在一开始设计要采用的数据结构时,虽然课题只要求“树结构最多10层(树的深度),每一层最多2个文件/目录”,考虑到问题的实际意义,我决定把它优化消除这一限制,于是采用孩子兄弟的数据结构,后来由于缩进输出的需要又增加了parent域。
2. 关键算法的设计
(1)输入处理:从外部文件每读入一行处理一行,数据结构的构造与读入同时进行,提取节点的name[]域和size域建立该行所有节点。其间,利用队列设立parentArray[]用来记录所有的目录节点地址,设置两个虚拟指针rear和head,每增加一个目录rear+1,head标记可作为当前节点parent域的目录,每遇到一个‘)’ head+1处理下一个目录包含的节点。当输入文件结束时所采用的孩子兄弟双亲链表二叉树也就建立完成。
(2)输出处理:先序遍历所建立的二叉树,每个节点输出一行,都是先通过其parent节点的第一个孩子是否有兄弟节点标记flag[0]~flag[i],然后从flag[i]~flag[0]控制输出缩进符” | ”或” ”后再输出该节点信息。
3. 数据结构图:
四、源程序清单(Win7系统WinTC环境下编译运行通过):
/*
========================================================
课题:文件目录结构的显示
Author:Estrong. All Rights Reserved.
指导老师:LinFang
2011年1月7日写于福建工程学院.
========================================================
*/
#include "stdio.h"
#include "string.h"
#define Null 0
#define MAX 200
/*输入文件每行应不超过MAX个字符*/
typedef struct node /*采用孩子兄弟双亲链表的存储结构*/
{
char name[12];
unsigned int size;
struct node *parent,*first_child,*next_sibling;
}BinTNode,*BinTree;
/* 添加新节点 */
BinTree New_BinTNode(char str[],int *I,BinTree parentArray[],int *head,int *rear)
{ int i=0;
BinTree bt;
bt=(BinTNode *)malloc(sizeof(BinTNode));
bt->parent=parentArray[*head]; /*链接双亲结点确定归属目录*/
bt->first_child=Null;
bt->next_sibling=Null;
if(str[*I]=='*')
{(*rear)++; parentArray[*rear]=bt;}
while(str[*I]!=' ')
{
bt->name[i]=str[*I];
(*I)++;i++;
}
bt->name[i]='\0';(*I)++;
bt->size=str[*I]-'0';(*I)++;
while ((str[*I]!=' ')&&(str[*I]!=')')&&((*I)<strlen(str)-1)) /*条件(*I)<strlen(str)-1)是考虑到根节点没有‘ ’和')'*/
{
bt->size=bt->size*10+(str[*I]-'0');
(*I)++;
}
return bt;
}
/* 创建数据(将输入数据转化为所采用的树结构) */
BinTree Create_BinTree(FILE *input)
{ int I=0,rear=0,head=1; /*parentArray[head]用来记录当前节点应指向的parent目录节点*/
/*parentArray[rear]用来记录最后一个目录节点*/
BinTree parentArray[MAX];/*parentArray[]用来记录所有的目录节点地址*/
BinTree root,bt,pbt;
char str[MAX];
fgets(str,MAX,input);
root=New_BinTNode(str,&I,parentArray,&head,&rear);
root->parent=Null; /* 在创建根节点时其parent的赋值是随机的,这里给它赋回空 */
while(!feof(input))
{I=1; /*I是输入文件每行字符串中的下标,贯穿该行字符处理始末*/
fgets(str,MAX,input);
while(I<=(strlen(str)-1))
{
if(str[I]!=')')
{
bt=New_BinTNode(str,&I,parentArray,&head,&rear);
parentArray[head]->first_child=bt;
pbt=bt;
do /*创建并链接一对括号里的所有兄弟节点*/
{
if(str[I]==' ')
{
I++;
pbt->next_sibling=New_BinTNode(str,&I,parentArray,&head,&rear); /*创建兄弟节点*/
pbt=pbt->next_sibling;
}
}while(str[I]!=')') ;
head++; I=I+3; /* 此时遇到右括号head+1处理下一个目录;I+3跳过") (" */
}
else
{head++ ; I=I+3;} /* 此时也遇到右括号 */
}
}
return root;
}
/* 求目录大小 */
int SIZE(BinTree bt) /*返回当前目录左子树的大小(未加上原目录大小)*/
{ int size;
if(bt)
size=bt->size+SIZE(bt->first_child)+SIZE(bt->next_sibling);
else size=0;
return size;
}
/* 输出单个节点 */
void out(BinTree bt,FILE *output)
{ char size_str[10];
BinTree pbt=bt->parent;
int i=-1,j=0,k;
int flag[11]; /*flag[i]用来控制缩进,子目录深度可以设置,目前设为11*/
for(k=0;k<11;k++) flag[k]=0;
if(bt->name[0]=='*') bt->size=bt->size+SIZE(bt->first_child);
/*目录的大小为该目录本身大小及其左子树(以其第一个孩子为根的树)所有节点的size和*/
while(pbt!=Null)
{
i++;
if(pbt->next_sibling) flag[j]=1; /*输出时flag[i]=1缩进"| " */
else flag[j]=0; /*输出时flag[i]=0缩进" " */
pbt=pbt->parent;
j++;
}
while(i>=0)
{
if(flag[i]) {printf("| ");fprintf(output,"| ");}
else {printf(" ");fprintf(output," ");}
i--;
}
printf("|_%s[%d]\n",bt->name,bt->size);
fprintf(output,"|_%s[%d]\n",bt->name,bt->size);
}
/* 先序遍历输出整棵树 */
void OUTPUT(BinTree bt,FILE *output)
{
if(bt)
{
out(bt,output);
OUTPUT(bt->first_child,output);
OUTPUT(bt->next_sibling,output);
}
}
/* 主函数 */
main()
{ FILE *input,*output;
BinTree root;
if((input=fopen("input.txt","r"))==NULL)
{
printf("Cannot find the file! Strike any key to exit!");
getch();
exit(1);
}
output=fopen("output.txt","w");
root=Create_BinTree(input);
OUTPUT(root,output);
getch();
fclose(input);fclose(output);
}
五、测试数据及测试结果:
(注:以下测试数据置于外部文件input.txt里)
*/kqss 1
(*mark 1 *alex 1 *course 1 aa.txt 12 hw.c 5 hjh.c 4 gffg.c 5)
() (kkhw.c 3 *course 1) (ghw.c 5 *test 1 abcd.exe 13 *fdg 1 hw.c 5 hjh.c 4)
(aa.txt 2 kqss.c 100 hgfgsdf.h 35 ghgjh.mp3 7) () (arfe.txt 12 kqss.c 100 fdfs.h 35)