# 算法专题：Topological Sort

1. BFS
拓扑排序的BFS方法，步骤如下：
I.遍历图，计算每个节点的入度（in order/degree），所谓入度，就是有几个其他节点有进入这个节点的边。很明显，入度为0的节点，没有前驱节点，因此在拓扑排序里面应该放在最前面。假如有好几个这种0入度的顶点，它们的顺序是无所谓的。这里把它们放入队列，同时也放入结果数组。
II.逐个删除队列里的0入度点，然后对于和其相连的顶点，入度-1。其实就相当于把这些顶点从图上面删掉。
III.再次检查图，把新的0入度的点加入队列和结果。
IV.重复以上过程，直至图为空。
可以看到，BFS方法就相当于每次把最外层的顶点给去除，一层一层剥下来。代码：
``````class DirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []

from collections import deque
class Solution:
def topBFS(self, graph):
countrd = {}
for x in graph:
countrd[x] = 0

for i in graph:
for j in i.neighbors:
countrd[j] = countrd[j] + 1

ans = []
zeroDegree = deque()
for i in graph:
if countrd[i] == 0:
ans.append(i)
zeroDegree.append(i)

while zeroDegree:
i = zeroDegree.pop()
countrd[i] -= 1
for n in i.neighbors:
countrd[n] -= 1
if countrd[n] == 0:
ans.append(n)
zeroDegree.appendleft(n)

return ans
``````

2.DFS
DFS其实和BFS有相似的地方，也是围绕入度来实现，只不过是先一条线到底：

``````class DirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []

class Solution:
def dfs(self, i, countrd, ans):
ans.append(i)
countrd[i] -= 1
for j in i.neighbors:
countrd[j] = countrd[j] - 1
if countrd[j] == 0:
self.dfs(j, countrd, ans)
"""
@param graph: A list of Directedgraph node
@return: A list of integer
"""
def topSort(self, graph):
countrd= {}
for x in graph:
countrd[x] = 0

for i in graph:
for j in i.neighbors:
countrd[j] = countrd[j] +1

ans = []
for i in graph:
if countrd[i] == 0:
self.dfs(i, countrd, ans)
return ans
``````

1.选课问题I。假设有n门课要上，序号从0到n-1，假设用数组对来表示前置课程，例如用[0,1]来表示1是0的前置课程。现在给出n和前置课程对的数组，返回是否能够完成课程。
【解】相当于给出n个顶点的图和其边的信息，问是否能够对其进行拓扑排序。很明显关键就在于有没有环。

``````import collections
class Solution(object):
def canFinish(self, numCourses, prerequisites):
"""
:type numCourses: int
:type prerequisites: List[List[int]]
:rtype: bool
"""
zero_in_degree_queue, in_degree, out_degree = collections.deque(), {}, {}

for i, j in prerequisites:
if i not in in_degree:
in_degree[i] = set()
if j not in out_degree:
out_degree[j] = set()

for i in range(numCourses):
if i not in in_degree:
zero_in_degree_queue.append(i)

while zero_in_degree_queue:
prerequisite = zero_in_degree_queue.popleft()

if prerequisite in out_degree:
for course in out_degree[prerequisite]:
if not in_degree[course]:
zero_in_degree_queue.append(course)

del out_degree[prerequisite]

if out_degree:
return False

return True
``````

2.选课问题II。接上题，返回拓扑排序的任意一个结果，假如没有，返回空数组。
【解】相似度99%，多加一个结果数组，把零入度的节点放进去即可。代码略。

3.外文字典。现在有一种新的外语，也用英文字母，但是顺序不一样。现在给出一组这种外文的单词，已经按照字典序排列，返回该外文的字母顺序。例如，给出[wrt,wrf,er,ett,rftt]，返回wertf。假如有多个可能顺序，返回任一个即可。
【解】还是拓扑排序，只不过这次有多个对象，如何转化是一个关键问题。

``````# T: O(n) S:O(|V|+|E|)=O(1) 因为只有26个字母
import collections
# BFS solution.
class Solution(object):
def alienOrder(self, words):
"""
:type words: List[str]
:rtype: str
"""
result, zero_in_degree_queue, in_degree, out_degree = [], collections.deque(), {}, {}
nodes = set()
for word in words:
for c in word:

for i in range(1, len(words)):
self.findEdges(words[i - 1], words[i], in_degree, out_degree)

for node in nodes:
if node not in in_degree:
zero_in_degree_queue.append(node)

while zero_in_degree_queue:
precedence = zero_in_degree_queue.popleft()
result.append(precedence)

if precedence in out_degree:
for c in out_degree[precedence]:
if not in_degree[c]:
zero_in_degree_queue.append(c)

del out_degree[precedence]

if out_degree:
return ""

return "".join(result)

# Construct the graph.
def findEdges(self, word1, word2, in_degree, out_degree):
str_len = min(len(word1), len(word2))
for i in range(str_len):
if word1[i] != word2[i]:
if word2[i] not in in_degree:
in_degree[word2[i]] = set()
if word1[i] not in out_degree:
out_degree[word1[i]] = set()
break
``````

### 推荐阅读更多精彩内容

• 1 序 2016年6月25日夜，帝都，天下着大雨，拖着行李箱和同学在校门口照了最后一张合照，搬离寝室打车去了提前租...
StarryThrone阅读 3,040评论 0 10
• 课程介绍 先修课：概率统计，程序设计实习，集合论与图论 后续课：算法分析与设计，编译原理，操作系统，数据库概论，人...
ShellyWhen阅读 960评论 0 3
• 现实生活中有很大一类问题可以用简洁明了的图论语言来描述，可以转化为图论问题。 相关定义 图可以表示为G=(V, E...
芥丶未央阅读 643评论 0 7
• 第一章 绪论 什么是数据结构？ 数据结构的定义：数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
SeanCheney阅读 3,054评论 0 17
• 春节倒计时中，想必在外漂泊的你已经开始进入抢模式了。 有一句歌词：有钱没钱，回家过年。无论平时再忙，到...
l小梨子阅读 41评论 0 0