思路
课前试着做了做这道题,没做出来2333,先来总结一下课前没有想到的地方吧:
- children 的存储方式:我一开始选用的是 vector ,插入时先二分查找位置然后执行插入,查找元素时二分查找,时间复杂度分别为 O ( n ) , O ( l o g n ) O(n),O(logn) O(n),O(logn),不是一个很好的选择
- 前几个操作还比较好实现,但是对于 tree 操作的取最后 5 个元素,当时只是想到,把整棵树先序遍历一遍,边遍历边存入数组,然后输出最后 5 个元素,但是如果后续有新操作导致树形结构改变的话,那么这个数组还得再重新更新一遍,这样肯定行不通,然后就放弃了…听课23333
这节课收获蛮大的,完美地解决了我课前的疑惑,也在一定程度上拓宽了我的思路,非常感谢学长的耐心讲解!
- map 存储 children结点,提高了插入和查找效率,插入降为 O ( l o g n ) O(logn) O(logn),查找降为 O ( 1 ) O(1) O(1)
- 链式前向星式存储树形结构,结点存储在内存池,提高了运行效率,同时创建一个数组存储有效命令,便于处理 undo 操作
- 将 Lazy-tag 应用在 tree 操作中,同时为每个结点存储其前向与后向结点,在第一次遍历之后,只有在当前结点又被更新过时,再重新遍历,否则直接输出结果,从而减少了重复遍历的操作。
代码实现
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const string cmd_type[]={"MKDIR","RM","CD","SZ","LS","TREE","UNDO"};
const int maxn=100010;
int T,Q,n,tot,now;
struct Command{
string name,arg;
int type;
void init(string str){
name=str;
for(int i=0;i<7;i++){
if(str==cmd_type[i]){
type=i;
if(i<3)
cin>>arg;
break;
}
}
}
};
struct Directory{
string name;
int fa,sz;
bool tag;
map<string,int> mp;
vector<string> pre,back;
void init(string s,int p){
name=s;
fa=p;
sz=1;
tag=0;
pre.clear();
back.clear();
mp.clear();
}
}node[maxn];
struct ValidCommand{
string name;
int u,v;
ValidCommand(string nn,int uu,int vv):name(nn),u(uu),v(vv){}
};
Command cmd;
vector<ValidCommand> v;
void init(){
tot=0;
now=0;
v.clear();
node[0].init("root",-1);
}
void insert(string s,int p){
node[++tot].init(s,p);
node[p].mp[s]=tot;
}
void update(int id,int num){
while(id!=-1){
node[id].sz+=num;
node[id].tag=0;
id=node[id].fa;
}
}
void mkdir(){
map<string,int>::iterator it=node[now].mp.find(cmd.arg);
if(it!=node[now].mp.end()){
cout<<"ERR"<<endl;
}
else{
cout<<"OK"<<endl;
insert(cmd.arg,now);
update(now,1);
v.push_back(ValidCommand("MKDIR",now,tot));
}
}
void rm(){
map<string,int>::iterator it=node[now].mp.find(cmd.arg);
if(it==node[now].mp.end()){
cout<<"ERR"<<endl;
}
else{
int u=it->second;
node[now].mp.erase(it);
update(now,-1*node[u].sz);
v.push_back(ValidCommand("RM",now,u));
cout<<"OK"<<endl;
}
}
void cd(){
if(cmd.arg==".."){
if(node[now].fa==-1){
cout<<"ERR"<<endl;
return;
}
else{
v.push_back(ValidCommand("CD",now,node[now].fa));
now=node[now].fa;
cout<<"OK"<<endl;
return;
}
}
else{
map<string,int>::iterator it=node[now].mp.find(cmd.arg);
if(it==node[now].mp.end()){
cout<<"ERR"<<endl;
return;
}
else{
v.push_back(ValidCommand("CD",now,node[now].mp[cmd.arg]));
now=node[now].mp[cmd.arg];
cout<<"OK"<<endl;
}
}
}
void sz(){
cout<<node[now].sz<<endl;
}
void ls(){
int Size=node[now].mp.size();
if(Size==0){
cout<<"EMPTY"<<endl;
return;
}
else if(Size<=10){
for(map<string,int>::iterator it=node[now].mp.begin();
it!=node[now].mp.end();it++){
cout<<it->first<<endl;
}
}
else{
map<string,int>::iterator it=node[now].mp.begin();
for(int i=0;i<5;i++){
cout<<it->first<<endl;
it++;
}
cout<<"..."<<endl;
it=node[now].mp.end();
for(int i=0;i<5;i++)
it--;
for(int i=0;i<5;i++){
cout<<it->first<<endl;
it++;
}
}
}
void undo(){
if(v.size()==0){
cout<<"ERR"<<endl;
return;
}
ValidCommand enable=v[v.size()-1];
v.pop_back();
cout<<"OK"<<endl;
if(enable.name=="MKDIR"){
int u=enable.v;
update(now,(-1)*node[u].sz);
node[now].mp.erase(node[u].name);
}
else if(enable.name=="RM"){
node[now].mp[node[enable.v].name]=enable.v;
update(now,node[enable.v].sz);
}
else{
now=enable.u;
}
}
void pushdown(int id);
void pretrack(int id){
node[id].pre.push_back(node[id].name);
if(node[id].sz==1)
return;
if(node[id].sz<=10){
for(map<string,int>::iterator it=node[id].mp.begin();
it!=node[id].mp.end();it++){
if(!node[it->second].tag)
pushdown(it->second);
node[id].pre.insert(node[id].pre.end(),node[it->second].pre.begin(),
node[it->second].pre.end());
}
return;
}
int cnt=1;
for(map<string,int>::iterator it=node[id].mp.begin();
it!=node[id].mp.end();it++){
if(!node[it->second].tag)
pushdown(it->second);
for(int j=0;j<node[it->second].pre.size();j++){
node[id].pre.push_back(node[it->second].pre[j]);
if(++cnt>=5)
break;
}
if(cnt>=5)
break;
}
}
void backtrack(int id){
int cnt=0;
map<string,int>::iterator it=node[id].mp.end();
--it;
while(1){
if(!node[it->second].tag)
pushdown(it->second);
for(int i=node[it->second].back.size()-1;i>=0;i--){
node[id].back.push_back(node[it->second].back[i]);
if(++cnt>=5){
reverse(node[id].back.begin(),node[id].back.end());
break;
}
}
if(cnt>=5||it==node[id].mp.begin())
break;
it--;
}
}
void pushdown(int id){
node[id].pre.clear();
node[id].back.clear();
pretrack(id);
if(node[id].sz>10)
backtrack(id);
else
node[id].back=node[id].pre;
node[id].tag=1;
}
void tree(){
if(!node[now].tag)
pushdown(now);
if(node[now].sz==1)
cout<<"EMPTY"<<endl;
else if(node[now].sz>1&&node[now].sz<=10){
for(int i=0;i<node[now].pre.size();i++)
cout<<node[now].pre[i]<<endl;
}
else{
for(int i=0;i<5;i++)
cout<<node[now].pre[i]<<endl;
cout<<"..."<<endl;
for(int i=5;i>=1;i--)
cout<<node[now].back[node[now].back.size()-i]<<endl;
}
}
int main()
{
// freopen("test.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>T;
while(T--){
cin>>Q;
init();
for(int i=1;i<=Q;i++){
string s;
cin>>s;
cmd.init(s);
switch(cmd.type){
case 0:{
mkdir();
break;
}
case 1:{
rm();
break;
}
case 2:{
cd();
break;
}
case 3:{
sz();
break;
}
case 4:{
ls();
break;
}
case 5:{
tree();
break;
}
case 6:{
undo();
break;
}
}
}
cout<<endl;
}
}