Topological Sort
Given an directed graph, a topological order of the graph nodes is defined as follow:
- For each directed edge A -> B in graph, A must before B in the order list.
The first node in the order can be any node in the graph with no nodes direct to it.
无向图不存在topological sort的问题(无先后关系)
- 常见topological sort都是用indegree的方法遍历,其实用DFS也可以!
- key point : 具有依赖关系的任务
LintCode 127. Topological Sorting
- 解法1. DFS
- 每次 DFS 得到一个符合正确拓扑顺序的 list,保证单序列顺序;每次新的 DFS 之后得到的 list 要排在之前结果的前面,保证全序列顺序
- 每次翻转都会改变单序列和全序列之间的顺序,对于每一个单序列或者全序列,两次翻转可以互相抵消
class Solution:
def topSort(self, graph):
visited = set()
ans = []
for root in graph:
self.dfs(root, visited, ans)
return ans[::-1]
def dfs(self, root, visited, ans):
if root in visited:
return
for nei in root.neighbors:
if nei not in visited:
self.dfs(nei, visited, ans)
visited.add(root)
ans.append(root)
- 解法2. BFS
- step1. 统计每个vertex/node的indegree
- step2. 从indegree==0开始放入queue,有edge的indegree-1,为0的放入queue
class Solution:
def topSort(self, graph):
indegree = collections.defaultdict(int)
for node in graph:
for nei in node.neighbors:
indegree[nei] += 1
queue = collections.deque([])
for node in graph:
if node not in indegree:
queue.append(node)
res = []
while queue:
node = queue.popleft()
res.append(node)
for nei in node.neighbors:
indegree[nei] -= 1
if indegree[nei] == 0:
queue.append(nei)
return res
210. Course Schedule II
首先要检查有没有环,没有环的话才有topological sort的结果!
class Solution(object):
def findOrder(self, numCourses, prerequisites):
indegree = [0] * numCourses
graph = collections.defaultdict(list)
for u, v in prerequisites:
indegree[u] += 1
graph[v].append(u)
queue = collections.deque([i for i in xrange(numCourses) if indegree[i] == 0])
res = []
while queue:
node = queue.popleft()
res.append(node)
for v in graph[node]:
indegree[v] -= 1
if indegree[v] == 0:
queue.append(v)
return res if sum(indegree) == 0 else []
269. Alien Dictionary
字母的顺序有个先后关系可以构成一个图,而拓扑排序的结果是前后关系正好满足前面的先后关系!
class Solution(object):
def alienOrder(self, words):
indegree = {}
for word in words:
for c in word:
indegree[c] = 0
graph = [set() for _ in xrange(26)]
for i in xrange(len(words)-1):
cur = words[i]
next = words[i+1]
for j in xrange(min(len(cur), len(next))):
if cur[j] != next[j]:
if next[j] not in graph[ord(cur[j])-ord('a')]:
graph[ord(cur[j])-ord('a')].add(next[j])
indegree[next[j]] += 1
break
res = []
cnt = len(indegree.keys())
q = collections.deque([c for c in indegree.keys() if indegree[c] == 0])
while q:
cur = q.popleft()
res.append(cur)
for n in list(graph[ord(cur)-ord('a')]):
indegree[n] -= 1
if indegree[n] == 0:
q.append(n)
return ''.join(res) if len(res) == cnt else ""
332. Reconstruct Itinerary
- 解法. DFS
- 有点类似用DFS来写topological sort的方法!
- DFS后根遍历,然后再reverse结果!!!
class Solution(object):
def findItinerary(self, tickets):
graph = collections.defaultdict(list)
for f, t in sorted(tickets, reverse = True):
graph[f].append(t)
res = []
def dfs(node):
while graph[node]:
dfs(graph[node].pop())
res.append(node)
dfs('JFK')
return res[::-1]
- 欧拉通路(Eulerian path)
- 若一个图为欧拉图或半欧拉图都可以通过一笔画遍历。通过图(有向图或无向图)中的所有边且每一条边仅通过一次的通路称为欧拉通路,若此通路为回路则称为欧拉回路
- 每次深度优先搜索的结果必然是图的一个连通分量。深度优先搜索可以从多点发起。如果将每个节点在深度优先搜索过程中的“结束时间”排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的“拓扑排序”,即topological sort.