PTA习题:基础实验4-2.6 目录树 (30分)

基础实验4-2.6 目录树 (30分)

在ZIP归档文件中,保留着所有压缩文件和目录的相对路径和名称。当使用WinZIP等GUI软件打开ZIP归档文件时,可以从这些信息中重建目录的树状结构。请编写程序实现目录的树状结构的重建工作。

输入格式:
输入首先给出正整数N(≤10​4​​),表示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;
}


  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值