Conversation
| roots.add(unionFind.find(i)) | ||
| return len(roots) | ||
| ``` | ||
| UnionFindを何も見なくても実装できるように言語化してみる。まずUnionFindの初期化ではサイズnと0からn-1までの各ノードの属する集合のルートをフィールドとしてもつ。各ノードの親は最初は各ノード自身を指定させる。findメソッドでは引数に入れたノードの集合のルートを求めて返すのだがノードAを引数にもらった際に単にself.parents[A]を求めた場合に親がノードBとして、ノードBがさらに親を持つ場合、再帰的に親の親を求めてノードが自分自身を親とする所まで遡る必要がある。またノードAの親を求める時に再びその再帰のプロセスを行うのは非効率なので、self.parent[node]を最終的な親に書き換えておく。 |
There was a problem hiding this comment.
まずUnionFindの初期化ではサイズnと0からn-1までの各ノードの属する集合のルートをフィールドとしてもつ。
コードと一致していないように見えますが、この説明で合っていますか?
def __init__(self, n):
self.parent = list(range(n))
self.rank = [1]*n
There was a problem hiding this comment.
最初self.n = nと書いていたのですが途中で書き換えてしまったので一致しなくなってしまいました...
| self.rank[pu] += self.rank[pv] | ||
| else: | ||
| self.parent[pu] = pv | ||
| self.rank[pv] += self.rank[pu] |
There was a problem hiding this comment.
これだとunion by rankではなく、union by sizeではないですか?
There was a problem hiding this comment.
本当ですね。NeetCodeの回答を参考にしたのですがあちらもrankを使っているけどもunion by sizeをしているみたいです。self.sizeに変名した方がいいですね。
| self.rank[pu] += self.rank[pv] | ||
| else: | ||
| self.parent[pu] = pv | ||
| self.rank[pv] += pu |
There was a problem hiding this comment.
間違ってますね...
puがintだったのでエラーが出ず見逃してました。
|
|
||
| return answer | ||
| ``` | ||
| 空間計算量はO(2*E + N), 時間計算量はO(2*E + N)である。(Eはedgesのサイズ、Nはノードの数) |
| @@ -0,0 +1,188 @@ | |||
| ## step1: | |||
| それぞれの集合の数を求めていくということで、Union Findを使うんだろなと思ったがUnion Findの実装方法をすっかり忘れてしまった。そこでDFSを使って各ノードを探索していくことにする。双方向グラフなのでお互いのノードに対してベクトルを辞書型で置いていき、深さ優先探索で辿っていく。edgesをfor文で回してもし辿ったことのないノードだったらそこで探索をし、そのグラフを探索済みのものとしてanswerをインクリメントしていく。 | |||
There was a problem hiding this comment.
双方向グラフなのでお互いのノードに対してベクトルを辞書型で置いていき
これがどういう意味なのか、文からは分かりませんでした。
| @@ -0,0 +1,188 @@ | |||
| ## step1: | |||
| それぞれの集合の数を求めていくということで、Union Findを使うんだろなと思ったがUnion Findの実装方法をすっかり忘れてしまった。そこでDFSを使って各ノードを探索していくことにする。双方向グラフなのでお互いのノードに対してベクトルを辞書型で置いていき、深さ優先探索で辿っていく。edgesをfor文で回してもし辿ったことのないノードだったらそこで探索をし、そのグラフを探索済みのものとしてanswerをインクリメントしていく。 | |||
There was a problem hiding this comment.
edgesをfor文で回してもし辿ったことのないノードだったらそこで探索
コードと一致していないように見えますが、どこに相当しますか?
There was a problem hiding this comment.
すいません、途中で方針を変えて言ってることが食い違ってしまうことが多々ありますね...
今回の場合は
「0からn-1までのノードを巡り辿ったことのないノードだったらそこで探索」
ですね。
| roots.add(unionFind.find(i)) | ||
| return len(roots) | ||
| ``` | ||
| UnionFindを何も見なくても実装できるように言語化してみる。まずUnionFindの初期化ではサイズnと0からn-1までの各ノードの属する集合のルートをフィールドとしてもつ。各ノードの親は最初は各ノード自身を指定させる。findメソッドでは引数に入れたノードの集合のルートを求めて返すのだがノードAを引数にもらった際に単にself.parents[A]を求めた場合に親がノードBとして、ノードBがさらに親を持つ場合、再帰的に親の親を求めてノードが自分自身を親とする所まで遡る必要がある。またノードAの親を求める時に再びその再帰のプロセスを行うのは非効率なので、self.parent[node]を最終的な親に書き換えておく。 |
There was a problem hiding this comment.
self.parent[node]を最終的な親に書き換えておく
最終的な親=ルートですかね?
There was a problem hiding this comment.
そうですね、最終的な親に名付けるならルートかなと思いました。
| return len(roots) | ||
| ``` | ||
| UnionFindを何も見なくても実装できるように言語化してみる。まずUnionFindの初期化ではサイズnと0からn-1までの各ノードの属する集合のルートをフィールドとしてもつ。各ノードの親は最初は各ノード自身を指定させる。findメソッドでは引数に入れたノードの集合のルートを求めて返すのだがノードAを引数にもらった際に単にself.parents[A]を求めた場合に親がノードBとして、ノードBがさらに親を持つ場合、再帰的に親の親を求めてノードが自分自身を親とする所まで遡る必要がある。またノードAの親を求める時に再びその再帰のプロセスを行うのは非効率なので、self.parent[node]を最終的な親に書き換えておく。 | ||
| unionメソッドは2つのノードを引数とし、それぞれが属している集合を合体させるのだが、もし合体させようとしてる2つのノードのルートが同じノードだった場合、それはその集合はすでに同一であることの証なので早期リターンする。もしparentが違ったら集合を合体させる。単に片方のルートのparentをもう片方のルートに設定すればいいのだが、集合のノード数が少ない方のルートの親を集合のノード数が多い方のルートに指定した方がself.parentの更新数が少なくなるのでself.rankを比較してノード数の多い方のルートを中心に結合する。この際に、結合された方のランクを結合する方へ加算するのを忘れない。self.findとself.unionの時間計算量はどちらもO(α(n))でほぼO(1)と同一視できるらしいが、もしself.findでの |
There was a problem hiding this comment.
集合のノード数が少ない方のルートの親を集合のノード数が多い方のルートに指定した方がself.parentの更新数が少なくなる
「self.parentの更新数が少なくなる」とは後のfind()を呼び出した時の話ですか?
There was a problem hiding this comment.
そうですね、findで再帰的に辿ったノードのparentをルート指定するように更新する時のことを言ってますね。
| return len(roots) | ||
| ``` | ||
| UnionFindを何も見なくても実装できるように言語化してみる。まずUnionFindの初期化ではサイズnと0からn-1までの各ノードの属する集合のルートをフィールドとしてもつ。各ノードの親は最初は各ノード自身を指定させる。findメソッドでは引数に入れたノードの集合のルートを求めて返すのだがノードAを引数にもらった際に単にself.parents[A]を求めた場合に親がノードBとして、ノードBがさらに親を持つ場合、再帰的に親の親を求めてノードが自分自身を親とする所まで遡る必要がある。またノードAの親を求める時に再びその再帰のプロセスを行うのは非効率なので、self.parent[node]を最終的な親に書き換えておく。 | ||
| unionメソッドは2つのノードを引数とし、それぞれが属している集合を合体させるのだが、もし合体させようとしてる2つのノードのルートが同じノードだった場合、それはその集合はすでに同一であることの証なので早期リターンする。もしparentが違ったら集合を合体させる。単に片方のルートのparentをもう片方のルートに設定すればいいのだが、集合のノード数が少ない方のルートの親を集合のノード数が多い方のルートに指定した方がself.parentの更新数が少なくなるのでself.rankを比較してノード数の多い方のルートを中心に結合する。この際に、結合された方のランクを結合する方へ加算するのを忘れない。self.findとself.unionの時間計算量はどちらもO(α(n))でほぼO(1)と同一視できるらしいが、もしself.findでの |
There was a problem hiding this comment.
ノード数の多い方のルートを中心に結合する
「中心に結合する」ってどういう意味ですかね?
There was a problem hiding this comment.
ここちょっと表現に迷いました。単純に、「ノード数の多い方のルートをノード数の少ない方のルートの親に指定する」と書けばよかったですね。
| def explore(x): | ||
| visited.add(neighbor) | ||
| for neighbor in x_to_neighbors[x]: | ||
| if not neighbor in visited: |
There was a problem hiding this comment.
neighbor not in visitedと書くのが一般的かと思います。
問題:https://neetcode.io/problems/count-connected-components/question
言語: Python3