week9-咕咕东的目录管理器

Posted liuzhuan-xingyun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了week9-咕咕东的目录管理器相关的知识,希望对你有一定的参考价值。

问题描述
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零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” 执行结果如下,
UNDO 特殊 撤销操作 撤销最近一个 “成功执行” 的操作(即MKDIR或RM或CD)的影响,撤销成功输出 “OK” 失败或者没有操作用于撤销则输出 “ERR”

input:
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);
每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。
每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。
output:
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。

技术图片
样例输入
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
样例输出
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
sample in&out

解题思路:

对于这一个题,所涉及的操作比较多,所以首先我们应该确定一个解题框架。首先,一个目录可能有多个子目录,并且要保证按字典序进行排序,如果使用vector的话,尽管能够实现存储多个孩子,但是不能实现字典序有序, 还要进行一步排序。如果采用map,一个目录名映射到一个目录指针,我们可以通过目录名很快的找到该指针,时间为log级,并且map会有按第一维自动排序的功能,所以只要加入map,它就能保证其first有序。并且其中还有一个指针指向其父亲,和一个int型的变量size,来表示以这个目录为根的所有目录的个数。由于输入由一系列指令构成,所以我们也用一个结构体来存储指令,才读入一个指令之后,按其对应的字符串,将其转换为相应的int,如果为前三条指令,即mkdir,rm,cd,则指令后面还有一个参数,则要读入并将其存在一个string类型的变量中。考虑到操作中有undo操作,所以还要一个变量来存储刚刚涉及的目录节点。

在大致考虑完存储之后,则考虑操作大致框架。对于mkdir操作,则是在当前目录下创建一个新目录,所以我们需要一个指针now来指向当前目录,初始值为root。在创建成功之后,返回刚刚创建的目录的指针,如果指针不为空,则创建成功,将刚刚执行成功的这条指令加入到一个vector对象中, 该对象存储已经成功执行的命令。在rm操作,和mikdir操作的框架差不多。在cd操作中,进入一个新目录,在操作成功后,更改now指针的指向。对于后三个操作,sz,ls,tree,直接调用函数,不需要其他改变。后面具体说明这些函数。对于undo操作,则从vector中取出一条最近执行成功的指令,如果为mkdir,则将新建的目录删除,如果为rm,则将之前删除的目录加上,如果为cd,则返回上一级。

在大致框架考虑完之后,则考虑每一步的具体的操作,对于mkdir操作,则是现在当前目录下查找要添加的目录,没有再进行添加,并且向上维护每一个目录的size值。对于rm操作,也是首先查找,当找到之后再进行删除,连同其孩子一并删除,并且向上维护个目录的size值。对于cd操作,则我们先判断是否参数为"…",如果是,则进入父亲目录,否则进入子目录。对于sz操作,直接返回结构体中的size。对于ls操作,则要首先判断当前目录下有几个子目录,如果小于10个,则全部输出,否则输出前5个和后5个,中间用"…"隔开。为了配个undo,还有一个操作时将删除的目录添加上,和mkdir操作的思想差不多。在进行许多操作时,都有一个维护目录的size值操作,其实从当前目录向上维护,一直到根节点。

对于最后一个操作tree,如果我们执行tree操作都将整个文件目录遍历一遍,通过分析数据范围肯定会超时。在有些情况下是不需要重新遍历,比如两次tree操作时的目录结构相同,则我们直接输出上次调用tree的结果就可以。则我们可以通过一个bool类型的变量来标记文件目录在一次tree操作后是否更改过。如果没有更改过,则直接输出已经保存好的结果。如果更改过,则要对文件目录进行遍历,如果该文件下的目录个数小于10个,则直接进行遍历,将结果存在vector中。如果目录个数大于10个,则我们只需要前5个和后5个,所以需两个遍历函数来分别进行操作,第一次遍历得到前5个目录,第二次遍历得到后5个目录。

代码:

  1 #include<iostream>
  2 #include<map>
  3 #include<vector>
  4 using namespace std;
  5 struct dir{//目录结构体
  6     string name;//目录名
  7     map<string,dir*> children;//孩子
  8     dir *parent;//父亲
  9     int size;//以当前目录为根的目录的个数
 10     bool update;//是否更新过
 11     vector<string> tendes;//存储遍历时的孩子
 12     dir(string na,dir *p)
 13     {
 14         size=1;
 15         name=na;
 16         parent=p;
 17     }
 18 public:
 19     dir* getchild(string mu)
 20     {//得到目录名为mu的孩子
 21         map<string,dir*>::iterator it=children.find(mu);
 22         if(it==children.end())
 23             return NULL;
 24         else
 25             return it->second;
 26     }
 27     dir* mkdir(string mu)
 28     {//创建目录
 29         if(children.find(mu)!=children.end())
 30             return NULL;
 31         dir *te=new dir(mu,this);
 32         children[mu]=te;
 33         maintain(1);
 34         return te;
 35     }
 36     dir* rm(string mu)
 37     {//删除目录
 38         map<string,dir*>::iterator it=children.find(mu);
 39         if(it==children.end())
 40             return NULL;
 41         maintain(-1*it->second->size);
 42         children.erase(it);
 43         return it->second;
 44     }
 45     dir* cd(string mu)
 46     {//进入目录
 47         if(mu=="..")
 48             return this->parent;
 49         return getchild(mu);
 50     }
 51     bool addchild(dir *ch)
 52     {//将刚刚删除的子目录添加上
 53         if(children.find(ch->name)!=children.end())
 54             return false;
 55         children[ch->name]=ch;    
 56         maintain(+ch->size);
 57         return true;
 58     }
 59     void maintain(int s)
 60     {//维护整个文件目录的size
 61         update=true;
 62         size+=s;
 63         if(parent!=NULL)
 64             parent->maintain(s);
 65     }
 66     void sz()
 67     {//输出size
 68         cout<<size<<endl; 
 69     }
 70     void ls()
 71     {//得到该文件下的所有文件夹
 72         int ss=children.size();
 73         if(ss==0)
 74             cout<<"EMPTY"<<endl;
 75         else if(ss<=10)
 76         {
 77             map<string,dir*>::iterator it=children.begin();
 78             while(it!=children.end())
 79             {
 80                 cout<<it->first<<endl;
 81                 it++;
 82             }
 83         }
 84         else
 85         {
 86             map<string,dir*>::iterator it=children.begin();
 87             for(int i=0;i<5;i++,it++)
 88                 cout<<it->first<<endl;
 89             cout<<"..."<<endl;
 90             it=children.end();
 91             for(int i=0;i<5;i++) it--;
 92             for(int i=0;i<5;i++,it++)
 93                 cout<<it->first<<endl;
 94         }
 95     }
 96     void tree()
 97     {//得到整个树形结构
 98         if(size==1) cout<<"EMPTY"<<endl;
 99         else if(size<=10)
100         {
101             if(update)//未更新过
102             {
103                 tendes.clear();
104                 treeall(&tendes);
105                 update=false;
106             }
107             for(int i=0;i<size;i++)
108                 cout<<tendes[i]<<endl;
109         }
110         else
111         {
112             if(update)
113             {
114                 tendes.clear();
115                 treef(5,&tendes);
116                 treel(5,&tendes);
117                 update=false;
118             }
119             for(int i=0;i<5;i++)
120                 cout<<tendes[i]<<endl;
121             cout<<"..."<<endl;
122             for(int i=9;i>=5;i--)
123                 cout<<tendes[i]<<endl;
124         }
125     }
126 private:
127     void treeall(vector<string> *v)
128     {//遍历所有节点
129         v->push_back(name);
130         map<string,dir*>::iterator it=children.begin();
131         while(it!=children.end())
132         {
133             it->second->treeall(v);
134             it++;
135         }
136     }
137     void treef(int n,vector<string> *v)
138     {//遍历前5个节点
139         v->push_back(name);
140         if(--n==0) return;
141         int nn=children.size();
142         map<string,dir*>::iterator it=children.begin();
143         while(nn--)
144         {
145             int ss=it->second->size;//得到每一个孩子的目录的大小
146             if(ss>=n)
147             {//如果大于所需要的
148                 it->second->treef(n,v);//遍历完其孩子就可以返回
149                 return;
150             }
151             else
152             {//否则还有继续遍历
153                 it->second->treef(ss,v);
154                 n-=ss; 
155             }
156             it++;
157         }
158     }
159     void treel(int n,vector<string> *v)
160     {//遍历后五个节点
161         int nn=children.size();
162         map<string,dir*>::iterator it=children.end();
163         while(nn--)
164         {//和遍历前5个的思想类似
165             it--;
166             int ss=it->second->size;
167             if(ss>=n)
168             {
169                 it->second->treel(n,v);
170                 return;
171             }
172             else
173             {
174                 it->second->treel(ss,v);
175                 n-=ss;
176             }
177             
178         }
179         v->push_back(name);
180     }
181 };
182 char ming[30];
183 struct com{//命令结构体
184     string commands[7]={"MKDIR","RM","CD","SZ","LS","TREE","UNDO"};
185     int type;
186     string can;
187     dir* tdir;//刚刚涉及的目录节点 
188     com(string tem)
189     {
190         for(int i=0;i<7;i++)
191         {
192             if(commands[i]==tem){
193                 type=i;
194                 if(i<3){
195                     cin>>ming;
196                     can=ming;
197                 }
198                 return;
199             }
200         }
201     }
202     
203 };
204 void solve()
205 {
206     int Q;
207     cin>>Q;
208     dir *now=new dir("root",NULL); 
209     vector<com*> comlist;
210     for(int i=0;i<Q;i++)
211     {//进行各个操作
212         cin>>ming;
213         com *c=new com(ming);
214         if(c->type==0)
215         {
216             c->tdir=now->mkdir(c->can);
217             if(c->tdir==NULL)
218                 cout<<"ERR"<<endl;
219             else
220             {
221                 cout<<"OK"<<endl; 
222                 comlist.push_back(c);
223             }
224         }     
225         else if(c->type==1)
226         {
227             c->tdir=now->rm(c->can);
228             if(c->tdir==NULL)
229                 cout<<"ERR"<<endl;
230             else
231             {
232                 cout<<"OK"<<endl; 
233                 comlist.push_back(c);
234             }
235         }
236         else if(c->type==2)
237         {
238             dir *te=now->cd(c->can);
239             if(te==NULL)
240                 cout<<"ERR"<<endl;
241             else
242             {
243                 cout<<"OK"<<endl;
244                 c->tdir=now;
245                 now=te;
246                 comlist.push_back(c);    
247             }
248         }
249             
250         else if(c->type==3)
251             now->sz();
252         else if(c->type==4)
253             now->ls();
254         else if(c->type==5)
255             now->tree();
256         else if(c->type==6)
257         {
258             bool su=false;
259             while(!su&&!comlist.empty())
260             {
261                 c=comlist.back();
262                 comlist.pop_back();
263                 if(c->type==0)
264                 {
265                     dir *temp=now->rm(c->can);
266                     if(temp!=NULL) su=true;
267                 }
268                 else if(c->type==1)
269                     su=now->addchild(c->tdir);
270                 else if(c->type==2)
271                 {
272                     now=c->tdir;
273                     su=true;
274                 }
275             }
276             if(su) cout<<"OK"<<endl;
277             else cout<<"ERR"<<endl;
278         }  
279     }
280 }
281 int main()
282 {
283     int T;
284     cin>>T;
285     for(int i=0;i<T;i++)
286     {
287         solve();
288     }    
289 } 

 

以上是关于week9-咕咕东的目录管理器的主要内容,如果未能解决你的问题,请参考以下文章

csp-M3-C-咕咕东学英语

csp-M3-C-咕咕东学英语

如何使用Android片段管理器传递变量[重复]

massCode 一款优秀的开源代码片段管理器

Android:使用支持片段管理器时复制片段

如何在片段着色器中平铺部分纹理