There is a new alien language which uses the latin alphabet. However, the order among letters are unknown to you. You receive a list of words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language.

For example,
Given the following words in dictionary, [
"wrt",
"wrf",
"er",
"ett",
"rftt"
]
The correct order is: "wertf". Note:
You may assume all letters are in lowercase.
If the order is invalid, return an empty string.
There may be multiple valid order of letters, return any one of them is fine.

参考了http://segmentfault.com/a/1190000003795463

拓扑排序

复杂度

时间 O(N) 空间 O(N)

思路

首先简单介绍一下拓扑排序,这是一个能够找出有向无环图(DAG: Directed Acyclic Graph)顺序的一个方法, 具体例子可以参见这个视频

假设我们有3条边:A->C, B->C, C->D,先将每个节点的计数器初始化为0。然后我们对遍历边时,每遇到一个边,把目的节点的计数器都加1。然后,我们再遍历一遍,找出所有计数器值还是0的节点,这些节点就是有向无环图的“根”。然后我们从根开始广度优先搜索。具体来说,搜索到某个节点时,将该节点加入结果中,然后所有被该节点指向的节点的计数器减1,在减1之后,如果某个被指向节点的计数器变成0了,那这个被指向的节点就是该节点下轮搜索的子节点。在实现的角度来看,我们可以用一个队列,这样每次从队列头拿出来一个加入结果中,同时把被这个节点指向的节点中计数器值减到0的节点也都加入队列尾中。需要注意的是,如果图是有环的,则计数器会产生断层,即某个节点的计数器永远无法清零(有环意味着有的节点被多加了1,然而遍历的时候一次只减一个1,所以导致无法归零),这样该节点也无法加入到结果中。所以我们只要判断这个结果的节点数和实际图中节点数相等,就代表无环,不相等,则代表有环。

对于这题来说,我们首先要初始化所有节点(即字母),一个是该字母指向的字母的集合(被指向的字母在字母表中处于较后的位置),一个是该字母的计数器。然后我们根据字典开始建图,但是字典中并没有显示给出边的情况,如何根据字典建图呢?其实边都暗藏在相邻两个词之间,比如abcabd,我们比较两个词的每一位,直到第一个不一样的字母cd,因为abd这个词在后面,所以d在字母表中应该是在c的后面。所以每两个相邻的词都能蕴含一条边的信息。在建图的同时,实际上我们也可以计数了,对于每条边,将较后的字母的计数器加1。计数时需要注意的是,我们不能将同样一条边计数两次,所以要用一个集合来排除已经计数过的边。最后,我们开始拓扑排序,从计数器为0的字母开始广度优先搜索。为了找到这些计数器为0的字母,我们还需要先遍历一遍所有的计数器。

最后,根据结果的字母个数和图中所有字母的个数,判断时候有环即可。无环直接返回结果。

注意

  • 因为这题代码很冗长,面试的时候最好都把几个大步骤都写成子函数,先完成主函数,再实现各个子函数,比如初始化图,建图,加边,排序,都可以分开

  • 要先对字典里所有存在的字母初始化入度为0,否则之后建图可能会漏掉一些没有入度的字母

  • 'a'+'b'+""'a'+""+'b'是不一样的,前者先算数字和,后者则是字符串拼接

  • 因为字典里有重复的边,所有要先判断,已经添加过的边不要重复添加

以上是博客原文,我的理解的Topological Sorting做法是:

维护一个graph, 一个indegree; 本例是HashMap<Character, HashSet<Character>> graph 以及 HashMap<Character, Integer> indegree

1. 先遍历边,每遇到一个边,目的节点indegree计数器+1;

2. 再遍历点,找出所有indegree计数器是0的节点,全部入队列。

3. 然后从队列中开始poll元素,每poll一个出来,加入到结果中, 然后所有被该节点指向的节点的indegree计数器减1,在减1之后,如果某个被指向节点的计数器变成0了,把该节点入队列

4. 如此直到队列变空,如果该图不存在cycle,则结果就是就是sort该DAG的一个顺序。如果图存在cycle,则队列也会变空,但是一些节点永远无法被加入到队列中,也无法被加入结果

该题我错了的地方:

1. 47行忘了break

2. 59行不该break却break了

 public class Solution {
public String alienOrder(String[] words) {
HashMap<Character, HashSet<Character>> graph = new HashMap<Character, HashSet<Character>>();
HashMap<Character, Integer> indegree = new HashMap<Character, Integer>();
String order = "";
initialization(words, graph, indegree); buildGraphCountIndegree(words, graph, indegree); order = topologicalSort(graph, indegree); return (order.length()==indegree.size())? order : "";
} public void initialization(String[] words, HashMap<Character, HashSet<Character>> graph, HashMap<Character, Integer> indegree) {
for (String each : words) {
for (int t=0; t<each.length(); t++) {
if (!graph.containsKey(each.charAt(t))) {
graph.put(each.charAt(t), new HashSet<Character>());
}
if (!indegree.containsKey(each.charAt(t))) {
indegree.put(each.charAt(t), 0);
}
}
}
} public void buildGraphCountIndegree(String[] words, HashMap<Character, HashSet<Character>> graph, HashMap<Character, Integer> indegree) {
HashSet<String> edges = new HashSet<String>();
for (int i=0; i<words.length-1; i++) {
String word1 = words[i];
String word2 = words[i+1];
for (int j=0; j<word1.length()&&j<word2.length(); j++) {
if (word1.charAt(j) == word2.charAt(j)) continue;
char from = word1.charAt(j);
char to = word2.charAt(j);
String edge = from + "" + to;
if (!edges.contains(edge)) {
// add to node to from node's adjacent set
graph.get(from).add(to); // increase to node's indegree by 1
indegree.put(to, indegree.get(to)+1); // add the edge to visited set
edges.add(edge);
break;
}
}
}
} public String topologicalSort(HashMap<Character, HashSet<Character>> graph, HashMap<Character, Integer> indegree) {
StringBuffer res = new StringBuffer();
LinkedList<Character> queue = new LinkedList<Character>();
for (Character key : indegree.keySet()) {
if (indegree.get(key) == 0) {
queue.offer(key);
//break;
}
} while (!queue.isEmpty()) {
Character cur = queue.poll();
res.append(cur.charValue());
HashSet<Character> adjList = graph.get(cur);
if (adjList != null) {
for (Character dst : adjList) {
int dstIndegree = indegree.get(dst);
dstIndegree--;
if (dstIndegree == 0) queue.offer(dst);
indegree.put(dst, dstIndegree);
}
}
}
return res.toString();
}
}

Leetcode: Alien Dictionary && Summary: Topological Sort的更多相关文章

  1. LeetCode Alien Dictionary

    原题链接在这里:https://leetcode.com/problems/alien-dictionary/ 题目: There is a new alien language which uses ...

  2. [LeetCode] Alien Dictionary 另类字典

    There is a new alien language which uses the latin alphabet. However, the order among letters are un ...

  3. [LeetCode] 269. Alien Dictionary 外文字典

    There is a new alien language which uses the latin alphabet. However, the order among letters are un ...

  4. LeetCode 269. Alien Dictionary

    原题链接在这里:https://leetcode.com/problems/alien-dictionary/ 题目: There is a new alien language which uses ...

  5. LeetCode编程训练 - 拓扑排序(Topological Sort)

    拓扑排序基础 拓扑排序用于解决有向无环图(DAG,Directed Acyclic Graph)按依赖关系排线性序列问题,直白地说解决这样的问题:有一组数据,其中一些数据依赖其他,问能否按依赖关系排序 ...

  6. Alien Dictionary

    There is a new alien language which uses the latin alphabet. However, the order among letters are un ...

  7. 269. Alien Dictionary火星语字典(拓扑排序)

    [抄题]: There is a new alien language which uses the latin alphabet. However, the order among letters ...

  8. 算法与数据结构基础 - 拓扑排序(Topological Sort)

    拓扑排序基础 拓扑排序用于解决有向无环图(DAG,Directed Acyclic Graph)按依赖关系排线性序列问题,直白地说解决这样的问题:有一组数据,其中一些数据依赖其他,问能否按依赖关系排序 ...

  9. [Locked] Alien Dictionary

    Alien Dictionary There is a new alien language which uses the latin alphabet. However, the order amo ...

随机推荐

  1. Java:按值传递还是按引用传递详细解说

    前天在做系统的时候被Java中参数传递问题卡了一下,回头查阅了相关的资料,对参数传递问题有了新的了解和掌握,但是有个问题感觉还是很模糊,就是Java中到底是否只存在值传递,因为在查阅资料时,经常看到有 ...

  2. 【故障处理】ORA-12162: TNS:net service name is incorrectly specified (转)

    本文将给大家阐述一个因未设置系统环境变量ORACLE_SID导致ORA-12162错误的案例.希望大家有所思考. 1.获得有关ORA-12162报错信息的通用表述信息 [oracle@asdlabdb ...

  3. Bluetooth SDP介绍

    目录 1. 概念 2. 服务记录(Service Record) 3. 服务属性(Service Attribute) 4. 服务类(Service Class) 5. 服务查找 5.1 UUID 5 ...

  4. 树莓派如何便捷的使用pi4j

    问题的由来 pi4j用起来很方便,但是感觉pi4j库的命名太杂乱,啰嗦了,很容易弄混,而且好像没听说官方有自己的编译器.如果没有智能点的编辑器的话,写起来真要命,但是树莓派运行Eclipse不太现实, ...

  5. 20145211 《Java程序设计》第3周学习总结——绝知此事要躬行

    教材学习内容总结 4.1何为面向对象 面向对象,面向过程都是一种思想,没有高低之分.面向对象,就像是对冰箱操作,冰箱是一个介质,用法就像是c语言中的结构体,功能定义在对象上.面向对象,角色转变,让我们 ...

  6. iOS Node Conflict svn冲突

    当出现这个冲突时,应该是我add的文件,和同事处理的方法有冲突导致的. 这个的解决办法是:先revert这个文件,然后再update.

  7. 最简单的PC机串口通信程序

    把串口当作文件IO来操作,简单易行!    已验证,gcc和tcc都可以编译成功,并使用.  需注意,先有串口,改好红色字体串口号再编译运行! #include <stdio.h>  #i ...

  8. Jackson:fasterxml和codehaus的区别

    Jackson fasterxml和codehaus的区别: 它们是jackson的两个分支.也是两个版本的不同包名.jackson从2.0开始改用新的包名fasterxml:1.x版本的包名是cod ...

  9. http://blog.csdn.net/jiyiqinlovexx/article/details/38326865

    http://blog.csdn.net/jiyiqinlovexx/article/details/38326865

  10. box_shadow

    .tip{width:485px; height:260px; position:absolute;top:10%; left:30%;background:#fcfdfd; box-shadow:1 ...