WEEK9 周记 作业——模拟题_股股东的目录管理器
一、题意
1.简述
2.输入格式
3.输出格式
4.时空限制
5.样例
Input
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
Output
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
二、算法
主要思路
按照题意将每一个功能都实现即可。
但是需要注意的是,有些地方是可以通过改进逻辑来降低时间复杂度的。
比如,对于RM操作,为了后面UNDO操作能够更好地执行,实际上我们在删除的时候只删除边即可,也就是只在其父节点的孩子map中将该节点删除即可,dir数组中则不必删除。
再比如tree操作的懒更新:记一下是不是最新了,当调用tree函数发现当前的就是最新的,就直接返回,复杂度为
O
(
1
)
O(1)
O(1),如果不是最新的再去前序遍历再去更新。
有一个比较好的降低时间复杂度的点:
在TREE函数中,如果是孩子节点的数目大于10个,那么就需要输出前5个和后5个。此时本来打算统一用一个前序遍历函数将所有的都给遍历一遍。这样代码量少。
但是还有更省时间的方法。前五个用从头前序遍历,遍历到第五个就直接结束。后五个也前序遍历,但不从头开始,而是从最后一棵子树开始进行前序遍历。这种并没有打破前序遍历的顺序,而是将前序遍历的后面才进行的操作先进行。而如果最后一刻子树也有子树,则也是从最后一棵子树进行前序遍历。先完成自己所有子树的前序遍历的节点先记录下来。当记录了5个之后直接结束函数。当然,记录下来的是从最后一个数到倒数第5个数,所以输出的时候需要从从数组的后面往前输出。
这样的操作使得不需要对进行TREE操作的数的所有节点都遍历一遍,能够降低时间复杂度。
三、代码
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
const string order[7]= {"MKDIR","RM","CD","SZ","LS","TREE","UNDO"};
struct Directory{
string name; //目录名
map<string,int> children; //孩子
int fa,sz; //父节点和当前子树的节点个数
vector<string> pre,bck; //存前序遍历的
bool tag; //是否被更新过
Directory(string name="root",int fa=-1):name(name),fa(fa){
sz = 1;
tag = 0; //得是0
}
} dir[5010]; //0是根目录
//删除某个目录的时候只需要删除边就行,
//不需要将这个目录对应的数组元素删除,不用这个数组元素就是了(以后再也找不到了)
int idx; //目录数组的索引
int now; //当前在哪一个目录
vector<pair<string,int> > v; //<cmd,route>
void update(int cur,int num){
while(cur!=-1){
dir[cur].sz += num;
dir[cur].tag = 0;
cur = dir[cur].fa;
}
}
void MKDIR(string& r){
if(dir[now].children.count(r)==1){ //注意map的这个函数的作用!!见收藏的博客
cout<<"ERR"<<endl;
return;
}
cout<<"OK"<<endl;
dir[now].children.insert(make_pair(r,++idx));
//注意map的这个函数的作用!!见收藏的博客
update(now,1);
Directory d(r,now);
dir[idx] = d;
v.push_back(pair<string,int>("MKDIR",idx));
}
void RM(string& r){
if(dir[now].children.count(r)==0){
cout<<"ERR"<<endl;
return;
}
cout<<"OK"<<endl;
int pos = dir[now].children[r];
dir[now].children.erase(r); //查一查map的删除
update(now,(-1)*dir[pos].sz);
v.push_back(pair<string,int>("RM",pos));
}
void CD(string& r){
if(r==".."){ //进入上层目录
if(now==0){
cout<<"ERR"<<endl;
return;
}
cout<<"OK"<<endl;
v.push_back(pair<string,int>("CD",now));
//不能放最前面,因为不会撤销不成功执行的操作
//不能放在now修改之后
now = dir[now].fa;
}else{//进入某一个子目录
if(dir[now].children.count(r)==0){
cout<<"ERR"<<endl;
return;
}
cout<<"OK"<<endl;
v.push_back(pair<string,int>("CD",now));
//不能放最前面,因为不会撤销不成功执行的操作
now = dir[now].children[r];
}
}
void SZ(){ //输出当前目录的大小
cout<<dir[now].sz<<endl;
}
void LS(){ //输出当前目录的直接目录名
int num = dir[now].children.size();
if(num==0){
cout<<"EMPTY"<<endl;
}else if(num>=1&&num<=10){
for(auto i=dir[now].children.begin();i!=dir[now].children.end();i++)
cout<<i->first<<endl;
}else{
auto it = dir[now].children.begin();
for(int i=0;i<5;i++){//前五个
cout<<it->first<<endl;
it++;
}
cout<<"..."<<endl;
it = dir[now].children.end();
for(int i=0;i<5;i++) it--;
for(int i=0;i<5;i++){//后五个
cout<<it->first<<endl;
it++;
}
}
}
void getAll(int cur){
dir[now].pre.push_back(dir[cur].name);
auto it=dir[cur].children.begin();
for(;it!=dir[cur].children.end();it++){
getAll(it->second);
}
}
void getPreVector(int cur,int& preIndex){
if(preIndex<5){
dir[now].pre.push_back(dir[cur].name);
preIndex++;
}
else return;
auto it=dir[cur].children.begin();
for(;it!=dir[cur].children.end();it++){
getPreVector(it->second,preIndex);
if(preIndex>=5) return; //能降低时间复杂度
}
}
void getBckVector(int cur,int& bckIndex){ //这个函数很巧妙,能够降低时间复杂度
auto it=dir[cur].children.end();
while(it!=dir[cur].children.begin()){//判断前一个当前it是不是begin
it--;
getBckVector(it->second,bckIndex);
if(bckIndex>=5) return; //能降低时间复杂度
}
if(bckIndex<5){
dir[now].bck.push_back(dir[cur].name);
bckIndex++;
}
else return;
}
//void preOrder(int cur,int preIndex,int bckIndex){
// if(preIndex<5){
// dir[now].pre.push_back(dir[cur].name);
// preIndex++;
// }
// auto it=dir[cur].children.begin();
// for(;it!=dir[cur].children.end();it++){
// preOrder(it->second);
// }
// if(bckIndex<5){
// dir[now].bck.push_back(dir[cur].name);
// bckIndex++;
// }
//}
void pushdown(){ //只针对当前now所在的节点
dir[now].pre.clear();
dir[now].bck.clear(); //每次更新都得清空,很重要
int sz = dir[now].sz;
if(sz>1&&sz<=10){
getAll(now);
}else{
int preIndex=0;
int bckIndex=0;
getPreVector(now,preIndex);
getBckVector(now,bckIndex);
}
dir[now].tag = 1; ///别忘了!
}
void TREE(){
int sz = dir[now].sz;
if(dir[now].tag==0){
pushdown();
}
if(sz==1){
cout<<"EMPTY"<<endl;
return;
}
if(sz>1&&sz<=10){
for(int i=0;i<sz;i++)
cout<<dir[now].pre[i]<<endl; //查查这里的[]
}else{
for(int i=0;i<5;i++)
cout<<dir[now].pre[i]<<endl; //查查这里的[]
cout<<"..."<<endl;
for(int i=4;i>=0;i--)
cout<<dir[now].bck[i]<<endl; //查查这里的[]
}
}
void UNDO(){
if(v.size()==0){
cout<<"ERR"<<endl;
return;
}
else{
cout<<"OK"<<endl;
pair<string,int> pp = v[v.size()-1]; //注意这种写法
v.pop_back();
if(pp.first=="MKDIR"){
dir[now].children.erase(dir[pp.second].name);
update(now,-1);
}else if(pp.first=="RM"){
dir[now].children[dir[pp.second].name] = pp.second;
update(now,dir[pp.second].sz);
}else{
now = pp.second;
}
}
}
int getType(string s,string& r){ //这里的输入判断还是比较巧妙的
for(int i=0;i<7;i++){
if(s==order[i]){
if(i<3) cin>>r;
return i;
}
}
}
int main(){
int t;
ios::sync_with_stdio(false);
cin>>t;
for(int tt=0;tt<t;tt++){
if(tt!=0) cout<<endl;
idx = 0;
now = 0;
v.clear();
dir[now].bck.clear();
dir[now].pre.clear();
dir[now].children.clear();
dir[now].tag = 0;
dir[now].sz = 1;
int q;
cin>>q;
while(q--){
string cmd,r;
cin>>cmd;
int type = getType(cmd,r);
switch(type){
case 0:
MKDIR(r);
break;
case 1:
RM(r);
break;
case 2:
CD(r);
break;
case 3:
SZ();
break;
case 4:
LS();
break;
case 5:
TREE();
break;
case 6:
UNDO();
break;
}
}
}
return 0;
}