ディレクトリパス、およびこのディレクトリ内のコンテンツを持つすべてのファイルを含むディレクトリ情報のリストを考えると、あなたはそれらのパスの観点から、ファイルシステム内の重複したファイルのすべてのグループを確認する必要があります。
重複したファイルのグループは、まったく同じ内容を有する少なくとも2つのファイルで構成します。
入力リスト内の単一のディレクトリ情報文字列の形式は次のとおりです。
"root/d1/d2/.../dm f1.txt(f1_content) f2.txt(f2_content) ... fn.txt(fn_content)"
これは、n個のファイル(ある意味f1.txt
、 f2.txt
... fn.txt
コンテンツと f1_content
、 f2_content
... fn_content
ディレクトリ内に、それぞれが) root/d1/d2/.../dm
。そのN> = 1およびm> = 0、M = 0は、それがディレクトリだけルートディレクトリであることを意味する場合に注意してください。
出力は、重複したファイルパスのグループのリストです。各グループの場合は、同じ内容を持つすべてのファイルのファイルパスが含まれています。ファイル・パスは、次の形式を持っている文字列です。
"directory_path/file_name.txt"
例1:
入力: [ "ルート/ 1.TXT(ABCD)2.txt(EFGH)"、 "ルート/ C 3.txt(ABCD)"、 "ルート/ C / Dの4.txt(EFGH)"、「ルート4 .TXT(EFGH) "] 出力 :[["ルート/ A / 2.txt " "ルート/ C / D / 4.txt"、 "ルート/ 4.txt"]、["ルート/ / 1。 TXT」、 "ルート/ C / 3.txt"]]
1 クラスソリューション{ 2 パブリック 静的リスト<リストの<string >> findDuplicate(文字列[]パス){ 3 地図<文字列、リストの<string >>地図= 新規 HashMapの<> (); 4 のための(文字列パス:パス){ 5 列[]トークン= path.split(" " )。 図6は、 のために(int型 i = 1 ; iはtokens.length <; iは++ ){ 7 文字列のファイル=トークンを[I] .substring(0、トークン[i]は.indexOf(' (' )); 8 文字列の内容=トークン[I] .substring(トークン[I] .indexOf(' (')+ 1、トークン[I] .indexOf(' )' )); 9 map.putIfAbsent(コンテンツ、新規のArrayList <> ()); 10 マップ。取得(コンテンツ).add(トークン[ 0 ] + " / " + ファイル)。 11 } 12 } 13 リターン。map.values()ストリーム()フィルタ(E - > e.size()> 1 ).collect(Collectors.toList())。 14 } 15 }
フォローアップの質問:
- あなたがファイルを検索する方法を、あなたは実際のファイルシステムを与えている想像してみて?DFSまたはBFS?
答えは、ツリー構造に依存します。分岐因子(n)と深さ(d)が高い場合には、BFSは、メモリO(^ N D)の多くを取るでしょう。O(D) - DFSため、空間の複雑さは、一般に、ツリーの高さです。
- ファイルの内容は、(GBレベル)非常に大きい場合は、どのようにあなたのソリューションを修正するのだろうか?
- あなただけの1キロバイトずつファイルを読み取ることができる場合は、どのようにあなたのソリューションを修正するのだろうか?
- 変更したソリューションの時間の複雑さとは何ですか?その一部を消費する最も時間のかかる部分とメモリとは何ですか?どのように最適化するには?
- どのようにあなたが見つける重複ファイルは偽陽性でないことを確認するには?
質問1:
コアアイデア:DFS
理由:ディレクトリの深さは、あまり深くはない場合BFSと比較して、DFSを使用するのに適しています。
質問2:
ファイルの内容は、(GBレベル)非常に大きい場合は、どのようにあなたのソリューションを修正するのだろうか?
回答:
コアアイデア:本当に大規模なコンテンツを読み込む前に、ファイルサイズなどのメタデータを利用し、作ります。
二つのステップ:
- DFSその大きさを持ってパスのセットに各サイズをマップする:地図<整数、セット>
- 各サイズのために、そこに2つ以上のファイルがある場合は、同じサイズの任意のファイルが同じハッシュを持っている場合は、MD5により、すべてのファイルのハッシュコードを計算し、それらは同一のファイルです:に各ハッシュをマッピング地図<文字列、セット>、ファイルパス+ファイル名の設定。このハッシュIDは非常に非常に大きなされているので、我々は、JavaライブラリのBigIntegerを使用しています。
ステップ-2を最適化します。GFSには、複数の大きなファイル「チャンク」(1つのチャンクは64キロバイトである)を格納します。我々は、ファイルサイズ、ファイル名、および各チャンクのチェックサム(コンテンツのXOR)と一緒に別のチャンクのインデックスを含むメタデータを、持っています。ステップ2については、我々は単に各ファイルのチェックサムを比較します。
短所:2つの異なるファイルが同じチェックサムを共有する可能性があるため、flase正の重複があるかもしれません。
質問-3:
あなただけの1キロバイトずつファイルを読み取ることができる場合は、どのようにあなたのソリューションを修正するのだろうか?
回答:
- makeHashQuick機能は、素早いが、メモリ飢えている可能性のjavaで実行する可能性がある-Xmx2GやRAMのavaliable場合はヒープ領域を増加させる可能性が高いです。
- 私たちは、メモリを効率的にするために、「BUFFSIZE」によって定義されたサイズでプレーする必要がある場合があります。
質問-4:
変更したソリューションの時間の複雑さとは何ですか?その一部を消費する最も時間のかかる部分とメモリとは何ですか?どのように最適化するには?
回答:
- 一部をハッシュすることは、ほとんどの時間がかかり、メモリを消費します。
- 上述のように最適化するだけでなく、偽陽性の問題を紹介します。
質問-5:
どのようにあなたが見つける重複ファイルは偽陽性でないことを確認するには?
回答:
質問-2-回答-1、それを避けることができます。
私たちは、チェックサムを使用して2つの「重複」を見つけたとき、チャンクでコンテンツチャンクを比較する必要があります。
私のDropboxのインタビューの準備では、私はこの問題に出くわしたと(これらはインタビュアーが、いないコード自体の中で最も興味があったことを質問があったように)本当にフォローアップの質問の背後にあるアイデアを探していました。これはフォローアップの議論を有する唯一のポストがあるので、私はここにコメントします!@yujunは、上記の優れたソリューションを与え、私は将来のインタビューを助けるためにもう少しを追加したいです。
重複ファイルを検索するには、文字列配列の任意の入力は非常に簡単です。各文字列をループし、文字列の/コレクションセットする文字列のHashMapを保つ:連結、ファイル名とパスのセットに各ファイルの内容をマッピングします。
私にとっては、代わりのパスのリストを与え、私はディレクトリを与えられ、その下のすべての重複ファイルのリストのリストを返すように頼まれました。私のようなディレクトリを表すことにしました:
class Directory{
List<Directory> subDirectories;
List<File> files; }
ディレクトリを考えると、あなたは非常に大きなファイル与えられた重複ファイルを見つけることができるか尋ねています。ここでの考え方は、ディスク内のファイルの内容を格納する必要があるので、あなたは、メモリの内容を保存することができないということです。ですから、各ファイルの内容をハッシュし、各ファイルのメタデータフィールドとしてハッシュを保存することができます。あなたが検索を実行するようそして、メモリに代わりにファイルの内容をハッシュを格納します。だから、アイデアは、あなたがルートディレクトリを通じてDFSを行うと、作成することができている HashMap<String, Set<String>>
マッピングにそのハッシュの内容に対応してファイルパス+ファイル名のセットに、各ハッシュを。
(注意: あなたが道を横断するBFS / DFSを選ぶことができ、それはより多くのメモリ効率的かつアップコーディングする迅速であるように私にはDFSを選びました。)
フォローアップ:これは素晴らしいですが、それは大きなファイルのために高価になることができた、一度単一のファイルのハッシュを計算する必要があります。とにかくあなたは、ファイルのハッシュを計算避けることができますがありますか?
一つの方法は、ディスク上の各ファイルのサイズのためのメタデータフィールドを維持することです。次に、2つのパスアプローチを取ることができます。
- DFSは、その大きさを持っているパスのセットにそれぞれのサイズをマッピングします
- 各サイズのために、そこに2つ以上のファイルがある場合は、同じサイズの任意のファイルが同じハッシュを持っている場合、それらは同じファイルである、すべてのファイルのハッシュを計算します。
あなたは同じサイズで複数のファイルを持っている場合は、この方法では、あなただけのハッシュを計算します。あなたがDFSを行うときに、あなたが作成することができ HashMap<Integer, Set<String>>
、その大きさを持つファイルパスのリストに、各ファイルのサイズをマッピングし、。各セット内の各文字列をループし、そのハッシュを取得するには、それはあなたのセット内に存在する場合はそうならば、あなたにそれを追加チェックし List<String> res
そうセットにそれを追加します。各キー(ファイルサイズを切り替え)の間では、あなたにあなたの解像度を追加することができます List<List<String>>
。
ただ、議論のための私の謙虚な意見を共有したい:
誰もがよりよい解決策を持っている場合は、訂正して:-)私を啓発したい場合、私はそれをいただければ幸いです
。質問2:
実世界のファイルシステムでは、我々は通常、大きなファイルを保存します複数の「チャンクは」(GFSで、1つのチャンクは64メガバイトである)ので、我々は、各チャンクのチェックサム(コンテンツのXOR)と一緒に、ファイルサイズ、ファイル名と異なるチャンクのインデックスを記録したメタデータを持っています。
私たちは、ファイルをアップロードする際、上述のように、私たちは、メタデータを記録します。
私たちは、重複をチェックする必要がある場合、我々は単にメタデータを確認できます。
1.Checkをファイルのサイズが同じである場合。
2.Ifはステップ1を通過すると、最初のチャンクのチェックサムを比較
3.Ifはステップ2のパスを、第二のチェックサムを確認する
...
ようにと。
2つの異なるファイルが同じチェックサムを共有する可能性があるため、偽陽性の重複があるかもしれません。
質問3:
上記のように、我々は全体の代わりに、ファイルのメタデータを読み取ることができ、及びKBによる情報KBを比較します。
質問5:
チェックサムを使用して、私たちは迅速かつ正確に非重複ファイルを見つけることができます。しかし、完全に偽陽性を避けるために、我々は我々がチェックサムを使用して2つの「重複」を見つけたとき、チャンクでコンテンツチャンクを比較する必要があります。