Skip to content

323. Number of Connected Components in an Undirected Graph #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 9, 2025
Merged
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
50 changes: 50 additions & 0 deletions 323/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*

Solve Time : 13:59

Time : O(n)
Space : O(n)

UnionFindを用意する方法をまず思いついたが、UnionFind木を用意する手間が惜しいと考え別ルートへ
UnionFindそのものを用意するのではなく、考え方を流用したコードを考える。
初期値を-1で初期化したサイズnのベクタを用意する。
個々の要素に自分のクラスタのリーダーとなるインデックスをもたせる。
edgesの処理に合わせてそれらを更新していくことでリーダー以外のノードがすべて-1以外の数値を持つようになるので、最後に-1を数える。
下記に示すケースで間違えたため、それに合わせて修正を加えてパスしたが、エッジケースの想定が甘かった。
```
n = 3, edges = [[2,0],[2,1]]
n = 5, edges = [[0,1],[1,2],[0,2],[3,4]]
```

修正を加えた結果だいぶUnionFindに近づいた気がする。
*/
class Solution {
public:
int countComponents(int n, vector<vector<int>>& edges) {
vector<int> nodes = vector<int>(n, -1);
for (auto edge : edges) {
int left = edge.front();
int right = edge.back();
while (nodes[right] >= 0) {
right = nodes[right];
}
while (nodes[left] >= 0) {
left = nodes[left];
}
if (left == right) {
continue;
}
if (left > right) {
swap(left, right);
}
nodes[right] = left;
Copy link

Choose a reason for hiding this comment

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

ほとんど Union Find ですが、ここのところで、nodes のグラフの単純化が行われていないので、結果的に計算時間はかかるかもしれませんね。ここで、元の edge.front(), edge.back() から上流の node に対して、nodes[node] = left にすれば、Union Find を展開したものになっていると思います。

}
int count = 0;
for (auto i : nodes) {
if (i == -1) {
count++;
}
}
return count;
}
};
44 changes: 44 additions & 0 deletions 323/step2_1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Time : O(n)
Space : O(n)

step1に対してセルフレビューで修正を加えた
- 各ノードを参照する事に階層を引き上げる処理を加えた。
Copy link

Choose a reason for hiding this comment

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

https://discord.com/channels/1084280443945353267/1183683738635346001/1197738650998415500
高速化の手法はいくつかあるみたいですが、これでも十分ですね。

- クラスタのトップを表すマジックナンバーを定数化

*/
class Solution {
public:
int countComponents(int n, vector<vector<int>>& edges) {
const int kClusterTop = -1;
vector<int> nodes = vector<int>(n, kClusterTop);
for (auto edge : edges) {
int left = edge.front();
int right = edge.back();
while (nodes[right] >= 0) {
const int previous_right = right;
right = nodes[right];
nodes[previous_right] = right;
}
while (nodes[left] >= 0) {
const int previous_left = left;
left = nodes[left];
nodes[previous_left] = left;
}
if (left == right) {
continue;
}
if (left > right) {
swap(left, right);
}
nodes[right] = left;
}
int count = 0;
for (auto i : nodes) {
if (i == kClusterTop) {
count++;
}
}
return count;
}
};
55 changes: 55 additions & 0 deletions 323/step2_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Time : O(N + M)
Space : O(N + M)

edges.size() = M

他の人の解法を参考にDFSを用いて解き直した。
edge list形式でDFSをするというのが初見でしっくりこなかったが、隣接リスト形式に変換してからDFSするという点はしっくりきた。
自明にデータ形式を変換する考察が不足しており、素直な解法にたどり着けなかったのでその点を補いたい。

*/
class Solution {
public:
int countComponents(int n, vector<vector<int>>& edges) {
vector<vector<int>> adjacency_nodes = vector<vector<int>>(n, vector<int>());
vector<char> visited = vector<char>(n, '0');
Copy link

Choose a reason for hiding this comment

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

個人的には、0, 1 を使いたいです。'1' ではなくて。

for (auto edge : edges) {
const int x = edge.front();
const int y = edge.back();
adjacency_nodes[x].push_back(y);
adjacency_nodes[y].push_back(x);
}
int count = 0;
for (int index = 0; index < n; ++index) {
if (visited[index] == '1') {
continue;
}
InvestigateIndexes(index, adjacency_nodes, visited);
++count;
}
return count;
}

private:
void InvestigateIndexes(int start_index,
const vector<vector<int>>& adjacency_nodes,
vector<char>& visited) {
stack<int> next_indexes;
next_indexes.emplace(start_index);
while (!next_indexes.empty()) {
const int index = next_indexes.top();
next_indexes.pop();
if (visited[index] == '1') {
continue;
}
visited[index] = '1';
for (int adjcent_index : adjacency_nodes[index]) {
if (visited[adjcent_index] == '1') {
continue;
}
next_indexes.emplace(adjcent_index);
}
}
}
};
39 changes: 39 additions & 0 deletions 323/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
class Solution {
Copy link

Choose a reason for hiding this comment

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

とても良いと思います。

public:
int countComponents(int n, vector<vector<int>>& edges) {
vector<vector<int>> adjcent_indexes = vector<vector<int>>(n, vector<int>());
for (auto edge : edges) {
const int left = edge.front();
const int right = edge.back();
adjcent_indexes[left].emplace_back(right);
adjcent_indexes[right].emplace_back(left);
}
set<int> visited;
int count = 0;
for (int index = 0; index < n; ++index) {
if (visited.contains(index)) {
continue;
}
++count;
VisitCluster(index, adjcent_indexes, visited);
}
return count;
}

private:
void VisitCluster(const int start_index, const vector<vector<int>>& adjacent_indexes, set<int>& visited) {
stack<int> next_indexes;
next_indexes.emplace(start_index);
while (!next_indexes.empty()) {
const int index = next_indexes.top();
next_indexes.pop();
if (visited.contains(index)) {
continue;
}
visited.insert(index);
for (const int next_index : adjacent_indexes[index]) {
next_indexes.emplace(next_index);
}
}
}
};