Tree & Undirected Graph
According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.”
For an undirected graph to be a valid tree
- no cycle
- the number of tree/components should be 1. If no cycle and no duplicated edges, #edges equals #vertices - 1
One intuitive approach is using graph traversal to detect cycle and count #components. But a more efficient way is using Union-Find to detect cycle.
Union-Find is more efficient only in the adjacency list representation.
** If the graph is represented in adjacency matrix, union find might need $$O(n^3)$$in worst case. We traverse over the complete matrix once. Union and find operations take $$O(n)$$ time in the worst case.
Union-Find approach:
class Solution {
public boolean validTree(int n, int[][] edges) {
//more than one connected components/tree
if(n-1 != edges.length) return false;
int[] nums = new int[n]; //parent list for union find
Arrays.fill(nums, -1);
for(int[] edge: edges) {
int x = find(nums, edge[0]);
int y = find(nums, edge[1]);
if(x==y) return false; //the two end of the edge belongs to the same set.
//union
nums[x] = y;
}
return true;
}
public int find(int[] nums, int i) {
if(nums[i]==-1) return i; // the number is alone
return find(nums, nums[i]); //keep searhing its parent/set representative
}
}
Graph DFS approach:
class Solution {
private int[] status;
private List<Integer>[] graph;
public boolean validTree(int n, int[][] edges) {
//represent the graph in adjacency list
graph = new ArrayList[n];
//0 is unvisited, 1 is visited, 2 is done
status = new int[n];
for(int i = 0; i < n; i++){
graph[i] = new ArrayList<Integer>();
}
for(int[] edge: edges) {
graph[edge[0]].add(edge[1]);
graph[edge[1]].add(edge[0]);
}
int numTree=0; //the DFS may produce a forest of trees
for(int i=0; i<n; i++) {
if(status[i]==0) {
numTree++;
if(DFSDetectCycle(i, -1)) return false;
}
}
//check componenet
return numTree==1;
}
public boolean DFSDetectCycle(int v, int parent) {
status[v] = 1;
boolean hasCycle = false;
for(Integer child:graph[v]) {
if(child!=parent) { // it is undirectional graph, we need to avoid going back from where we are from in DFS
System.out.println(child);
System.out.println("status: "+ status[child]);
if(status[child]==0) {
hasCycle = DFSDetectCycle(child, v);
} else if (status[child]==2) {
} else {
hasCycle = true; //cycle detected;
}
}
if(hasCycle) break;
}
status[v] = 2;
return hasCycle;
}
}