[LintCode] 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.

You can assume that no duplicate edges will appear in edges. Since all edges are undirected[0, 1] is the same as [1, 0] and thus will not appear together in edges.

Example

Given n = 5 and edges = [[0, 1], [0, 2], [0, 3], [1, 4]], return true.

Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], return false.

 

Algorithm:

For an undirected graph of n vertices, if the number of edges is not n - 1, then this graph can not be a valid tree. This graph is either cyclic in the case of more than n - 1 edges, or disconnected in the case of fewer than n - 1 edges. If there are n - 1 edges, then further check is needed to detect if there is any cycle in the graph. If there is a cycle, then this graph is not a valid tree. If there is no cycles, then it is a valid tree.

 

Solution 1. BFS, O(V + E) runtime, O(V + E) space 

Start from a random node and do a BFS, if all vertices can be reached then we know there can not be any cycles, we have a valid tree.

 1 public class Solution {
 2     public boolean validTree(int n, int[][] edges) {
 3         if(edges.length != n - 1)
 4         {
 5             return false;
 6         }
 7         HashMap<Integer, HashSet<Integer>> graph = initializeGraph(n, edges);
 8         
 9         //bfs
10         Queue<Integer> queue = new LinkedList<Integer>();
11         HashSet<Integer> visitedSet = new HashSet<Integer>();
12         
13         queue.offer(0);
14         visitedSet.add(0);
15         int visitedNodes = 0;
16         while(!queue.isEmpty())
17         {
18             int node = queue.poll();
19             visitedNodes++;
20             for(Integer neighbor : graph.get(node))
21             {
22                 if(visitedSet.contains(neighbor))
23                 {
24                     continue;
25                 }
26                 visitedSet.add(neighbor);
27                 queue.offer(neighbor);
28             }
29         }
30         return visitedNodes == n;
31     }
32 }

 

Solution 2. DFS to count all reachable nodes, O(V + E) runtime, O(V + E) space for graph representation, O(n) for the set of visited nodes. The recursion stack space usage should be taken into account too.

 1 public class Solution {
 2     public boolean validTree(int n, int[][] edges) {
 3         if(edges.length != n - 1)
 4         {
 5             return false;
 6         }
 7         HashMap<Integer, HashSet<Integer>> graph = initializeGraph(n, edges);
 8         HashSet<Integer> visited = new HashSet<Integer>();
 9         visited.add(0);
10         dfs(0, graph, visited);
11         return visited.size() == n;   
12     }
13     private HashMap<Integer, HashSet<Integer>> initializeGraph(int n, int[][] edges)
14     {
15         HashMap<Integer, HashSet<Integer>> graph = new HashMap<Integer, HashSet<Integer>>();
16         for(int i = 0; i < n; i++)
17         {
18             graph.put(i, new HashSet<Integer>());
19         }
20         for(int i = 0; i < edges.length; i++)
21         {
22             int u = edges[i][0];
23             int v = edges[i][1];
24             graph.get(u).add(v);
25             graph.get(v).add(u);
26         }
27         return graph;
28     }
29     private void dfs(int startNode, 
30                     HashMap<Integer, HashSet<Integer>> graph,
31                     HashSet<Integer> visited){
32         for(Integer neighbor : graph.get(startNode)){
33             if(!visited.contains(neighbor)){
34                 visited.add(neighbor);
35                 dfs(neighbor, graph, visited);
36             }
37         }        
38     }
39 }

 

Solution 3. DFS to check if there is any cycle

 1 //Algorithm 3. dfs all nodes to check if there is a cycle in this graph. 
 2 //O(m + n) time, O(m + n) space, if not considering the recursion space usage
 3 public class Solution {
 4     public boolean validTree(int n, int[][] edges) {
 5         if(n == 0 || edges.length != n - 1)
 6         {
 7             return false;
 8         }
 9         HashMap<Integer, HashSet<Integer>> graph = initializeGraph(n, edges);
10         return hasCycleDFS(graph) == false;
11     }
12     private HashMap<Integer, HashSet<Integer>> initializeGraph(int n, int[][] edges)
13     {
14         HashMap<Integer, HashSet<Integer>> graph = new HashMap<Integer, HashSet<Integer>>();
15         for(int i = 0; i < n; i++)
16         {
17             graph.put(i, new HashSet<Integer>());
18         }
19         for(int i = 0; i < edges.length; i++)
20         {
21             int u = edges[i][0];
22             int v = edges[i][1];
23             graph.get(u).add(v);
24             graph.get(v).add(u);
25         }
26         return graph;
27     }
28     private boolean hasCycleDFS(HashMap<Integer, HashSet<Integer>> graph){
29         HashSet<Integer> visited = new HashSet<Integer>();
30         for(Integer node : graph.keySet()){
31             if(!visited.contains(node)){
32                 boolean flag = hasCycleDFSUtil(node, visited, -1, graph);
33                 if(flag){
34                     return true;
35                 }
36             }
37         }
38         return false;
39     }
40     private boolean hasCycleDFSUtil(int node, 
41                                     HashSet<Integer> visited, 
42                                     int parent,
43                                     HashMap<Integer, HashSet<Integer>> graph){
44         visited.add(node);
45         for(Integer neighbor : graph.get(node)){
46             //if we skil this check, then when the logic check its parent node
47             //it will mistakenly think a cycle is found. 
48             //e.g, A -- B -- C, start dfs from A. When visiting C, its parent B
49             //has been added to the visited set, if we only rely on the visited.contains(B)
50             //check, it will cause this method return true. We should only return true
51             //when its neighbor has been visited and this neighbor is not its parent node
52             if(neighbor == parent){
53                 continue;
54             }    
55             if(visited.contains(neighbor)){
56                 return true;
57             }
58             boolean hasCycle = hasCycleDFSUtil(neighbor, visited, node, graph);
59             if(hasCycle){
60                 return true;
61             }
62         }
63         return false;
64     }
65 }

 

Solution 4. Union Find

 1 //Algorithm 4.  Union Find, O(m + n) time, O(n) space, m is the number of edges
 2 // n is the number of nodes
 3 class UnionFind {
 4     private int[] father = null;
 5     private int count = 0;
 6     public UnionFind(int n){
 7         father = new int[n];
 8         count = n;
 9         for(int i = 0; i < n; i++){
10             father[i] = i;
11         }
12     }
13     public int find(int x){
14         if(father[x] == x){
15             return x;
16         }
17         return father[x] = find(father[x]);
18     }
19     public void connect(int a, int b){
20         int root_a = find(a);
21         int root_b = find(b);
22         if(root_a != root_b){
23             father[root_a] = father[root_b];
24             count--;
25         }
26     }
27     public int queryCount(){
28         return count;
29     }
30 }    
31 public class Solution {
32     public boolean validTree(int n, int[][] edges) {
33         if(edges.length != n - 1)
34         {
35             return false;
36         }
37         UnionFind uf = new UnionFind(n);
38         for(int i = 0; i < edges.length; i++){
39             uf.connect(edges[i][0], edges[i][1]);
40         }
41         return uf.queryCount() == 1;
42     }
43 }

 

 

 

Related Problems

Connecting Graph

Connected Component in Undirected Graph

posted @ 2017-11-10 13:06  Review->Improve  阅读(531)  评论(0编辑  收藏  举报