261. Graph Valid Tree
Given n nodes labeled from 0 to n-1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.
Example 1:
Input: n = 5, and edges = [[0,1], [0,2], [0,3], [1,4]]
Output: true
Example 2:
Input: n = 5, and edges = [[0,1], [1,2], [2,3], [1,3], [1,4]]
Output: false
cinspiration:https://www.youtube.com/watch?v=vsIb9B84Rt8
思路:
valid tree需要满足:
- 除了跟节点,每个节点入度为1
- 只能有一个跟节点
- 不能有环 (有环也能出现每个节点入度为1的情况)
方法1: Union Find
思路:
建立一个祖先查询的vector,初始化为-1。当一个数的祖先为-1时说明它就是这一支/自己的祖先,否则要按照表中的数字继续向上查找。对每一对vertex,如果他们已经有相同的祖先,并且又被当前edge连起来,说明已经构成cycle,返回false。如果并不相连,在本循环中连起来,通过将x的祖先改为y:roots[x] = y。当循环过所有edge都没有被return false打断,说明没有出现cycle,此时需要再判断一步是否只存在一棵树。判断方法可以有两种:
- edges.size() == n- 1: 数的egde一定是|V| - 1条
- 是否只有一个祖先是-1 的节点
class Solution {
public:
bool validTree(int n, vector<pair<int, int>>& edges) {
//
vector<int> roots(n, -1);
for (auto edge: edges){
int x = find(roots, edge.first);
int y = find(roots, edge.second);
if (x == y) {
return false;
}
else {
roots[x] = y;
}
}
//int k = 0;
//for (int i = 0; i < n; i++){
// if (roots[i]==-1) k++;
//}
//return k == 1;
return edges.size() == n - 1;
}
int find(vector<int> & roots, int i){
while (roots[i] != -1){
i = roots[i];
}
return i;
}
};
方法2: dfs
grandyang: http://www.cnblogs.com/grandyang/p/5257919.html
思路:
建立双向图,用dfs来检测是否有环。如果有则返回false,如果从一个节点开始并没有visit过所有的节点,返回false。
这里需要一个prev来记住谁是上一个节点,主要是因为这里是双向图,不记住会返回上一个节点并无限循环。
而之所以需要建双向图是因为事先不知道child/parent
class Solution {
public:
bool validTree(int n, vector<pair<int, int>>& edges) {
vector<vector<int>> graph(n, vector<int>(0));
vector<int> visited(n, 0);
for (auto edge: edges){
graph[edge.first].push_back(edge.second);
graph[edge.second].push_back(edge.first);
}
if (dfs(0, graph, visited, -1)) return false;
for (auto i : visited){
if (i == 0) return false;
}
return true;
}
bool dfs(int i, vector<vector<int>>& graph, vector<int> &visited, int prev){
if (visited[i] == 1) return true;
visited[i] = 1;
for (int j: graph[i]){
if (j != prev && dfs(j, graph, visited, i)){
return true;
}
}
return false;
}
};
方法3: bfs
思路:
建立双向图,用bfs来检测是否有环。如果已经访问过则返回false,否则将邻居节点全部入队,并将该节点的反向edge从邻居节点映射中删除,作用等同于dfs中的prev操作。以任意节点起始,最后检查是否所有节点都visited过。
// BFS
class Solution {
public:
bool validTree(int n, vector<pair<int, int>>& edges) {
vector<unordered_set<int>> g(n, unordered_set<int>());
unordered_set<int> s{{0}};
queue<int> q{{0}};
for (auto a : edges) {
g[a.first].insert(a.second);
g[a.second].insert(a.first);
}
while (!q.empty()) {
int t = q.front(); q.pop();
for (auto a : g[t]) {
if (s.count(a)) return false;
s.insert(a);
q.push(a);
g[a].erase(t);
}
}
return s.size() == n;
}
};
方法4: topological sort
思路:
这道题也能用topological sort来解,主要就是,要忽视双向途中造成的环(用同样的prev trick),其他一切有环都返回fasle。最后判断是否还有没有visited的vertex。基本和dfs的方法是一样的。
class Solution {
public:
bool validTree(int n, vector<vector<int>>& edges) {
vector<vector<int>> graph(n, vector<int>(0));
vector<int> visited(n, 0);
for (auto e: edges) {
graph[e[0]].push_back(e[1]);
graph[e[1]].push_back(e[0]);
}
if (dfs(graph, visited, 0, -1)) return false;
for (auto i: visited) {
if (i == 0) return false;
}
return true;
}
bool dfs(vector<vector<int>> & graph, vector<int> & visited, int i, int prev) {
if (visited[i] == 2) return false;
if (visited[i] == 1) return true;
visited[i] = 1;
for (auto j: graph[i]) {
if (j != prev && dfs(graph, visited, j, i)){
return true;
}
}
visited[i] = 2;
return false;
}
};