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
解题思路:
对于这一个题,所涉及的操作比较多,所以首先我们应该确定一个解题框架。首先,一个目录可能有多个子目录,并且要保证按字典序进行排序,如果使用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-咕咕东的目录管理器的主要内容,如果未能解决你的问题,请参考以下文章