【alg4-有向图】有向环检测和拓扑排序

优先级限制下的调度问题

给定一组需要完成的任务,以及一组关于任务完成的先后次序的优先级限制。在满足限制条件的前提下应该如何安排并完成所有任务?
对于任意一个这样的问题,我们都可以马上画出一张有向图,其中顶点对应任务,有向边对应优先级顺序。
在有向图中,优先级限制下的调度问题等价于下面这个基本的问题。
拓扑排序。给定一幅有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素。

有向图中的环

一般来说,如果一个有优先级限制的问题中存在有向环,那么这个问题肯定是无解的。要检查这种错误,需要解决下面这个问题。
有向环检测。给定有向图中包含有向环吗?如果有,按照路径的方向从某个顶点并返回自己来找到环上的所有顶点。

有向无环图:有向无环图(DAG)就是一幅不含有向环的有向图。

解决有向环检测的问题可以回答下面这个问题:一幅有向图是有向无环图吗?基于深度优先搜索来解决这个问题并不困难,因为由系统维护的递归调用的栈表示的正是“当前”正在遍历的有向路径。一旦我们找到了一条有向边v->w且w已经存在于栈中,就找到了一个环,因为栈表示的是一条由w到v的有向路径,而v->w正好补全了这个环。同时,如果没有找到这样的边,那就意味着这幅有向图是无环的。

有向环的API

API功能
DirectedCycle(Digraph G)寻找有向环的构造函数
boolean hasCycle()G是否含有有向环
Iterable<Integer> cycle()有向环中的所有顶点(如果存在的话)

代码

package section4_2;

import java.util.ArrayList;
import java.util.List;

public class DirectedCycle {

    private boolean[] onStack;
    private boolean[] marked;
    private int[] edgeTo;
    private List<Integer> cycle;

    public DirectedCycle(Digraph G) {
        onStack = new boolean[G.V()];
        edgeTo = new int[G.V()];
        marked = new boolean[G.V()];
        for (int v = 0;v < G.V();v++) {
            if (!marked[v]) dfs(G,v);
        }
    }

    public void dfs(Digraph G, int v) {
        onStack[v] = true;
        marked[v] = true;
        for (int w : G.adj(v)) {
            if (this.hasCycle()) {
                return;
            } else if (!marked[w]) {
                edgeTo[w] = v;
                dfs(G,w);
            } else if (onStack[w]) {
                cycle = new ArrayList<>();
                for (int x = v;x != w;x = edgeTo[x]) {
                    cycle.add(0,x);
                }
                cycle.add(0,w);
                cycle.add(0,v);
            }
        }
        onStack[v] = false;
    }

    public boolean hasCycle() {
        return cycle != null;
    }

    public Iterable<Integer> cycle() {
        return cycle;
    }

    public static void main(String[] args) {
        int[][] data = {
                {0,1},
                {1,2},
                {2,3},
                {3,1}
        };
        int vn = 4;
        int en = 4;
        Digraph digraph = new Digraph(vn,en,data);
        DirectedCycle directedCycle = new DirectedCycle(digraph);
        if (directedCycle.hasCycle()) {
            for (int x : directedCycle.cycle()) {
                System.out.print(x + " ");
            }
        }
    }

}

顶点的深度优先次序与拓扑排序

优先级限制下的调度问题等价于计算有向无环图中的所有顶点的拓扑顺序。

当且仅当一幅有向图是无环图时它才能进行拓扑排序。

有向图中基于深度优先搜索的顶点排序

它的基本思想是深度优先搜索正好只会访问每个顶点一次。如果将dfs()的参数顶点保存在一个数据结构中,遍历这个数据结构实际上就能访问图中的所有顶点,遍历的顺序取决于这个数据结构的性质以及是在递归调用之前还是之后进行保存。在典型的应用中,人们感兴趣的是顶点的以下3种排列顺序:

  • 前序:在递归调用之前将顶点加入队列。
  • 后序:在递归调用之后将顶点加入队列。
  • 逆后序:在递归调用之后将顶点压入栈。

输入:
在这里插入图片描述
输出次序:
在这里插入图片描述
代码:

package section4_2;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class DepthFirstOrder {

    private boolean[] marked;
    private Queue<Integer> pre;
    private Queue<Integer> post;
    private List<Integer> reversePost;

    public DepthFirstOrder(Digraph G) {
        pre = new LinkedList<>();
        post = new LinkedList<>();
        reversePost = new ArrayList<>();
        marked = new boolean[G.V()];
        for (int v = 0;v < G.V();v++) {
            if (!marked[v]) {
                dfs(G,v);
            }
        }
    }

    private void dfs(Digraph G, int v) {
        pre.offer(v);
        marked[v] = true;
        for (int w : G.adj(v)) {
            if (!marked[w]) {
                dfs(G,w);
            }
        }
        post.offer(v);
        reversePost.add(0,v);
    }

    public Iterable<Integer> pre() {
        return pre;
    }

    public Iterable<Integer> post() {
        return post;
    }

    public Iterable<Integer> reversePost() {
        return reversePost;
    }

}

拓扑排序

一幅有向无环图的拓扑顺序即为所有顶点的逆后序排列。

拓扑排序API

API功能
Topological(Digraph G)拓扑排序的构造函数
boolean isDAG()G是有向无环图吗
Iterable<Integer> order()拓扑有序的所有顶点

代码:

package section4_2;

public class Topological {

    private Iterable<Integer> order;

    public Topological(Digraph G) {
        DirectedCycle cyclefinder = new DirectedCycle(G);
        if (!cyclefinder.hasCycle()) {
            DepthFirstOrder dfs = new DepthFirstOrder(G);
            order = dfs.reversePost();
        }
    }

    public Iterable<Integer> order() {
        return order;
    }

    public boolean isDAG() {
        return order != null;
    }

    public static void main(String[] args) {
        int[][] data = {
                {0,5},
                {0,1},
                {0,6},
                {2,0},
                {2,3},
                {3,5},
                {5,4},
                {6,4},
                {7,6},
                {8,7},
                {6,9},
                {9,10},
                {9,11},
                {9,12},
                {11,12}
        };
        int vn = 13;
        int en = 15;
        Digraph digraph = new Digraph(vn,en,data);

        Topological topo = new Topological(digraph);

        for (int v : topo.order()) {
            System.out.print(v + " ");
        }
    }

}

输出:
在这里插入图片描述
在实际应用中,拓扑排序和有向环的检测总会一起出现,因为有向环的检测是排序的前提。因此,解决任务调度类应用通常需要以下三步:

  • 指明任务和优先级条件;
  • 不断检测并去除有向图中的所有环,以确保存在可行的方案;
  • 使用拓扑排序解决调度问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
stk-alg-server 是指"算法服务端"。在计算机科学算法是一种解决问题的方法和步骤,而算法服务端则是提供算法计算能力的服务器。 stk-alg-server 提供了各种算法,可以根据客户的需求来执行不同的算法操作。它可以处理各种类型的数据,如数值、文本、图像等,以及不同领域的问题,如机器学习、数据挖掘、图像识别等。 算法服务端的工作流程一般包括以下几个步骤: 1. 接收请求:算法服务端通过网络接口接收客户端发送的请求。请求可以包括要处理的数据和所需的算法类型。 2. 数据处理:对接收到的数据进行预处理,例如数据清洗、标准化等操作,以确保数据的准确性和一致性。 3. 算法计算:根据客户端请求的算法类型,使用相应的算法对数据进行计算和处理。常见的算法有排序算法、搜索算法、聚类算法等。 4. 结果返回:将算法计算得到的结果返回给客户端。结果可以是计算得到的数值、分析报告、图像等形式。 5. 错误处理:在处理请求的过程,可能会遇到错误或异常情况。算法服务端需要捕获并处理这些错误,以保证系统的稳定性和可靠性。 stk-alg-server 的应用非常广泛。它可以用于科学研究、工程设计、金融分析等各种领域。客户可以通过调用算法服务端的接口,利用其强大的计算能力和丰富的算法库,来解决复杂的问题和提升工作效率。同时,算法服务端还可以进行算法的优化和更新,以满足不断变化的客户需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值