Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
https://neetcode.io/problems/count-connected-components/question
で代用
## step1
- 絵が嘘つきすぎてて問題文読み間違えているのか何回も確認する羽目になった
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分は図のどこがおかしいのかよく分からないんですが、参考までにお聞きしても良いですか?

- 問題文全体を和訳したら流石に絵がおかしいことに自信が持てたので英語もっと自信持って読みたい
- [200]や[695]でやったようにしたら解けそう
- 全てのnodeをunvisited_nodeに入れておく
- 下記をunvisitedがなくなるまでloopさせたらOK
- unvisitedからpopして連結している部分をedgeから探していく
- 連結している部分もunvisitedから取り除く
- その際にカウントを増やす
- 最後にカウントを返す
- 20分ほどで書けた
- 辺を管理する方法はdefaultdictの他に二次元配列も選択肢にあった
- 二次元配列だと空の部分が無駄になるのであまり使いたくない気持ちだった
- 辺が多い場合はあまり変わらないが、辺が少ない場合はdefaultdictの方がサイズが空間計算量が小さくて済むと思った
- step2で確かめてみる
- 時間計算量はedgeの格納でO(m)、幅優先探索でO(n+n)だと思う(node(n)edge(m))
- 空間計算量はedgeの格納でO(m)、幅優先探索でO(n+n)だと思う(node(n)edge(m))
- small_to_large_edgesと書いていたが両側から管理するようにしたので命名が適さなくなった

## step2
- 一旦UnionFindでもやってみる
- 元々のnodeが小さい方を親にしていくようにする
- 管理するものがないので実装は迷わなかった
- 8分くらい

- 他の人のコードを読んでみる
- https://github.com/Yuto729/LeetCode_arai60/pull/24/files
- vertex(頂点)は思いつかなかった
- union_findでsizeの部分がなぜそれでOKになるのかがわからなかった
- Union Findの経路圧縮には3つほど方法がある
- neighberはグラフ理論ではあるノードから見て、1本の辺で到達できるノードという意味なので覚える
- adjacent_matrix(隣接行列)
- https://github.com/naoto-iwase/leetcode/pull/28
- `listで記載する方法、勉強になりました。問題の制約から今回はありえませんが、nが大きく、疎のノードが多い場合には空間計算量の点でdictがベターな選択肢になることもあるかもしれません。`これを思ったのでdictにした
- `https://youtu.be/nclfErc9pbE?si=QP-DUn3SNRnTabkK`見てみる
- Path Compression(経路圧縮)のやり方がわかりやすかった
- Path Splitting(経路分割)と Path Halving(経路半減)はあんまり理解できなかった
- https://github.com/yas-2023/leetcode_arai60/pull/19/files
```
num_nodesの数が決まっているので自分であればdictではなく以下のようにlistのlistで作りそうです。
adjancy = [[] for _ in range(num_nodes)]
このように書いても使い勝手はほぼ変わらず辞書の構築コストやハッシュの計算コストなどがかからないため若干効率的かもしれません。
ただ孤立した点が多いようなグラフだと前もって空のリストを作っているのがメモリの無駄遣いになってしまうという懸念はありそうです。また初期化に関してはdefaultdictを使うほうがシンプルで、読みやすさの面でもyasさんの実装のほうがよさそうですね。
```
- 辞書の構築コスト、ハッシュの計算コストと二次元配列の構築コストを比較した時の感覚が全くないのだが二次元配列の構築コストがかなり小さい?
- https://github.com/garunitule/coding_practice/pull/19/files
- 自分の実装の流れとほぼ同じだったのだが、自分は幅優先探索をしているつもりだったがよくよく考えてみると深さ優先探索になっていた
- 処理の流れとどこを優先して処理しているのか(配列の末尾には何が入っているのか)をもっとよくイメージする必要があると思った
- 他の人のコードを読むときに理解に時間が掛かりそうだと思ったらざっくり目で追って読み飛ばしてしまう癖が成長を妨げている気がする
- 次回以降は直近5人のうち理解が難しそうな2人分のコードは頭の中で処理が完全に再現できるように理解することを目指して残り3人は今まで通りざっくり読むようにして徐々に深く読めるようになっていきたい
- 今回はこの方のコードは100%理解するつもりで読む
- step1
- 初期値-1とするのか
- parentのDictはkeyとvalueは何をあらわしてる?
- valueは親のnode,keyは現在のnode
- child_to_parentとして欲しい気持ち
- find_rootはrootと深さを返している
- unionは結合する回数なのでunion_countとかunion_numとしてもらえたらもっとわかりやすかった
- 最初union: int = 0で何するのかわからなかった
- 最後のn - unionで分かった
- これはどういう解法なのかが分かっていなくて読んだからunionが結合の回数を表しているのにすぐに思い至れなかった気もする
- step2はすっと読めた
- visitを再帰で書いており、回数が気になったがnodeが100までなのでOK(defaultは1000)
- step3もすっと読めた
-
- そういえばnodeと癖で書いてしまったがnodeは情報が少ないのでやめるように指摘されていたのを忘れていた
## step3
- 8分ほどで書けた
- ケアレスミスもなく、1度でpassした
- edge_dict=>vertex_to_neghbersに修正した

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# https://neetcode.io/problems/count-connected-components/question
import collections


class Solution:
def countComponents(self, n: int, edges: List[List[int]]) -> int:
unvisited = set(range(n))
edge_dict = collections.defaultdict(set)
for node1, node2 in edges:
edge_dict[node1].add(node2)
edge_dict[node2].add(node1)

connected_component_count = 0

while unvisited:
node = unvisited.pop()
connected_component_count += 1
connected_component = [node]

while connected_component:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connected_componentよりはunvisited_nodes_in_connected_componentという意味合いな気がしますね
unvisited_connected_nodesとかもありかもしれないです

connected_node = connected_component.pop()
for node in edge_dict[connected_node]:
if node not in unvisited:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

逆の逆みたいなのは少し読みづらいですね

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for文+visitedとかの方が個人的には見やすいかもしれません

continue
connected_component.append(node)
unvisited.remove(node)

return connected_component_count
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# https://neetcode.io/problems/count-connected-components/question
class UnionFind:
def __init__(self, n: int):
self.parents = list(range(n))

def find(self, n):
if self.parents[n] == n:
return n
return self.find(self.parents[n])

def union(self, node1, node2):
node1_parent = self.find(node1)
node2_parent = self.find(node2)

if node1_parent == node2_parent:
return

if node1_parent > node2_parent:
node1_parent, node2_parent = node2_parent, node1_parent

self.parents[node2_parent] = node1_parent


class Solution:
def countComponents(self, n: int, edges: list[list[int]]) -> int:
uf = UnionFind(n)

for edge in edges:
node1, node2 = edge
uf.union(node1, node2)

root_nodes = set()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return len(set(uf.parents))でも行けそうです

for i in range(n):
root_nodes.add(uf.find(i))

return len(root_nodes)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# https://neetcode.io/problems/count-connected-components/question
import collections


class Solution:
def countComponents(self, n: int, edges: list[list[int]]) -> int:
unvisited_vertices = set(range(n))
vertex_to_neghbers = collections.defaultdict(set)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neighborsですかね

for vertex1, vertex2 in edges:
vertex_to_neghbers[vertex1].add(vertex2)
vertex_to_neghbers[vertex2].add(vertex1)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちなみにこれ片方だけでも書けますか


component_count = 0
while unvisited_vertices:
vertex = unvisited_vertices.pop()
component_count += 1
traversed_vertices = [vertex]

while traversed_vertices:
vertex = traversed_vertices.pop()
for neighber in vertex_to_neghbers[vertex]:
if neighber not in unvisited_vertices:
continue
traversed_vertices.append(neighber)
unvisited_vertices.remove(neighber)

return component_count