Problem:

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.

For 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.

Show Hint

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.

General Analysis:

This problem should give you a good taste of magic algorithm. With the right algorithm, a problem could be solved so elegantly and concisely. My first solution is complex and wrong. 

Big picture, if a graph is a tree, it should possess following qualifications:
1. there is only one source node in the graph.
2. there is no circle in the graph.

Wrong Solution:

public class Solution {
public boolean validTree(int n, int[][] edges) {
if (edges == null)
throw new IllegalArgumentException("edges is null");
boolean[] flag = new boolean[n];
HashMap<Integer, ArrayList<Integer>> map = new HashMap<Integer, ArrayList<Integer>> ();
for (int[] edge : edges) {
flag[edge[1]] = true;
if (map.containsKey(edge[0])) {
map.get(edge[0]).add(edge[1]);
} else{
ArrayList<Integer> item = new ArrayList<Integer> ();
item.add(edge[1]);
map.put(edge[0], item);
}
}
int source = -1;
for(int i = 0; i < n; i++) {
if (!flag[i]) {
if (source == -1)
source = i;
else
return false;
}
}
if (source == -1)
return false;
Arrays.fill(flag, false);
Queue<Integer> queue = new LinkedList<Integer> ();
queue.offer(source);
while (!queue.isEmpty()) {
int cur = queue.poll();
if (flag[cur])
return false;
flag[cur] = true;
if (map.containsKey(cur)) {
ArrayList<Integer> ends = map.get(cur);
for (int end : ends)
queue.offer(end);
}
}
return true;
}
}

Mistake Analysis:

Error cases:
The above solution try to use BFS traversal over the graph to identify if there is a circle in the graph. However, the solution ignores a very important fact: the graph is undirected rather than directed. When means[0 1] [1 0] is actually the same edge, they could be used interchangely. Thus
1. You could not base on whether a node appears on the left side or not, to decide whether it's a source node.
2. Also, you could not based on it to detect a cycle.
The above method only works for Directed Graph.

Analysis:

Actually for Undireted Grpah, we have a classic algorithm to test if there is a circle in the graph. And how many independent sets are there in a graph.

The classic method is called: Union-Find.
Idea: Imagine we could divide the whole grpah into separate sets. (there is no edge to connect two sets). When we read an edge: [start end] from the edge array, it means those two edges actually in the same set, we enlarge the set. If both "start" and "end" have already appeared in the same set before we use the edge, it means a circle was detected. Key skills in the implementation:
1. How to record each node's set? Could it be very complex?
Absolutely no! A node's beloning set could be recored through a int array.
------------------------------------------------------------------------
int[] union = new int[n];
------------------------------------------------------------------------
Before we absorb any edge in the edges array, we treat each node as an independent set. The set number is its own node number.
------------------------------------------------------------------------
Arrays.fill(union, -1);
...
private int findUnion(int[] union, int i) {
if (union[i] == -1)
return i;
...
}
Note : -1 means it has not been connected with any set yet.
------------------------------------------------------------------------ 2. How to find a node's belonging set?
We could use DFS method to find a node's beloning set. private int findUnion(int[] union, int i) {
if (union[i] == -1)
return i;
return findUnion(union, union[i]);
} Note: the way to update union.
for (int[] edge : edges) {
int x = findUnion(union, edge[0]);
int y = findUnion(union, edge[1]);
...
union[y] = x;
}
We can trace a node's directly connected node back, so as to find out the set's no. Note: the set's no is not fixed, it could be affected by the [start, end]'s order, but it does not matter. After a edge was updated, we still could trace through any node back to a same node(it is number is the temporay set no). Suppose we have already known "node_m" in the set 0. we update the union array through following way:
int x = findUnion(union, edge[0]);
int y = findUnion(union, edge[1]);
union[y] = x; Case 1: [node_m node_n]
case 2: [node_n node_m] for case 1:
x = findUnion(union, node_m) = 0;
y = findUnion(union, node_n) = node_n;
union[node_n] = 0;
When we use DFS method, we can track node_n's set no is 0. for case 2:
x = findUnion(union, node_n) = node_n;
y = findUnion(union, node_m) = 0;
union[0] = node_n
When we use DFS method, we can track all nodes in the set back to node_n. Haha!!!So tricky and powerful!
Thus, we could easily detect if there is a circle in the graph.
if (x == y)
return false; 3. How to detect if there are more than two independt sets in the graph?
You may think about through counting how many elements in the union array is "-1". Acutally, we should use a property of tree to answer this question very quickly.
For a tree with N nodes in total, it must have N-1 edges.
return edges.length == n - 1;
If there are more than N-1 edges, it must have circle.
If there are edges < N-1, there are must more than 1 sets(more than two sources).

Solution:

public class Solution {
public boolean validTree(int n, int[][] edges) {
if (edges == null)
throw new IllegalArgumentException("edges is null");
int[] union = new int[n];
Arrays.fill(union, -1);
for (int[] edge : edges) {
int x = findUnion(union, edge[0]);
int y = findUnion(union, edge[1]);
if (x == y)
return false;
union[y] = x;
}
return edges.length == n - 1;
} private int findUnion(int[] union, int i) {
if (union[i] == -1)
return i;
return findUnion(union, union[i]);
}
}

[LeetCode#261] Graph Valid Tree的更多相关文章

  1. [LeetCode] 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), ...

  2. [LeetCode] 261. Graph Valid Tree _ Medium tag: BFS

    Given n nodes labeled from 0 to n-1 and a list of undirected edges (each edge is a pair of nodes), w ...

  3. 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 nod ...

  4. [Locked] Graph Valid Tree

    Graph Valid Tree Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is ...

  5. [LeetCode] 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), ...

  6. LeetCode Graph Valid Tree

    原题链接在这里:https://leetcode.com/problems/graph-valid-tree/ 题目: Given n nodes labeled from 0 to n - 1 an ...

  7. Leetcode: Graph Valid Tree && Summary: Detect cycle in undirected graph

    Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), ...

  8. Graph Valid Tree -- LeetCode

    Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), ...

  9. [Swift]LeetCode261.图验证树 $ 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), ...

随机推荐

  1. Linux Shell删除某一个目录下的所有文件夹(保留其他文件)

    #!/bin/bash direc=$(pwd) for dir2del in $direc/* ; do if [ -d $dir2del ]; then rm -rf $dir2del fi do ...

  2. css动画结束后 js无法修改translated值 .

    由于项目的需要,俺要做一些页面的转场动画. 即将是移动端,肯定是首先css动画了. 结果确发现,css动画中,如果设置animation-fill-mode: both;在动画结束后无法个性trans ...

  3. Ajax无刷新提交表单和显示

    ajax无刷新表单提交:   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "htt ...

  4. HTML语言语法大全

    (文章转载至博客园 dodo-yufan) <! - - ... - -> 註解 <!> 跑馬燈 <marquee>...</marquee>普通捲動  ...

  5. jmeter,监控插件

    1.下载JMeterPlugins.jar 2.下载后放在\apache-jmeter-3.0\lib\ext下 3.重启jmeter,监听器中即可看到jp@gc-开头的监听器

  6. P2P之UDP穿透NAT原理

    首先先介绍一些基本概念:             NAT(Network   Address   Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的,它的主要目的就 ...

  7. C#调用ActiveX控件

    背景:最近项目中需要用到ActiveX控件,项目是在.Net平台下开发的.因此就直接在项目中添加了对ActiveX控件的引用,添加引用成功.在代码中实例化类的实例也没有问题,但在调用其方法或属性时总是 ...

  8. 组策略彻底解决windows 2003 终端数

         win2003的话可以从组策略修改: 组策略级别要高于终端服务配置,当启用组策略后终端服务配置中的相应选项会变成灰色不可修改 运行-gpedit.msc-计算机配置-管理模板-Windows ...

  9. 从零开始 WIN8.1 下Android 开发环境搭建

    一.JDK安装 当前最新版本是JDK8.0 地址http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-21331 ...

  10. java_设计模式_组合模式_Composite Pattern(2016-08-12)

    概念: 组合模式(Composite Pattern)将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性. 有时候又叫做部分-整体模式,它使我们树 ...