数据结构与算法-拓扑排序

拓扑排序,感觉就是图的广度优先搜索和深度优先搜索。

拓扑排序的两种实现方法:Kahn算法与DFS深度优先搜索算法

Kahn算法:使用的是贪心算法思想,借助一个队列,以及一个统计当前顶点入度数的数组。当顶点的入度为0,那么就把顶点入队列。当队列不为空,那么就取出队列的顶点,同时把该顶点所有指向的顶点取出来,同时把这些顶点的入度都减一,然后判断这些顶点的入度是否为0,如果为0,就将该顶点入队列,不断取该队列的顶点,直到顶点为空。

DFS算法:遍历图的所有顶点,取出一个顶点之后,继续取出该顶点指向的所有顶点。

以下是leetcode第207题的拓扑排序代码,使用了Kahn算法和深度优先算法两种方式解决,其中我感觉Kahn算法比较好理解,但是深度优先的时候,涉及递归,有点被绕晕了,调试了好几个小时,最后瞄了大神们的题解,才缝缝补补的敲出来了。其实就是有一个地方被卡住了,需要借助一个数组保存当前顶点访问状态,在访问某个顶点时,设为1,当其他顶点访问到该顶点为1,表示又回到了该顶点,那么说明有环,否则将该顶点设为-1,表示该顶点已经被访问过了。

package com.freshbin.dataStructAndAlgo.chapter43.mycode;

import java.util.LinkedList;

/**
 * 拓扑排序
 * @author freshbin
 * @date 2020/4/29 9:11
 */
public class Solution {
    /**
     * leetcode207
     * 你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。
     * 在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
     * 给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
     *
     * 示例 1:
     * 输入: 2, [[1,0]]
     * 输出: true
     * 解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
     *
     * 示例 2:
     * 输入: 2, [[1,0],[0,1]]
     * 输出: false
     * 解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
     *
     * 思路:
     * 1、先把所有的课程关联关系都用邻接表存起来
     * 2、统计所有的课程数(用一个数组存各个课程的入度数,即先于该课程学习的课程数, 当入度数为0表示不需要依赖课程,直接打印)
     * 3、如果统计出来的课程数小于实际课程数,说明有环,否则,无环。
     *
     * 广度优先算法
     * @param numCourses
     * @param prerequisites
     * @return
     */
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] relyCourseNums = new int[numCourses];
        LinkedList<Integer>[] adj = initGraph(numCourses, prerequisites, relyCourseNums);

        LinkedList<Integer> queue = new LinkedList<>();
        for(int i = 0; i < numCourses; i++) {
            if(relyCourseNums[i] == 0) {
                queue.add(i); // 入度为0的课程不需要依赖其他课程,可以直接放入队列
            }
        }

        System.out.println("广度优先访问如下");
        int count = 0;
        while(!queue.isEmpty()) {
            int currentCourse = queue.remove();
            for(int j = 0; j < adj[currentCourse].size(); j++) {
                int nextCourse = adj[currentCourse].get(j);
                relyCourseNums[nextCourse]--;
                if(relyCourseNums[nextCourse] != 0) {
                    // 入度不为0,说明nextCourse还存在依赖的课程,即需要先学习的课程,所以这里直接跳过
                    continue;
                }
                queue.add(nextCourse);
            }
            count++;
            System.out.print(currentCourse + "->");
        }
        System.out.println();

        return count == numCourses;
    }

    public LinkedList<Integer>[] initGraph(int n, int[][] prerequisites, int[] relyCourseNums) {
        LinkedList<Integer>[] adj = new LinkedList[n];
        for(int i = 0; i < n; i++) {
            adj[i] = new LinkedList<>();
        }

        for(int i = 0; i < prerequisites.length; i++) {
            int s = prerequisites[i][1];
            int t = prerequisites[i][0];
            adj[s].add(t); // 构造图
            relyCourseNums[t]++; // 统计每个课程需要依赖的课程数
        }

        return adj;
    }

    public static void main(String[] arg) {
        Solution solution = new Solution();
        int numCourse = 2;
//        int[][] prerequisites = {{0,1}, {3,1}, {1,3}, {3,2}};
        int[][] prerequisites = {{0, 1}};
        System.out.println("广度优先,该课程学习路径安排是否合理:" + solution.canFinish(numCourse, prerequisites));
        System.out.println();
        System.out.println("深度优先,该课程学习路径安排是否合理:" + solution.canFinish01(numCourse, prerequisites));
    }

    public Boolean flag = true;
    /**
     * 深度优先算法
     * @param numCourses
     * @param prerequisites
     * @return
     */
    public boolean canFinish01(int numCourses, int[][] prerequisites) {
        LinkedList<Integer>[] adj = initGraph(numCourses, prerequisites);
        LinkedList<Integer>[] inverseAdj = initInverseLinkedList(adj);

        System.out.println("深度优先访问如下");
        int[] visitedPoint = new int[numCourses];
        for(int i = 0; i < numCourses; i++) {
            if(!flag) {
                return false;
            }
            if(visitedPoint[i] == -1) {
                // 已经被访问过
                continue;
            }
            dfs(i, inverseAdj, visitedPoint);
        }
        System.out.println();

        return flag;
    }

    public void dfs(int index, LinkedList<Integer>[] inverseAdj, int[] visitedPoint) {
        if(visitedPoint[index] == 1) {
            flag = false;
            return;
        }
        if(visitedPoint[index] == -1) {
            return;
        }
        visitedPoint[index] = 1;
        for(int i = 0; i < inverseAdj[index].size(); i++) {
            int courseIndex = inverseAdj[index].get(i);
            dfs(courseIndex, inverseAdj, visitedPoint);
        }
        visitedPoint[index] = -1;
        System.out.print(index + "->");
    }

    public LinkedList<Integer>[] initGraph(int n, int[][] prerequisites) {
        LinkedList<Integer>[] adj = new LinkedList[n];
        for(int i = 0; i < n; i++) {
            adj[i] = new LinkedList<>();
        }

        for(int i = 0; i < prerequisites.length; i++) {
            int s = prerequisites[i][1];
            int t = prerequisites[i][0];
            adj[s].add(t); // 构造图
        }

        return adj;
    }

    /**
     * 构造反向边
     * @param adj
     * @return
     */
    public LinkedList<Integer>[] initInverseLinkedList(LinkedList<Integer>[] adj) {
        LinkedList<Integer>[] inverseAdj = new LinkedList[adj.length];
        for(int i = 0; i < adj.length; i++) {
            inverseAdj[i] = new LinkedList<>();
        }

        for(int i = 0; i < adj.length; i++) {
            for(int j = 0; j < adj[i].size(); j++) {
                int nextPoint = adj[i].get(j);
                inverseAdj[nextPoint].add(i);
            }
        }
        return inverseAdj;
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值