PTA 目录树

234 阅读5分钟

在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
结尾无空行

代码1:

# include <iostream>
# include <algorithm>
# include <string>
using namespace std;

typedef struct CSNode
{
	string data;    //数据域
	struct CSNode* mulu{}; //指向对应长子结点的指针域
	struct CSNode* file{};   //指向对应右兄弟结点的指针域
	int flag{};    //判断是文件还是目录的 flag
}CSNode, * CSTree;

CSTree insertNode(CSTree t, const string& str, int flag)    //核心步骤
{
	CSTree a_node = new CSNode;
	CSTree pre = t, ptr;
	a_node->data = str;    //初始化新结点
	a_node->mulu = a_node->file = nullptr;
	a_node->flag = flag;
	if (t->mulu == nullptr)    //所在目录没孩子,直接插入结点
	{
		t->mulu = a_node;
		return t->mulu;
	}
	ptr = t->mulu; //由于根结点本身插入时,是插在长子位,因此另外设置 pre 当前驱结点,ptr 当 pre 的后继
	while (ptr != nullptr && ((ptr->flag > a_node->flag) ||
		     (ptr->flag == a_node->flag && str > ptr->data)))
	{
		pre = ptr;
		ptr = ptr->file;
	}

	if (ptr == nullptr)    //要先判空,不然有段错误
	{                      //无处可插入,插在链尾
		a_node->file = pre->file;
		pre->file = a_node;
		return a_node;    //接下来以 a_node 为根目录操作
	}
	else if (ptr->data == a_node->data && ptr->flag == a_node->flag) //目录或文件已存在
	{
		delete a_node;    //把申请的新结点打掉
		return ptr;    //接下来在已有的 ptr 目录下操作
	}
	else    //找到了应该插入的位置
	{
		if (pre->data == t->data)    //插在根目录的长子位
		{
			a_node->file = pre->mulu;
			pre->mulu = a_node;
		}
		else    //正常插入
		{
			a_node->file = pre->file;
			pre->file = a_node;
		}
		return a_node;    //接下来以 a_node 为根目录操作
	}
}

void createTree(CSTree pre, string str)
{
	int idx = 0;

	getline(cin, str);
	for (int i = 0; i < str.size(); i++)
	{
		if (str[i] == '\\')    //注意用反义字符,不然会报错
		{                              //只要不在串尾,只会是目录
			pre = insertNode(pre, str.substr(idx, i - idx), 1);
			idx = i + 1;    //移动字符串到下一个目录,即 '\' 之后
		}
	}
	if (idx < str.size())    //文件只出现在字符串尾
	{
		insertNode(pre, str.substr(idx, str.size() - idx), 0);
	}
}

void PrintTree(CSTree T, int space)    //遍历函数
{                                         
	if (T == nullptr)
		return;
	for (int i = 0; i < space; i++) //输出空格
	{
		cout << " ";
	}
	cout << T->data << endl;    //前序遍历
	PrintTree(T->mulu, space + 2); //下一层多两个空格
	PrintTree(T->file, space);       //兄弟结点不需要多空格
}

int main()
{
	int num;
	string str;
	CSTree Tree = new CSNode;
	Tree->data = "root";
	Tree->mulu = Tree->file = nullptr;
	Tree->flag = 1;
	cin >> num;
	for (int i = 0; i <= num; i++)
		createTree(Tree, str);
	PrintTree(Tree, 0);
}

提交结果1:

代码2:

# 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;
}

提交结果2:

代码3:

# include <iostream>
# include <algorithm>
# include <cstring>
using namespace std;
typedef struct File_* File;
typedef struct File_
{
	char name[270];
};
typedef struct Node* Tree;
typedef struct Node
{
	char name[270]{};
	Tree child[507]{};
	File files[507]{};
	int child_num = 0;
	int files_num = 0;
};
bool cmp1(Tree a, Tree b)
{
	return strcmp(a->name, b->name) < 0;
}
bool cmp2(File a, File b)
{
	return strcmp(a->name, b->name) < 0;
}
void solve(char* s, Tree parent)
{
	int pos = 0, len = strlen(s);
	for (; (s[pos] != 92) && pos < len; ++pos);
	if (s[pos] == 92)
	{
		char name_[270];
		int exist = 0;
		strncpy(name_, s, pos);
		name_[pos] = '\0';
		Tree now;
		for (int i = 0; i < parent->child_num; ++i)
		{
			if (strcmp(name_, parent->child[i]->name) == 0)
			{
				exist = 1;
				now = parent->child[i];
				break;
			}
		}
		if (!exist)
		{
			now = (Tree)malloc(sizeof(Node));
			strcpy(now->name, name_);
			now->child_num = 0;
			now->files_num = 0;
			parent->child[parent->child_num] = now;
			parent->child_num++;
		}
		if (pos < (strlen(s) - 1))
		{
			solve(s + pos + 1, now);
		}
		else
			return;
	}
	else
	{
		File newone = (File)malloc(sizeof(File_));
		strcpy(newone->name, s);
		parent->files[parent->files_num] = newone;
		parent->files_num++;
		return;
	}
}
void PrintTree(Tree now, int depth)
{
	for (int i = 0; i < depth; ++i)
		cout << "  ";
	cout << now->name << endl;
	depth++;
	sort(now->child, now->child + now->child_num, cmp1);
	for (int i = 0; i < now->child_num; ++i)
		PrintTree(now->child[i], depth);
	sort(now->files, now->files + now->files_num, cmp2);
	for (int i = 0; i < now->files_num; ++i)
	{
		for (int i1 = 0; i1 < (depth); ++i1)
		{
			cout << "  ";
		}
		cout << now->files[i]->name << endl;
	}
}
int main()
{
	int n;
	cin >> n;
	Tree root = (Tree)malloc(sizeof(Node));
	strcpy(root->name, "root");
	while (n--)
	{
		char str[270];
		cin >> str;
		solve(str, root);
	}
	PrintTree(root, 0);
	return 0;
}

提交结果3: