題目網址:https://leetcode.cn/problems/graph-valid-tree/
題意: 給定編號 0
到 n - 1
的 n
個 node, 和一個無向邊 的 list edges
, 其中 edges[i] = [a, b]
代表 graph 中 a
和 b
之間有一邊, 返回此 graph 是否為 tree。
注意: edges
中沒有重複的邊(e.g. [1, 0]
和 [0, 1]
不會同時出現在 edges
中)
Solution 1:
想法:利用 DFS, 若 graph 要為 tree, 則必須滿足以下兩點:
graph 中不可有 cycle
graph 必須為 connected, 也就是圖中任兩點必定有邊相連, 不能有 isolated vertex
class Solution {public : bool validTree (int n, vector<vector<int >> &edges) { vector<vector<int >> adj (n); vector<bool > visited (n, false ) ; for (const auto & e : edges) { adj[e[0 ]].emplace_back (e[1 ]); adj[e[1 ]].emplace_back (e[0 ]); } return dfs (adj, visited, 0 , -1 ) && connected (visited); } private : bool dfs (vector<vector<int >>& adj, vector<bool >& visited, int cur, int prev) { if (visited[cur]) { return false ; } visited[cur] = true ; for (const auto & v : adj[cur]) { if (v != prev) { if (!dfs (adj, visited, v, cur)) { return false ; } } } return true ; } bool connected (vector<bool >& visited) { for (const auto & v : visited) { if (!v) { return false ; } } return true ; } };
time: $O(V + E)$ ➔ 必須拜訪所有 node, 和其延伸出去的所有 edge
space: $O(V + E)$ ➔ 因為 adj
為 $O(V + E)$, V
是因為要記錄所有點, E
是因為會記錄所有邊
Solution 2:
想法:利用 BFS
class Solution {public : bool validTree (int n, vector<vector<int >> &edges) { vector<unordered_set<int >> adj (n); for (const auto & e : edges) { adj[e[0 ]].emplace (e[1 ]); adj[e[1 ]].emplace (e[0 ]); } queue<int > q ({0 }) ; unordered_set<int > visited ({0 }) ; while (!q.empty ()) { for (int k = q.size (); k > 0 ; --k) { const auto cur = q.front (); q.pop (); for (const auto & v : adj[cur]) { if (visited.find (v) != visited.end ()) { return false ; } visited.emplace (v); q.emplace (v); adj[v].erase (cur); } } } return visited.size () == n; } };
time: $O(V + E)$ ➔ 必須拜訪所有 node, 和其延伸出去的所有 edge
space: $O(V + E)$ ➔ adj
Solution 3:
想法:利用 Union Find + Path Compression, 用 Union Find 合併每條邊的兩個 node 時, 如果這兩個 node 已經在同一 set 中, 則存在 cycle
e.g. edges = [[1,2], [1, 3], [2, 3]]
當完成 [1, 2]
和 [1, 3]
時, parents[2] = 1 = parents[3]
然後執行 [2, 3]
時, 因為 parents[2] == parents[3]
代表有 cycle
class Solution {public : bool validTree (int n, vector<vector<int >>& edges) { vector<int > parents (n, 0 ) ; vector<int > ranks (n, 1 ) ; for (int i = 0 ; i < n; ++i) { parents[i] = i; } res = n; for (const auto & e : edges) { const int r1 = find (parents, e[0 ]); const int r2 = find (parents, e[1 ]); if (r1 == r2) { return false ; } union_ (parents, ranks, r1, r2); } return res == 1 ; } private : int res; void union_ (vector<int >& parents, vector<int >& ranks, const int r1, const int r2) { if (ranks[r1] > ranks[r2]) { parents[r2] = r1; } else if (ranks[r2] > ranks[r1]) { parents[r1] = r2; } else { parents[r2] = r1; ranks[r1]++; } --res; } int find (vector<int >& parents, int i) { while (i != parents[i]) { parents[i] = parents[parents[i]]; i = parents[i]; } return i; } };
time: $O(V + E)$ ➔ 遍歷每個 node 初始化 parents
, 以及遍歷每條 edge 做 union find, 其中 edge 的兩端點做 find()
皆只需 $O(1)$
space: $O(n)$ ➔ parents
, ranks