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. ASP.NET中常用重置数据的方法

    aspx: <asp:Repeater ID="rptProlist" runat="server" onitemdatabound="rptP ...

  2. 多线程(Thread),其实很简单!

    目录:  1:线程简介 2:怎么操作线程      3:Thread的常用方法 4:简单的获奖机     5:应用程序域   线程:是Windows任务调度的最小单位.线程是程序中的一个执行流,每个线 ...

  3. JS2 for应用

    for应用  再谈js获取元素一二: var oUl=document.getElementById('list');      //静态方法 var oUl=document.getElements ...

  4. .NET设计模式(10):装饰模式(Decorator Pattern)

      .NET设计模式(10):装饰模式(Decorator Pattern)   装饰模式(Decorator Pattern) --.NET设计模式系列之十 年月..在....对于..由于使用装饰模 ...

  5. Eclipse闪退/打不开/无法启动/一闪而过

    转自:http://my.oschina.net/psuyun/blog/421058 很长时间了,写java.写android都是用的Eclipse.可是突然有一天,当我像往常一样试图打开Eclip ...

  6. 【Windows】Windows中的数据类型以及命名

    一.大写标示符 Windows中的很多标识符都是以两个或者三个大写字母作为前缀的,且其后紧跟一个下划线.这些标识符都是常量数值,前缀表明该常量的一般类别.如下 前缀 常量 CS(Class Style ...

  7. Oracle学习【索引及触发器】

    索引B_Tree结构 请参照 响应图例 索引是一种允许直接访问数据表中某一数据行的树形结构,为了提高查询效率而引入,是独立于表的对象,可以存放在与表不同的表空间中.索引记录中存有索引关键字和指向表中数 ...

  8. boost::xml——基本操作以及中文乱码解决方案 (续)

    本博文主要想说明以下两点: 1.对于上一篇的<boost::xml——基本操作以及中文乱码解决方案>解释,这篇博文基本解决了正确输入输出中英文问题,但是好像还没有解决修改中文出现乱码的问题 ...

  9. clipboard让复制的文本换行

    https://clipboardjs.com/dist/clipboard.min.js 用clipboard实现复制时, 想让复制的文本换行, 有两咱方法: 第一种, HTML实现: <!- ...

  10. jquery data方法取值与js attr取值的区别

    <a data-v="3"></a> jquery data方法的运行机制: 第一次查找dom,使用attributes获取到dom节点值,并将其值存到缓存 ...