Topological Sorting

Question

  1. Given an directed graph, a topological order of the graph nodes is defined as follow:
  2. For each directed edge A -> B in graph, A must before B in the order list.
  3. The first node in the order can be any node in the graph with no nodes direct to it.
  4. Find any topological order for the given graph.

Example
For graph as follow:

Topological Sorting

  1. The topological order can be:
  2. [0, 1, 2, 3, 4, 5]
  3. [0, 2, 3, 1, 5, 4]
  4. ...
  5. Note
  6. You can assume that there is at least one topological order in the graph.
  7. Challenge
  8. Can you do it in both BFS and DFS?

题解1 - DFS and BFS

图搜索相关的问题较为常见的解法是用 DFS,这里结合 BFS 进行求解,分为三步走:

  1. 统计各定点的入度——只需统计节点在邻接列表中出现的次数即可知。
  2. 遍历图中各节点,找到入度为0的节点。
  3. 对入度为0的节点进行递归 DFS,将节点加入到最终返回结果中。

C++

  1. /**
  2. * Definition for Directed graph.
  3. * struct DirectedGraphNode {
  4. * int label;
  5. * vector<DirectedGraphNode *> neighbors;
  6. * DirectedGraphNode(int x) : label(x) {};
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. /**
  12. * @param graph: A list of Directed graph node
  13. * @return: Any topological order for the given graph.
  14. */
  15. vector<DirectedGraphNode*> topSort(vector<DirectedGraphNode*> graph) {
  16. vector<DirectedGraphNode*> result;
  17. if (graph.size() == 0) return result;
  18. map<DirectedGraphNode*, int> indegree;
  19. // get indegree of all DirectedGraphNode
  20. indeg(graph, indegree);
  21. // dfs recursively
  22. for (int i = 0; i < graph.size(); ++i) {
  23. if (indegree[graph[i]] == 0) {
  24. dfs(indegree, graph[i], result);
  25. }
  26. }
  27. return result;
  28. }
  29. private:
  30. /** get indegree of all DirectedGraphNode
  31. *
  32. */
  33. void indeg(vector<DirectedGraphNode*> &graph,
  34. map<DirectedGraphNode*, int> &indegree) {
  35. for (int i = 0; i < graph.size(); ++i) {
  36. for (int j = 0; j < graph[i]->neighbors.size(); j++) {
  37. if (indegree.find(graph[i]->neighbors[j]) == indegree.end()) {
  38. indegree[graph[i]->neighbors[j]] = 1;
  39. } else {
  40. indegree[graph[i]->neighbors[j]] += 1;
  41. }
  42. }
  43. }
  44. }
  45. void dfs(map<DirectedGraphNode*, int> &indegree, DirectedGraphNode *i,
  46. vector<DirectedGraphNode*> &ret) {
  47. ret.push_back(i);
  48. indegree[i]--;
  49. for (int j = 0; j < i->neighbors.size(); ++j) {
  50. indegree[i->neighbors[j]]--;
  51. if (indegree[i->neighbors[j]] == 0) {
  52. dfs(indegree, i->neighbors[j], ret);
  53. }
  54. }
  55. }
  56. };

源码分析

C++中使用 unordered_map 可获得更高的性能,私有方法中使用引用传值。

复杂度分析

以 V 表示顶点数,E 表示有向图中边的条数。

首先获得节点的入度数,时间复杂度为 O(V+E), 使用了哈希表存储,空间复杂度为 O(V). 遍历图求得入度为0的节点,时间复杂度为 O(V). 仅在入度为0时调用 DFS,故时间复杂度为 O(V+E).

需要注意的是这里的 DFS 不是纯 DFS,使用了 BFS 的思想进行了优化,否则一个节点将被遍历多次,时间复杂度可能恶化为指数级别。

综上,时间复杂度近似为 O(V+E), 空间复杂度为 O(V).

题解2 - BFS

拓扑排序除了可用 DFS 求解外,也可使用 BFS, 具体方法为:

  1. 获得图中各节点的入度。
  2. BFS 首先遍历求得入度数为0的节点,入队,便于下一次 BFS。
  3. 队列不为空时,弹出队顶元素并对其邻接节点进行 BFS,将入度为0的节点加入到最终结果和队列中,重复此过程直至队列为空。

C++

  1. /**
  2. * Definition for Directed graph.
  3. * struct DirectedGraphNode {
  4. * int label;
  5. * vector<DirectedGraphNode *> neighbors;
  6. * DirectedGraphNode(int x) : label(x) {};
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. /**
  12. * @param graph: A list of Directed graph node
  13. * @return: Any topological order for the given graph.
  14. */
  15. vector<DirectedGraphNode*> topSort(vector<DirectedGraphNode*> graph) {
  16. vector<DirectedGraphNode*> result;
  17. if (graph.size() == 0) return result;
  18. map<DirectedGraphNode*, int> indegree;
  19. // get indegree of all DirectedGraphNode
  20. indeg(graph, indegree);
  21. queue<DirectedGraphNode*> q;
  22. // bfs
  23. bfs(graph, indegree, q, result);
  24. return result;
  25. }
  26. private:
  27. /** get indegree of all DirectedGraphNode
  28. *
  29. */
  30. void indeg(vector<DirectedGraphNode*> &graph,
  31. map<DirectedGraphNode*, int> &indegree) {
  32. for (int i = 0; i < graph.size(); ++i) {
  33. for (int j = 0; j < graph[i]->neighbors.size(); j++) {
  34. if (indegree.find(graph[i]->neighbors[j]) == indegree.end()) {
  35. indegree[graph[i]->neighbors[j]] = 1;
  36. } else {
  37. indegree[graph[i]->neighbors[j]] += 1;
  38. }
  39. }
  40. }
  41. }
  42. void bfs(vector<DirectedGraphNode*> &graph, map<DirectedGraphNode*, int> &indegree,
  43. queue<DirectedGraphNode *> &q, vector<DirectedGraphNode*> &ret) {
  44. for (int i = 0; i < graph.size(); ++i) {
  45. if (indegree[graph[i]] == 0) {
  46. ret.push_back(graph[i]);
  47. q.push(graph[i]);
  48. }
  49. }
  50. while (!q.empty()) {
  51. DirectedGraphNode *cur = q.front();
  52. q.pop();
  53. for(int j = 0; j < cur->neighbors.size(); ++j) {
  54. indegree[cur->neighbors[j]]--;
  55. if (indegree[cur->neighbors[j]] == 0) {
  56. ret.push_back(cur->neighbors[j]);
  57. q.push(cur->neighbors[j]);
  58. }
  59. }
  60. }
  61. }
  62. };

源码分析

C++中在判断入度是否为0时将对 map 产生副作用,在求入度数时只有入度数大于等于1才会出现在 map 中,故不在 map 中时直接调用 indegree 方法将产生新的键值对,初始值为0,恰好满足此题需求。

复杂度分析

同题解1 的分析,时间复杂度为 O(V+E), 空间复杂度为 O(V).

Reference