zoukankan      html  css  js  c++  java
  • 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 } 
  • 相关阅读:
    转自:stuff字符串拼接方法
    mssql sqlserver in 关键字在值为null的应用举例
    mssql sqlserver 将字段null(空值)值替换为指定值的三种方法分享
    mssql sqlserver 使用sql脚本输出交替不同的背景色的html信息的方法分享
    MSSQL coalesce系统函数简介
    MSSQL sql server order by 1,2 的具体含义
    JavaScript正则表达式模式匹配(1)——基本字符匹配
    正则表达式的语法和使用说明
    log4j日志的基本使用方法(1)——概述、配置文件
    写在博客园开博第一篇博文
  • 原文地址:https://www.cnblogs.com/liuzhuan-xingyun/p/13048858.html
Copyright © 2011-2022 走看看