これは私が答えようとしているインタビューの質問です:
N
メンバーを含むソーシャルネットワークと、メンバーのペアが友情を形成したM
タイムスタンプを含むログファイルを前提として、すべてのメンバーが接続された最も早い時間を決定するアルゴリズムを設計します(つまり、すべてのメンバーは、友達の友達の友達です...友達の友達です)。ログファイルがタイムスタンプでソートされており、友情は同値関係であると想定します。アルゴリズムの実行時間はM log N
以上で、N
に比例する余分なスペースを使用する必要があります。
最初に思ったのは…「これはできない!」。
しかし、このソーシャルネットワークをデータ構造としてどのように表現できるかを考えました。 Union-findは、使用できるデータ構造です。ここで、すべてのメンバーが接続されている場合の意味を理解する必要があります。実際のデータ構造と、すべてのメンバーがお互いに友達になったときの様子をどのように表示できますか?
システムが完全に接続される方法を視覚的または概念的に理解できるようになるまで、そのイベントに対応するタイムスタンプを見つける方法を理解し始めることができると思います。
Union-findデータ構造に友情を追加すると、2つのグラフコンポーネントが結合されるかどうかに注意できます。これらのマージイベントのN-1が発生するまで、エッジを追加し続けるだけです。
擬似コード形式:
G := UnionFind(1..N)
count := 0
for timestamp, p1, p2 in friendships {
if G.Find(p1) != G.Find(p2) {
G.Union(p1, p2)
count++
if count == N-1 {
return timestamp
}
}
}
return +infinity
この演習を解決するために、ログファイルが次のようになると仮定しました。
0 1 2015-08-14 18:00:00
1 9 2015-08-14 18:01:00
0 2 2015-08-14 18:02:00
0 3 2015-08-14 18:04:00
0 4 2015-08-14 18:06:00
0 5 2015-08-14 18:08:00
0 6 2015-08-14 18:10:00
0 7 2015-08-14 18:12:00
0 8 2015-08-14 18:14:00
1 2 2015-08-14 18:16:00
1 3 2015-08-14 18:18:00
1 4 2015-08-14 18:20:00
1 5 2015-08-14 18:22:00
2 1 2015-08-14 18:24:00
2 3 2015-08-14 18:26:00
2 4 2015-08-14 18:28:00
5 5 2015-08-14 18:30:00
最初の2つの番号は、友情を形成したメンバーであり、その後にタイムスタンプが続きます。
もう1つの重要な点は、ファイルが並べ替えられていることを演習で説明しているため、昇順で並べ替えることにしました。
この情報を使用して、クラスで提供される WeightedQuickUnionFind データ構造を使用し、メンバーに対して和集合操作を実行するファイルを簡単に処理できます。和集合を作成したら、コンポーネントの数を尋ねることができます。構造、すべてのメンバーが 等価関係 を持っていることを意味するものが1つしかない場合。
これが私がしたコードです:
public static void main(String[] args) {
int n = StdIn.readInt();
WeightedQuickUnion uf = new WeightedQuickUnion(n);
String date, time;
//timestamps are sorted ascending
while (!StdIn.isEmpty()) {
int p = StdIn.readInt();
int q = StdIn.readInt();
date = StdIn.readString();
time = StdIn.readString();
uf.union(p, q);
StdOut.println("["+p+","+q+"]");
if(uf.getComponents() == 1){
StdOut.println("All members were connected at: " + date + time);
break;
}
}
反復しているため、パフォーマンスはM lg Nになります[〜#〜] m [〜#〜]回(ログファイルの行数)およびユニオン操作には次の時間がかかります:lg n。
@Andrésの回答に追加します。これらすべてが接続されているかどうかを確認するメソッドは、WeightedQuickUnionFindクラスに追加されます。
public boolean isAllConnected(){
int N = id.length;
while(--N>0 && id[N] == id[0]);
return N == 0;
}
これは、接続されているコンポーネントの数のカウントを追加することで簡単に実行できます。 N個のメンバーをN個のオブジェクトと見なし、M個のタイムスタンプをM個のユニオンと見なします。その場合、「すべてのメンバーが接続される最も早い時間」は、接続されたコンポーネントの数が1に等しい時間です。
Andresによって提供された答えは正しいものです。別の方法もあります。ここでは、N個の要素に比例する余分なスペースを使用できます。したがって、ツリー内のノードの総数の配列を保持でき、両方のツリーをマージした後、ユニオン関数で、マージされるツリー内のノードの数を、ルートであるツリー内のノードの数に追加する必要があります。両方の。したがって、追加した後、新しいツリーのノードの総数がNに等しいかどうかを確認する必要があります。等しい場合は、N人のメンバー全員が互いに友達になる最も早い時期になります。
ソーシャルネットワークは、各ノードが0からn
の接続を持つツリーまたはグラフとして表すことができます。
必要なデータ構造の主要部分は整数の配列です。各要素のインデックスはソーシャルネットワークのメンバーIDとして解釈でき、値は最初のメンバーのルートである別のメンバーのIDです。
ログを読み取り、各ログレコードのデータ構造でunion
操作を実行して(ツリーを構築)、2つの基準を分析する必要があります。
Set<Integer> haveConnection
)global
ルートは1つだけです(最初から、独自のルートを持つ接続されていないサブネットワークが多数あるため)(Set<Integer> roots
)両方の基準が満たされると、ネットワークのすべてのメンバーが接続されます。
幸運を!