week9 A - 咕咕东的目录管理器

一、题目描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
样例输入:

1
22
MKDIR dira
CD dirb
CD dira
MKDIR a
MKDIR b
MKDIR c
CD ..
MKDIR dirb
CD dirb
MKDIR x
CD ..
MKDIR dirc
CD dirc
MKDIR y
CD ..
SZ
LS
TREE
RM dira
TREE
UNDO
TREE

样例输出:

OK
ERR
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
9
dira
dirb
dirc
root
dira
a
b
c
dirb
x
dirc
y
OK
root
dirb
x
dirc
y
OK
root
dira
a
b
c
dirb
x
dirc
y

二、思路概述

  • 有两个结构体,一个用来存放指令command,一个用来存放目录的信息logue.
  • 所有的操作指令都放在一个vector数组里,并记录是哪个目录到哪个目录进行的操作,便于回溯。
  • MKDIR新建目录:先检查当前目录下是否已经存在要新建的目录,若没有,则新建一个,并更新这个目录所属的目录的size。
  • RM删除目录:基本上和新建目录是相反的操作。
  • CD进入目录:若是"…",则要进入上一级目录,否则查找目录是否存在于当前目录下,并进入那个目录。
  • SZ输出当前目录大小:只要直接输出即可
  • LS输出当前直接子目录:看直接子目录的孩子数量,若<=10,则直接输出即可,若>10,则是另一种情况。
  • TREE输出子树:为了节约时间,对每个结点实现懒更新,对每个结点都有一个pre数组和bck数组用来存储前序遍历的前几个点,和后序的几个点。当节点不需要更新时,速度就会非常快。
  • UNDO撤回,查看存储指令的数组里末尾是什么指令,针对那个指令,进行相应的撤回。

四、完整代码

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

const string cdstring[] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
struct command {
	int typeshu=-1;//操作类型
	string type;//操作类型和type对应
	string czshu;//不同的操作有不同的操作参数

	command(){}

	void init(string st)
	{
		type = st;
		for (int i = 0; i < 7; i++) {
			if (st == cdstring[i]) {
				typeshu = i;
				if (i < 3)cin >> czshu;//只有三种操作有操作参数
					break;
			}
		}
	}
};

struct logue {//目录的树结构
	string name;//当前目录的名字
	map<string, int> child;//当前目录的子目录,string是子目录名字,int是孩子在lg数组的下标
	int parent;//目录的上一级,所在的数组下标
	int mlsize;//目录的大小
	bool tag;//标识
	vector<string>pre, bck;

	logue(){}
	void init(string na, int par) {//新建一个空的目录
		tag = 0;
		parent = par;
		mlsize = 1;
		name = na;
		pre.clear();
		bck.clear();
		child.clear();
	}
};

logue lg[100100] ;//lg数组存储目录
int cnt, now;//cnt为lg数组计数
command cd;//cd表示操作,有的函数会更新
vector<pair<string, pair<int, int> > >op;//op数组存操作,string为操作名,第一个int为当前目录的数组lg下标,第二个int为操作目录的数组下标

void initialize() {
	cnt = 0;
	now = 0;
	op.clear();
	lg[0].init("root", -1);//建立根目录
}

void update_size(int x, int num) {//更新目录size
	while (x != -1) {
		lg[x].tag = 0;
		lg[x].mlsize = lg[x].mlsize + num;//x的子目录总数+1
		x = lg[x].parent;//向上更新
		//cout << lg[0].parent;
		//cout << x << endl;
		//cout << "while" << endl;
	}
}

void make_newloge(string s, int par) {
	cnt++;
	lg[cnt].init(s, par);//增加一个目录,就把它放进数组lg里面
	lg[par].child[s] = cnt;	//在此目录的上一级目录里增加这个孩子
}

//创建新目录
void mkdir() {
	//cout << "mkdir" << endl;
	if (lg[now].child.count(cd.czshu)) {//如果已存在,则输出error
		cout << "ERR" << endl;
	}
	else {
		//cout << "yes" << endl;
        make_newloge(cd.czshu, now);//now为当前目录在lg数组的下标
	    op.push_back(make_pair("MKDIR", make_pair(now, cnt)));
	    update_size(now, 1);//更新数目
	    cout << "OK" << endl;
	}
}

//删除给定的目录
void rm() {
	if (!lg[now].child.count(cd.czshu)) {//没有找到要删除的目录
		cout << "ERR" << endl;
	}
	else {//只是删除关系,并未删除元素
		int bj = lg[now].child[cd.czshu];//目录在lg数组的下标
		update_size(now, -lg[bj].mlsize);//更新size
		op.push_back(make_pair("RM", make_pair(now, bj)));//增加操作
		lg[now].child.erase(lg[bj].name);//在它的父母的孩子里删除
		cout << "OK" << endl;
	}
}

//切换,返回
void cds() {
	//cout << "cd" << endl;
	if (cd.czshu == "..") {//特别情况,返回上一级目录
		if (lg[now].parent == -1) {
			cout << "ERR" << endl;
		}
		else {
			op.push_back(make_pair("CD", make_pair(now, lg[now].parent)));//由now到lg[now].parent执行CD操作
			now = lg[now].parent;
			cout << "OK" << endl;
		}
	}
	//进入cd.czshu这个目录
	else if(!lg[now].child.count(cd.czshu)){
		cout << "ERR" << endl;
	}
	else if(lg[now].child.count(cd.czshu) ){//进入子目录s,即cd.czshu
		int cn = lg[now].child[cd.czshu];
		op.push_back(make_pair("CD", make_pair(now, cn)));
		now = cn;
		cout << "OK" << endl;
	}
}

//输出大小
void  sz() {
	cout << lg[now].mlsize << endl;
}

//撤销上一步操作
void undo() {
	if (op.size() == 0) {
		cout << "ERR" << endl;
		return;
	}
	string s = op[op.size() - 1].first;//操作命令
	int f = op[op.size() - 1].second.first;//now原目录
	int se= op[op.size() - 1].second.second;//操作后的目录
	
	op.pop_back();
	cout << "OK" << endl;
	int temp = now;
	if (s == "MKDIR") {
		//cd.type = "RM";
		now = f;
		cd.czshu = lg[se].name;
		int u = lg[now].child[cd.czshu];
		update_size(now, -lg[u].mlsize);//更新size
		lg[now].child.erase(lg[u].name);//在now目录里删除这个孩子
		now = temp;
	}
	else if (s == "RM") {
		now = f;
		update_size(now, lg[se].mlsize);
		lg[now].child[lg[se].name] =se;//设定now的孩子有u
		now = temp;
	}//CD的撤销
	else if(s =="CD")now = f;

}

//询问当前目录的直接子目录
void ls() {
	if (lg[now].child.size() == 0) {//没有子目录
		cout << "EMPTY" << endl;
	}
	else if (lg[now].child.size() >= 1 && lg[now].child.size() <= 10) {
		auto u = lg[now].child.begin();
		while (u != lg[now].child.end()) {
			cout << u->first <<endl;
			u++;
		}
	}
	else if(lg[now].child.size()>10){
		auto u= lg[now].child.begin();
		for (int i = 0; i < 5; i++) {
			cout << u->first << endl;
			u++;
		}
		cout << "..." << endl;
		auto uu = lg[now].child.end();
		for (int i = 0; i < 5; i++)uu--;
		while (uu != lg[now].child.end()) {
			cout << uu->first<<endl;
			uu++;
		}
	}
}

//注入灵魂
void pushdown(int x);


void pretrack(int x) {

	lg[x].pre.push_back(lg[x].name);
	if (lg[x].mlsize == 1)return;//x没有一个子目录,是空的
	if (lg[x].mlsize <= 10) {
		for (auto i : lg[x].child) {
			if (!lg[i.second].tag)pushdown(i.second);//更新x的孩子的pre数组和back数组
			lg[x].pre.insert(lg[x].pre.end(),lg[i.second].pre.begin(), lg[i.second].pre.end());//一个目录的孩子的pre数组按照顺序插入x的pre数组里面
		}
		return;
	}

	//lg[x].mlsize > 10
	int pos = 1;
	for (auto i : lg[x].child) {
		if (!lg[i.second].tag)pushdown(i.second);
		for (auto j : lg[i.second].pre) {
			lg[x].pre.push_back(j);//把x的每个孩子的pre数组里的元素插入到lg里面,只插前5个
			pos++;
			if (pos >= 5)break;
		}
		if (pos >= 5)break;
	}
}

void bcktrack(int x) {
	auto it = lg[x].child.end();
	it--;
	int pos = 0;
	for (;; it--) {
		int se = it->second;//x的孩子从后开始计算
		if (!lg[se].tag)pushdown(se);//更新se
		for (int i = lg[se].bck.size() - 1; i >= 0; i--) {
			lg[x].bck.push_back(lg[se].bck[i]);//x的孩子的最末位bak插入x的bck
			pos++;
			if (pos >= 5) {
				reverse(lg[x].bck.begin(), lg[x].bck.end());
				break;
			}
		}
		if (pos >= 5)break;
		if (it == lg[x].child.begin())break;
	}
}

void pushdown(int x) {
	//构造pre和back
	lg[x].pre.clear();
	lg[x].bck.clear();
	pretrack(x);
	if (lg[x].mlsize > 10)bcktrack(x);
	else lg[x].bck = lg[x].pre;
	
	lg[x].tag = 1;//更新过了x
}

void tree() {
	if (!lg[now].tag)pushdown(now);	//now未被更新过,构造pre和back,降低时间,重复查询

	int m = lg[now].mlsize;
	if (m == 1)cout << "EMPTY" << endl;//没有子目录
	else if (m > 1 && m <= 10) {
		for (int i = 0; i < lg[now].pre.size(); i++)//直接输出前序序列
			cout << lg[now].pre[i] << endl;
	}
	else {//m>10
		for (int i = 0; i < 5; i++)
			cout << lg[now].pre[i] << endl;//前序的前五个
		cout << "..." << endl;
		for (int i = 5; i >= 1; i--)//后序的倒数五个
			cout << lg[now].bck[lg[now].bck.size() - i] << endl;
		 
	}

}


//char temp[20];
void firsolve()
{
	//测试数据的命令总数 Q
	int q; cin>>q;
	initialize();
	//cout << lg[0].parent << endl;
	while (q--) {
		string s;
		cin >> s;
		cd.init(s);//cout <<"当前目录"<< lg[now].name<< endl;
		switch (cd.typeshu) {		
		case 0:mkdir(); break;
		case 1:rm(); break;
		case 2:cds(); break;
		case 3:sz(); break;
		case 4:ls(); break;
		case 5:tree(); break;
		case 6:undo(); break;
		}
	}
}
int main()
{
	int t;//组数 T (T <= 20);
	cin >> t;
	while (t--) firsolve(); cout << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值