拓扑排序算法

简介: 拓扑排序算法

拓扑排序

适用范围:要求有向图,且有入度为0的节点,且没有环。

给出一个有向图,如何确定访问节点的顺序,并能保证所有的都被访问到,拓扑排序就是来解决这个问题的。

举个例子

假定有向图如下:

网络异常,图片无法展示
|

  1. 先确定入度为0的点,为A,这个点在拓扑排序中是排在最前面的
  2. 确定好A点之后,将A点从图中抹去,剩下B、C、D三个点,可以确定拓扑排序的第二个点是B(入度为0)
  3. 同样将B点从图中抹去,可以依次确定拓扑排序的第三、四个点是C、D

拓扑排序的思路

给出一个有向图,依次找出入度为0的点,就可以确定出访问节点的顺序

public class TopologySort{
    public static List<Node> sortedTopology(Grapgh grapgh){
        // key: 某一个node
        // value:剩余的入度
        HashMap<Node, Integer> inMap = new HashMap<>();
        // 入度为0的点,才能进这个队列
        Queue<Node> zeroInQueue = new LinkedList<>();
        for(Node node: graph.nodes.values()){
            inMap.put(node, node.in);
            if(node.in == 0){
                zeroInQueue.add(node):
            }
        }
        // 拓扑排序的结果,依次加入result
        List<Node> result = new ArrayList<>();
        while(!zeroInQueue.isEmpty()){
            Node cur = zeroInQueue.poll();
            result.add(cur);
            for(Node next: cur.nexts){
                inMap.put(next, inMap.get(next)-1);
                if(inMao.get(next)==0){
                    zeroInQueue.add(next);
                }
            }
        }
        return result;
    }
}
复制代码

Leetcode-207. 课程表

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程  bi 。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

分析

本题的选课,有种依赖关系,可以用有向无环图来描述依赖关系:

网络异常,图片无法展示
|

这里我们需要把一个有向无环图转成线性的排序,这就是拓扑排序

拓扑排序中有两个概念:入度出度

在这里,入度为0就表示这门课不依赖别的课,可以直接去上课。因为在选课时,我们就先去学那个入度为0的课程。学完这个课程之后,就可以再去学别的课程。

步骤

  1. 让入度为 0 的课入列,它们是能直接选的课。
  2. 然后逐个出列,出列代表着课被选,同时清除该课的相关联系,也就是减小相关课的入度。
  3. 如果相关课的入度新变为 0,安排它入列、再出列……直到没有入度为 0 的课可入列。
  4. 当遍历完成时,如果仍有课的入度不为 0,无法被选,完成不了所有课。否则,能找到一种顺序把所有课上完。

代码实现

const canFinish = (numCourses, prerequisites) => {
  const inDegree = new Array(numCourses).fill(0); // 入度数组
  const map = {};                                 // 邻接表
  for (let i = 0; i < prerequisites.length; i++) {
    inDegree[prerequisites[i][0]]++;              // 求课的初始入度值
    if (map[prerequisites[i][1]]) {               // 当前课已经存在于邻接表
      map[prerequisites[i][1]].push(prerequisites[i][0]); // 添加依赖它的后续课
    } else {                                      // 当前课不存在于邻接表
      map[prerequisites[i][1]] = [prerequisites[i][0]];
    }
  }
  const queue = [];
  for (let i = 0; i < inDegree.length; i++) { // 所有入度为0的课入列
    if (inDegree[i] == 0) queue.push(i);
  }
  let count = 0;
  while (queue.length) {
    const selected = queue.shift();           // 当前选的课,出列
    count++;                                  // 选课数+1
    const toEnQueue = map[selected];          // 获取这门课对应的后续课
    if (toEnQueue && toEnQueue.length) {      // 确实有后续课
      for (let i = 0; i < toEnQueue.length; i++) {
        inDegree[toEnQueue[i]]--;             // 依赖它的后续课的入度-1
        if (inDegree[toEnQueue[i]] == 0) {    // 如果因此减为0,入列
          queue.push(toEnQueue[i]);
        }
      }
    }
  }
  return count == numCourses; // 选了的课等于总课数,true,否则false
};
复制代码

Leetcode题汇总

207. 课程表

210. 课程表 II

269. 火星词典

329. 矩阵中的最长递增路径

444. 序列重建



相关文章
|
3月前
|
算法 测试技术 C++
【广度优先搜索】【拓扑排序】【C++算法】913. 猫和老鼠
【广度优先搜索】【拓扑排序】【C++算法】913. 猫和老鼠
|
5月前
|
存储 人工智能 算法
【算法总结】拓扑排序
【算法总结】拓扑排序
30 0
|
5月前
|
算法
拓扑排序【学习算法】
拓扑排序【学习算法】
32 0
|
10月前
|
存储 算法 Android开发
【算法基础】拓扑排序及实战
在图论中,如果一个有向图从任意顶点出发无法经过若干条边回到该点,则这个图是一个**有向无环图(DAG,Directed Acyclic Graph)**
|
5月前
|
算法 搜索推荐 Python
Python算法——树的拓扑排序
Python算法——树的拓扑排序
71 0
|
7月前
|
算法 测试技术 C#
C++算法:利用拓扑排序解决戳印序列
C++算法:利用拓扑排序解决戳印序列
|
9月前
|
算法 前端开发 C++
拓扑排序详解(包含算法原理图解、算法实现过程详解、算法例题变式全面讲解等)
拓扑排序详解(包含算法原理图解、算法实现过程详解、算法例题变式全面讲解等)
164 0
|
11月前
|
算法 容器
【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树(下)
【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树
80 0
|
11月前
|
存储 算法 UED
【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树(上)
【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树
51 0
|
11月前
|
人工智能 算法 搜索推荐
LeetCode算法小抄 -- 环检测算法 和 拓扑排序算法
LeetCode算法小抄 -- 环检测算法 和 拓扑排序算法