【Week9作业 A】咕咕东的目录管理器【模拟】

题意:

咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!

初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。
目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。

现在咕咕东可以在命令行下执行以下表格中描述的命令:

命令 类型 实现 说明
MKDIR s 操作 在当前目录下创建一个子目录 s,s 是一个字符串 创建成功输出 “OK”;若当前目录下已有该子目录则输出 “ERR”
RM s 操作 在当前目录下删除子目录 s,s 是一个字符串 删除成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”
CD s 操作 进入一个子目录 s,s 是一个字符串(执行后,当前目录可能会改变) 进入成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”
特殊地,若 s 等于 “…” 则表示返回上级目录,同理,返回成功输出 “OK”,返回失败(当前目录已是根目录没有上级目录)则输出 “ERR”
SZ 询问 输出当前目录的大小 也即输出 1+当前目录的子目录数
LS 询问 输出多行表示当前目录的 “直接子目录” 名 若没有子目录,则输出 “EMPTY”;若子目录数属于 [1,10] 则全部输出;若子目录数大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。
TREE 询问 输出多行表示以当前目录为根的子树的前序遍历结果 若没有后代目录,则输出 “EMPTY”;若后代目录数+1(当前目录)属于 [1,10] 则全部输出;若后代目录数+1(当前目录)大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。若目录结构如上图,当前目录为 “root” 执行结果如下root dira a b c dirb x dirc y(之间有回车)。
UNDO 特殊 撤销操作 撤销最近一个 “成功执行” 的操作(即MKDIR或RM或CD)的影响,撤销成功输出 “OK” 失败或者没有操作用于撤销则输出 “ERR”。


思路:

基本思路都按照课件的思路来写的,所以就写一写自己思考的东西。
MKDIR:如果不存在节点,要在node[now].mp里添加儿子关系,然后从now向根节点更新子树节点数目sz。接下来要在node[index]里创建新节点,节点所有信息都进行初始化,然后将操作记录在vector v中,以便undo时使用。
RM:只用删除边关系,不用删除节点,以便于undo时恢复边和节点。
UNDO:首先是结构point:string name,int now,int next。name为操作名称,now为当前目录,next为要添加/删除/移到的目录位置。用vector v来记录操作序列。当undo时就取出v尾部的节点,根据节点信息来进行undo操作。
TREE:需要用到遍历子树pushdown操作。而pushdown里要有求子树所有节点alltrack(节点个数<=10),求前五个节点pretrack和后五个节点bcktrack。alltrack只需要前序遍历子树,将节点放在vector pre中即可;pretrack用num来记录还要选几个节点,递归遍历时若num为0则return;bcktrack同样用num来记录,不过遍历是先进入到最右儿子直到到达叶子,然后再取该节点。


总结:

一道非常复杂的模拟题,如果没有上课时所讲的思路,估计几天也a不了,因为会各种超时。以后做题时要先分析复杂度,再根据复杂度来选择数据结构和算法,以免写完代码之后才发现T要进行重构。
同时还发现了自己迭代器的使用不够熟练,begin~end是前闭后开,反向遍历时第一个数是it=end();it–;写的时候写错了,RE调试了好久。


代码:

#include <iostream>
#include <string>
#include <map>
#include <vector>
using namespace std;

struct Directory
{
	string name;	//名称
	map<string,int>mp;	//后代的名称和编号
	int fa,sz;	//父亲节点,子树的规模
	vector<string> pre,bck; //存前序遍历的前几个,后几个
	bool tag;	//是否被更新过
};
Directory node[200000];
int now,index;	//当前节点与最新节点
struct point	//用于undo储存信息
{
	string name;	//操作名称
	int now;	//当前目录
	int next;	//要添加/删除/移动的目录位置
};
vector<point> v;
void update(int id,int num)	//将变化数据向上传导
{
	while(id!=-1)
	{
		node[id].tag=0;
		node[id].sz+=num;
		id=node[id].fa;
	}
}
void MKDIR(string &s)	//在当前目录下创建一个子目录s
{
	if(node[now].mp.count(s)==1)	//存在节点
		cout<<"ERR"<<endl;
	else
	{
		node[now].mp.insert(pair<string,int>(s,index));
		update(now,1);
		//在node[index]创建新节点
		node[index].name=s;
		node[index].fa=now;
		node[index].sz=1;
		node[index].tag=0;
		node[index].mp.clear();
		node[index].pre.clear();
		node[index].bck.clear();
		//将操作放入undo的vector中
		point p;
		p.name="MKDIR",p.now=now,p.next=index;
		v.push_back(p); 
		index++;
		cout<<"OK"<<endl;
	}
}
void RM(string &s)	//在当前目录下删除子目录s
{
	if(node[now].mp.count(s)==1)	//存在节点
	{
		int indexOfS=node[now].mp[s];
		node[now].mp.erase(s);
		update(now,(-1)*node[indexOfS].sz);
		//将操作放入undo的vector中
		point p;
		p.name="RM",p.now=now,p.next=indexOfS;
		v.push_back(p); 
		cout<<"OK"<<endl;
	}
	else	cout<<"ERR"<<endl;
}
void CD(string &s)	//进入子目录s
{
	if(s=="..")	//返回上级目录
	{
		if(node[now].fa==-1)	//根目录
			cout<<"ERR"<<endl;
		else
		{
			//将操作放到undo的vector中
			point p;
			p.name="CD",p.now=now,p.next=node[now].fa;
			v.push_back(p);
			now=node[now].fa;
			cout<<"OK"<<endl;
		}
	}
	else //进入子目录s
	{
		if(node[now].mp.count(s)==1)	//存在s
		{
			//将操作放到undo的vector中
			point p;
			p.name="CD",p.now=now,p.next=node[now].mp[s];
			v.push_back(p);
			now=node[now].mp[s];
			cout<<"OK"<<endl;
		}
		else cout<<"ERR"<<endl;
	}
}
void SZ()	//输出当前目录的大小
{
	cout<<node[now].sz<<endl;
}
void LS()	//输出当前目录的直接子目录名
{
	if(node[now].mp.size()==0)	//没有子目录
		cout<<"EMPTY"<<endl;
	else if(node[now].mp.size()>=1&&node[now].mp.size()<=10)	//全部输出
	{
		for(map<string,int>::iterator it=node[now].mp.begin(); it!=node[now].mp.end(); it++)
			cout<<it->first<<endl;
	}
	else //输出前五个和后五个
	{
		//前五个
		map<string,int>::iterator it=node[now].mp.begin();
		for(int i=1; i<=5; i++)
		{
			cout<<it->first<<endl;
			it++;
		}
		cout<<"..."<<endl;
		//后五个
		it=node[now].mp.end();
		for(int i=1; i<=5; i++)
			it--;
		for(int i=1; i<=5; i++)
		{
			cout<<it->first<<endl;
			it++;
		}
	}
}
void alltrack(int id,vector<string> &pre)	//遍历id子树,求出全部(小于10个)
{
	pre.push_back(node[id].name);
	for(map<string,int>::iterator it=node[id].mp.begin(); it!=node[id].mp.end(); it++)
		alltrack(it->second,pre);
} 
void pretrack(int id,vector<string> &pre,int &num)	//遍历id子树,求出前五个 
{
	pre.push_back(node[id].name);
	num--;
	if(num==0)	return;
	for(map<string,int>::iterator it=node[id].mp.begin(); it!=node[id].mp.end(); it++)
	{
		pretrack(it->second,pre,num);
		if(num==0)	break;
	}
}
void bcktrack(int id,vector<string> &bck,int &num)	//遍历id子树,求出后五个 
{
	map<string,int>::iterator it=node[id].mp.end();	
	int n=node[id].mp.size();
	for(int i=0;i<n;i++)
	{
		it--;
		bcktrack(it->second,bck,num);
		if(num==0)	return;
	}
	bck.push_back(node[id].name);
	num--;
}
void pushdown(int id)	//遍历id子树 
{
	node[id].pre.clear();
	node[id].bck.clear();
	if(node[id].sz<=10)
		alltrack(id,node[id].pre);
	else
	{
		int num=5;
		pretrack(id,node[id].pre,num);
		num=5;
		bcktrack(id,node[id].bck,num);
	}
	node[id].tag=1;
}
void TREE()	//输出以当前目录为根的子树的前序遍历结果
{
	if(!node[now].tag)	//需要遍历就去遍历 
		pushdown(now);
	if(node[now].sz==1)	//子目录为空
		cout<<"EMPTY"<<endl;
	else if(node[now].sz>1&&node[now].sz<=10) 
	{
		for(int i=0;i<node[now].pre.size();i++)
			cout<<node[now].pre[i]<<endl;
	}
	else
	{
		for(int i=0;i<5;i++)
			cout<<node[now].pre[i]<<endl;
		cout<<"..."<<endl;
		for(int i=4;i>=0;i--)
			cout<<node[now].bck[i]<<endl;
	}
}
void UNDO()	//撤销最近一个成功执行的MKDIR或RM或CD
{
	if(v.size()==0)	//没有操作
		cout<<"ERR"<<endl;
	else
	{
		point p=v[v.size()-1];
		v.pop_back();
		cout<<"OK"<<endl;
		if(p.name=="MKDIR")	//要删除
		{
			update(p.now,(-1)*node[p.next].sz);
			node[p.now].mp.erase(node[p.next].name);
		}
		else if(p.name=="RM")	//要添加
		{
			update(p.now,node[p.next].sz);
			node[p.now].mp[node[p.next].name]=p.next;
		}
		else	//要返回
			now=p.now;
	}
}
int main()
{
	ios::sync_with_stdio(false);
	int t;
	cin>>t;
	for(int t1=0; t1<t; t1++)
	{
		if(t1!=0)	//测试数据之间输出空行 
			cout<<endl;	
		int q;
		cin>>q;
		//初始化
		//根节点为node[0],其父亲为-1
		now=0,index=1;
		node[0].name="root",node[0].fa=-1,node[0].sz=1,node[0].tag=0;
		node[0].pre.clear(),node[0].bck.clear(),node[0].mp.clear();
		v.clear(); 
		for(int q1=0; q1<q; q1++)
		{
			string op;
			cin>>op;
			if(op=="MKDIR")
			{
				string s;
				cin>>s;
				MKDIR(s);
			}
			else if(op=="RM")
			{
				string s;
				cin>>s;
				RM(s);
			}
			else if(op=="CD")
			{
				string s;
				cin>>s;
				CD(s);
			}
			else if(op=="SZ")
				SZ();
			else if(op=="LS")
				LS();
			else if(op=="TREE")
				TREE();
			else if(op=="UNDO")
				UNDO();
		}
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值