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