1+ package  com .leetcode .graphs ;
2+ 
3+ import  java .util .ArrayList ;
4+ import  java .util .List ;
5+ 
6+ import  static  org .junit .jupiter .api .Assertions .assertFalse ;
7+ import  static  org .junit .jupiter .api .Assertions .assertTrue ;
8+ 
9+ /** 
10+  * Level: Medium 
11+  * Problem Link: https://leetcode.com/problems/graph-valid-tree/ 
12+  * Problem Description: 
13+  * 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 
14+  * to check whether these edges make up a valid tree. 
15+  * <p> 
16+  * Example 1: 
17+  * Input: n = 5, and edges = [[0,1], [0,2], [0,3], [1,4]] 
18+  * Output: true 
19+  * <p> 
20+  * Example 2: 
21+  * Input: n = 5, and edges = [[0,1], [1,2], [2,3], [1,3], [1,4]] 
22+  * Output: false 
23+  * <p> 
24+  * Note: you can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0,1] is the 
25+  * same as [1,0] and thus will not appear together in edges. 
26+  * 
27+  * @author rampatra 
28+  * @since 2019-08-05 
29+  */ 
30+ public  class  GraphValidTree  {
31+ 
32+     /** 
33+      * 
34+      * @param n 
35+      * @param edges 
36+      * @return 
37+      */ 
38+     public  static  boolean  isValidTree (int  n , int [][] edges ) {
39+         List <List <Integer >> adjacencyList  = new  ArrayList <>(n );
40+ 
41+         for  (int  i  = 0 ; i  < n ; i ++) {
42+             adjacencyList .add (new  ArrayList <>());
43+         }
44+ 
45+         for  (int  i  = 0 ; i  < edges .length ; i ++) {
46+             adjacencyList .get (edges [i ][0 ]).add (edges [i ][1 ]);
47+         }
48+ 
49+         boolean [] visited  = new  boolean [n ];
50+ 
51+         if  (hasCycle (adjacencyList , 0 , -1 , visited )) {
52+             return  false ;
53+         }
54+ 
55+         for  (int  i  = 0 ; i  < n ; i ++) {
56+             if  (!visited [i ]) {
57+                 return  false ;
58+             }
59+         }
60+ 
61+         return  true ;
62+     }
63+ 
64+     private  static  boolean  hasCycle (List <List <Integer >> adjacencyList , int  node1 , int  exclude , boolean [] visited ) {
65+         visited [node1 ] = true ;
66+ 
67+         for  (int  i  = 0 ; i  < adjacencyList .get (node1 ).size (); i ++) {
68+             int  node2  = adjacencyList .get (node1 ).get (i );
69+ 
70+             if  ((visited [node2 ] && exclude  != node2 ) || (!visited [node2 ] && hasCycle (adjacencyList , node2 , node1 , visited ))) {
71+                 return  true ;
72+             }
73+         }
74+ 
75+         return  false ;
76+     }
77+ 
78+ 
79+     /** 
80+      * Union-find algorithm: We keep all connected nodes in one set in the union operation and in find operation we 
81+      * check whether two nodes belong to the same set. If yes then there's a cycle and if not then no cycle. 
82+      * 
83+      * Good articles on union-find: 
84+      * - https://www.hackerearth.com/practice/notes/disjoint-set-union-union-find/ 
85+      * - https://www.youtube.com/watch?v=wU6udHRIkcc 
86+      * 
87+      * @param n 
88+      * @param edges 
89+      * @return 
90+      */ 
91+     public  static  boolean  isValidTreeUsingUnionFind (int  n , int [][] edges ) {
92+         int [] roots  = new  int [n ];
93+ 
94+         for  (int  i  = 0 ; i  < n ; i ++) {
95+             roots [i ] = i ;
96+         }
97+ 
98+         for  (int  i  = 0 ; i  < edges .length ; i ++) {
99+             // find operation 
100+             if  (roots [edges [i ][0 ]] == roots [edges [i ][1 ]]) {
101+                 return  false ;
102+             }
103+             // union operation 
104+             roots [edges [i ][1 ]] = findRoot (roots , roots [edges [i ][0 ]]); // note: we can optimize this even further by 
105+             // considering size of each side and then join the side with smaller size to the one with a larger size (weighted union). 
106+             // We can use another array called size to keep count of the size or we can use the same root array with 
107+             // negative values, i.e, negative resembles that the node is pointing to itself and the number will represent 
108+             // the size. For example, roots = [-2, -1, -1, 0] means that node 3 is pointing to node 0 and node 0 is pointing 
109+             // to itself and is has 2 nodes under it including itself. 
110+         }
111+ 
112+         return  edges .length  == n  - 1 ;
113+     }
114+ 
115+     private  static  int  findRoot (int [] roots , int  node ) {
116+         while  (roots [node ] != node ) {
117+             node  = roots [node ];
118+         }
119+         return  node ;
120+     }
121+ 
122+     public  static  void  main (String [] args ) {
123+         assertTrue (isValidTree (5 , new  int [][]{{0 , 1 }, {0 , 2 }, {0 , 3 }, {1 , 4 }}));
124+         assertFalse (isValidTree (5 , new  int [][]{{0 , 1 }, {1 , 2 }, {2 , 3 }, {1 , 3 }, {1 , 4 }}));
125+         assertFalse (isValidTree (3 , new  int [][]{{0 , 1 }, {1 , 2 }, {2 , 0 }}));
126+ 
127+         assertTrue (isValidTreeUsingUnionFind (5 , new  int [][]{{0 , 1 }, {0 , 2 }, {0 , 3 }, {1 , 4 }}));
128+         assertFalse (isValidTreeUsingUnionFind (5 , new  int [][]{{0 , 1 }, {1 , 2 }, {2 , 3 }, {1 , 3 }, {1 , 4 }}));
129+         assertFalse (isValidTreeUsingUnionFind (3 , new  int [][]{{0 , 1 }, {1 , 2 }, {2 , 0 }}));
130+     }
131+ }
0 commit comments