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. JS实现图片宽高的等比缩放

    关于图片宽高的等比缩放,其实需求就是让图片自适应父容器的宽高,并且是等比缩放图片,使图片不变形. 例如,需要实现如下的效果: 要实现上面的效果,需要知道图片的宽高,父容器的宽高,然后计算缩放后的宽高. ...

  2. 如何创建一个自己的【Composer/Packagist】包

    首先让我们踏着欢快的脚步去Github创建一个新库,这里取名 composer-car,又欢快的将它克隆到本地: $ git clone git@github.com:victorruan/compo ...

  3. CI框架篇之类库篇--基础(1)

    使用 CodeIgniter 类库: 所有的类库文件存放在system/libraries 文件夹.大多数情况下你需要预先在controller中初始化后才能使用它们: $this->load- ...

  4. HUST 4681 String (DP LCS变形)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4681 题目大意:给定三个字符串A,B,C 求最长的串D,要求(1)D是A的字序列 (2)D是B的子序列 ...

  5. C++文件操作详解(ifstream、ofstream、fstream)

    C++ 通过以下几个类支持文件的输入输出: ofstream: 写操作(输出)的文件类 (由ostream引申而来) ifstream: 读操作(输入)的文件类(由istream引申而来) fstre ...

  6. ubuntu1404安装配置java环境(jdk8)

    这个安装比较简单,网上也有数不清的教学,我这里记录以下,方便以后万一失忆了回来看看能想起来.个人博客http://www.cnblogs.com/wdfwolf3/ 1.下载安装 言归正传,我们需要到 ...

  7. 读书笔记之 - javascript 设计模式 - 装饰者模式

    本章讨论的是一种为对象增添特性的技术,它并不使用创建新子类这种手段. 装饰者模式可以透明地把对象包装在具有同样接口的另一对象之中,这样一来,你可以给一些方法添加一些行为,然后将方法调用传递给原始对象. ...

  8. MYSQL 不排序

    mysql: SELECT * FROM EVENT WHERE eventId IN(443,419,431,440,420,414,509) ORDER BY INSTR(',443,419,43 ...

  9. JavaScript Iframe富文本编辑器中的光标定位

    最近在项目中碰到一个比较棘手的问题: 在iframe富文本编辑器中,有个工具栏,这个工具栏在iframe标签之外,工具栏上有一个按钮,点击该按钮向iframe正在编辑中的光标处插入一个图片,图片会插入 ...

  10. Hadoop, Python, and NoSQL lead the pack for big data jobs

    Hadoop, Python, and NoSQL lead the pack for big data jobs   Rise in cloud-based analytics could incr ...