基础实验4-2.6 目录树 (30分)
在ZIP归档文件中,保留着所有压缩文件和目录的相对路径和名称。当使用WinZIP等GUI软件打开ZIP归档文件时,可以从这些信息中重建目录的树状结构。请编写程序实现目录的树状结构的重建工作。
输入格式:
输入首先给出正整数N(≤104),表示ZIP归档文件中的文件和目录的数量。随后N行,每行有如下格式的文件或目录的相对路径和名称(每行不超过260个字符):
- 路径和名称中的字符仅包括英文字母(区分大小写);
- 符号“\”仅作为路径分隔符出现;
- 目录以符号“\”结束;
- 不存在重复的输入项目;
- 整个输入大小不超过2MB。
输出格式:
假设所有的路径都相对于root目录。从root目录开始,在输出时每个目录首先输出自己的名字,然后以字典序输出所有子目录,然后以字典序输出所有文件。注意,在输出时,应根据目录的相对关系使用空格进行缩进,每级目录或文件比上一级多缩进2个空格。
输入样例:
7
b
c
ab\cd
a\bc
ab\d
a\d\a
a\d\z\
输出样例:
root
a
d
z
a
bc
ab
cd
d
c
b
题目分析:
首先要注意的是目录和文件输出的优先级不同,优先输出目录,其次是文件。后面跟了\的就是目录,没有\的是文件。我用结构体中的ismulu区分目录和文件:ismulu=1表示目录,ismulu=0表示文件。
样例中每一层可能有多个结点,属于多叉树,这里通过父子-兄弟表示法,将多叉树转化为二叉树进行表示。父子关系用结点-左孩子表示,兄弟关系用结点-右孩子表示。于是样例转化为二叉树表示如下:
每次读入一行,每一行里面,后一层结点总是作为前一层结点的左孩子(或左孩子的右孩子序列中的结点)插入。因此每一行的内层循环中总是要把q更新为前一层结点。
而读入不同行时,每一次都要将q重新置为根结点F,从根结点开始插入。
最后先序遍历输出各结点和相应的空格数。
C++代码实现:
#include <iostream>
#include<string>
using namespace std;
typedef struct file* File;
struct file
{
string filename;
File Left;
File Right;
int ismulu; //判断是目录还是文件,ismulu=1表示是目录,ismulu=0表示是文件。目录输出的优先级比文件的高。
int blank; //记录需要输出的空格数
};
void Preorder(File F) //先序遍历输出
{
if (F == NULL)return;
int i;
for (i = 0; i < F->blank; i++)
{
cout << " ";
}
cout << F->filename << endl;
Preorder(F->Left);
Preorder(F->Right);
}
int main()
{
int N;
cin >> N;
string s;
int i, j, k;
File F = new struct file;
File q = new struct file;
File h = new struct file;
F->filename = "root"; //根结点
F->Left = F->Right = NULL;
F->blank = 0; F->ismulu = 0;
q = F;
char* name = new char[261];
int t;
for (i = 0; i < N; i++) //循环N遍,读入N行字符串
{
cin >> s;
t = 0; //记录是第几层路径下的,2*t就是需要输出的空格数
for (k = 0, j = 0; j < s.size(); j++)
{
while (j < s.size() && s[j] != '\\') //每次读入一个目录名或一个文件名
{
name[k] = s[j];
j++; k++;
}
name[k] = '\0';
k = 0;
t++; //路径层数+1
File p = new struct file; //创建结点
p->filename = name;
if (j == s.size())p->ismulu = 0;
else p->ismulu = 1;
p->Left = p->Right = NULL;
p->blank = 2 * t;
if (q->Left == NULL) //结点q没有左孩子结点,新创建的p就作为q的左孩子结点
{
q->Left = p;
}
else //结点q有左孩子结点
{
//新创建的结点p需取代原来的结点q的左孩子结点,成为结点q的左孩子结点
if (q->Left->ismulu < p->ismulu || (q->Left->ismulu == p->ismulu&&q->Left->filename > p->filename))
{
p->Right = q->Left;
q->Left = p;
}
//新创建的结点p等于结点q的左孩子结点
else if (q->Left->ismulu == p->ismulu&&q->Left->filename == p->filename)
{
p = q->Left;
}
//新创建的结点p需插入结点q的左孩子结点的右孩子序列中
else
{
q = q->Left;
while (q->Right != NULL && (q->Right->ismulu > p->ismulu || (q->Right->ismulu == p->ismulu&&q->Right->filename < p->filename))) //找到结点p插入的位置
{
q = q->Right;
}
if (q->Right&&q->Right->ismulu == p->ismulu&&q->Right->filename == p->filename) //结点p在右孩子序列中已经存在了,则不插入,更新p值为序列中存在的那个结点
{
p = q->Right;
}
else { //结点p插入结点q的左孩子结点的右孩子序列
p->Right = q->Right;
q->Right = p;
}
}
}
q = p; //更新q值。同一行输入中路径的下一层结点作为q的左孩子结点或者插入q的左孩子结点的右孩子序列中
}
q = F; //每读入一行将q初始化为根结点F
}
Preorder(F);//先序遍历
return 0;
}