ZIPアーカイブファイルには、すべての圧縮ファイルとディレクトリの相対パスと名前が保持されます。WinZIPなどのGUIソフトウェアでZIPアーカイブファイルを開くと、この情報からディレクトリのツリー構造を再構築できます。ディレクトリのツリー構造を再構築するプログラムを作成してください。
入力フォーマット:
入力は最初に正の整数N(≤104)を与えます。これはZIPアーカイブ内のファイルとディレクトリの数を表します。次のN行では、各行に次の形式のファイルまたはディレクトリの相対パスと名前があります(各行は260文字を超えません)。
- パスと名前の文字には英語の文字のみが含まれます(大文字と小文字が区別されます)。
- 記号「\」はパス区切り文字としてのみ表示されます。
- ディレクトリは記号「\」で終わります。
- 重複する入力項目はありません。
- 全体の入力サイズは2MBを超えません。
出力フォーマット:
すべてのパスがルートディレクトリからの相対パスであると想定します。ルートディレクトリから開始して、出力時に、各ディレクトリは最初に独自の名前を出力し、次にすべてのサブディレクトリを字句順に出力し、次にすべてのファイルを字句順に出力します。なお、出力する場合は、ディレクトリの相対的な関係に応じて、インデントにスペースを使用する必要があります。ディレクトリまたはファイルの各レベルは、前のレベルより2スペース多くインデントされます。
入力サンプル:
サンプル出力:
実際、この問題はポリツリーを構築する必要はありません。教師Heが教えた左子と右兄弟の表記法を使用するだけです。データ構造はバイナリリンクリストです。最後に、バイナリツリーのプレオーダートラバーサルでこの問題を解決できます。もちろん、ツリーの構築方法は難しい点です。最初にここにコードを投稿し、この質問の具体的な分析のアイデアといくつかの落とし穴を数日で書きます。
-------------------------------------------------- -------------------------------------------------- ---------------------------
達成のコアアイデア:
注意点をいくつか挙げておきますが、ディレクトリの挿入優先度はファイルよりも高くする必要があります。実際、ディレクトリは非リーフノードと同等であり、ファイルはリーフノードと同等です。したがって、ファイルの字句順序がいくら小さくても、常にディレクトリに移動します。戻ってくる。
もう1つのポイントは、毎回1行の文字列のみを処理し、この行のディレクトリとファイルをルートノードルートから1つずつ挿入することです。
最後のポイントは、同じ挿入優先度の字句順序が最初であるということです。
以下は、ケースを例にしたツリーを構築する具体的なプロセスです。このプロセスを読んだ後、プログラムの書き方を知っているなら、すぐに書いてください。
確立プロセス全体を要約すると、次のようになります。
まず、ルートノードルートのみを使用してバイナリツリーを構築します。
次に、文字列の行を読み取ります。文字列の行には複数のディレクトリを含めることができますが、最大で1つのファイルしか含めることができないことに注意してください。
まず、ルートを指すようにポインタposを設定します。
このルールに従って、最初に文字列の最初のディレクトリを見つけ、次にposをルートとするバイナリツリーでこのディレクトリの存在を探します。存在する場合は、posを使用して、バイナリツリーにすでに存在するディレクトリのアドレスを記録します。このディレクトリがバイナリツリーに存在しない場合は、このディレクトリをバイナリツリーに配置する場所を探します。検索プロセスは、ルートノードの子から始まり、チェーンに沿って右に進みます。右端まで進んでいることに注意してください。我々は、(いわゆる優先順位ここでは、ディレクトリの優先順位が高くなるように1は、0よりも大きい場合、ディレクトリの優先順位は、ファイルの優先度が0で、1であることを指定)チェーン上のノードノードの優先順位を比較するたびに挿入したい場合優先度がチェーン上のノードよりも高いか、優先度が同じであるが、挿入されるノードの字句順序が小さい場合、チェーン上のノードの前に直接挿入できるか、優先度がチェーン上のノードよりも低いか、優先度が同じである場合ただし、字句の順序がチェーン上のノードNodeより大きい場合は、このチェーンに沿って逆方向に検索を続けます。最も極端なケースは、チェーンの終わりを見つけることです。次に、チェーンの終わりは、挿入された要素を持ってくる位置です。 最後に、最初のディレクトリをバイナリツリーに挿入し、posを挿入された位置に更新します。
次に、2番目のディレクトリを読み取り、上記の手順を繰り返します。
3番目のディレクトリへの読み取りを続行します。。。
すべてのディレクトリを読み取った後、最後のディレクトリの後ろにファイルがあるかどうかを確認する必要があります。ある場合は、posをルートとするサブツリーに挿入します。ファイルはディレクトリとは異なり、ディレクトリはすでにサブツリーにある可能性があることに注意してください。存在するため、最初にディレクトリがバイナリツリーに存在するかどうかを確認する必要がありますが、ファイルを確認する必要はありません。posをルートとするサブツリーに直接挿入するだけです。
これまでに、文字列の行の処理を完了しました。文字列からディレクトリまたはファイルをインターセプトする方法についての詳細があります。substr()関数を使用する必要があります。
したがって、すべての文字列が処理されるまで、文字列の次の行を同じ方法で処理します。
次の図は、ディレクトリとファイルを挿入する方法のプロセスをシミュレートしています。
プログラムフレームワーク:
int main()
{
建立一颗只有根结点root的二叉树;
for(i=0; i<N; i++)
{
读入一行字符串str;
设置一个指针pos指向root;
while(str中还有目录还没插入二叉树中)
{
读入一个目录;
将目录插入以pos为根的子树中,并把pos更新为插入的地址;
}
if(有文件未读入)
{
读入文件;
把文件插入以pos为根结点的子树;
}
}
前序遍历以root为根结点的二叉树;
return 0;
}
コード:
#include<stdio.h>
#include<string.h>
#include<string>
#include<stack>
#include<iostream>
using namespace std;
struct TreeNode//左孩子右兄弟的二叉树表示法
{
string str;
TreeNode* child;//左孩子
TreeNode* sibling;//右兄弟
int priority;//priority==0表示为文件,priority==1为目录,目录的插入优先级比文件高
};
typedef TreeNode* Position;
typedef Position BinTree;
Position createNode(string s, int priority)//创建一个新结点并初始化
{
Position Node = new TreeNode;
Node->str = s;
Node->child = Node->sibling = NULL;
Node->priority = priority;
return Node;
}
Position insert(BinTree root, string s, int priority)//插入结点函数,把结点作为root的孩子插入,并返回插入的位置
{
if(root->child == NULL)//如果root的根本没有儿子的话,那么直接插入在root儿子的位置即可。
{
root->child = createNode(s,priority);
return root->child;
}
Position tmpNode = root->child,father = root;
//没有return,说明root有儿子,开从root的儿子开始循链找插入位置
//如果待插入结点的优先级小于tmpNode的优先级,或者tmpNode的优先级和待插入结点优先级一致,但待插入结点的字典序大于tmpNode的字典序,要继续循链寻找。
//注意因为循链寻找的的极端过程是找到了链的最右边,即可能tmpNode可能会为NULL,所以要加入判断语句tmpNode!=NULL,即当找到链的末尾时就要退出,否则会产生段错误。
while(tmpNode!=NULL&&((priority<tmpNode->priority)||(tmpNode->priority==priority&&s>tmpNode->str)))
{
father = tmpNode;
tmpNode = tmpNode->sibling;
}
//当我们退出循环时有3种情况,1、找到了链的末尾,说明结点应该插入链的末尾处;2、待插入的目录已经存在于二叉树中,我们直接返回目录所在位置即可,无需插入;
//3、找到了应该插入的位置。这3中情况分别对应下面的if else语句。
if(tmpNode==NULL)//1、找到了链的末尾,说明结点应该插入链的末尾处;
{
Position Node = createNode(s,priority);
Node->sibling = father->sibling;
father->sibling = Node;
return Node;
}
if(tmpNode->priority==priority&&s==tmpNode->str)//2、待插入的目录已经存在于二叉树中,我们直接返回目录所在位置即可,无需插入;
return tmpNode;
else//3、找到了应该插入的位置。
{
Position Node = createNode(s,priority);
if(father->str == root->str)//但这里又有一个细节,插入位置是在root的child的位置和root的child的sibling的位置的插入方法是不一样的。
{
Node->sibling = father->child;
father->child = Node;
}
else
{
Node->sibling = father->sibling;
father->sibling = Node;
}
return Node;
}
}
void PreorderTraversal(BinTree BT,int layer)//前序遍历输出,注意需要一个参数layer来控制缩进,递归和非递归都可以,非递归版本我也写了但是没有递归这么漂亮,想要非递归版本可以给我留言
{
int i;
int childlayer,siblinglayer;
if(BT)
{
for(i=0; i<layer; i++)
printf(" ");
childlayer = layer+1;
siblinglayer = layer;
cout<<BT->str<<endl;
PreorderTraversal(BT->child,childlayer);
PreorderTraversal(BT->sibling,siblinglayer);
}
}
int main()
{
int N,i,bgn,end,j;
string str;
Position pos;
BinTree root = createNode("root",1);//建立只有一个根结点的二叉树,根结点也是目录所以优先级为1
scanf("%d\n",&N);
for(i=0; i<N; i++)
{
getline(cin,str); pos = root; bgn = end = 0;//bgn记录目录或文件的首字符位置,end记录目录或文件最后一个字符后面一个字符的位置
for(j=0; j<str.length(); j++)
{
if(str[j] == '\\')
{
end = j;
pos = insert(pos,str.substr(bgn,end-bgn),1);
bgn = end+1;
}
}
if(str[bgn]!='\0')//读完所有目录后,判断字符串中是否还有文件,如果有,读入文件
{
insert(pos,str.substr(bgn,str.length()-bgn),0);
}
}
PreorderTraversal(root,0);//前序遍历输出
return 0;
}