各位读者,晚上好。
这篇博客继续介绍无环加权有向图方面的内容。里面用到的不少类都有变动,通过附件的方式补充。
本博客代码示例均来自:算法 Algorithmes Forth Edition
[美] Robert Sedgewick Kevin Wayne 著 谢路云译
文章目录
一、环加权有向图的最短路径算法
package com.cmh.algorithm.graph;
import edu.princeton.cs.algs4.Stack;
/**
* 无环加权有向图的最短路径算法
* Author:起舞的日子
* Date: 2020/4/26 下午9:24
*/
public class AcyclicSP {
private DirectedEdge[] edgeTo;
private double[] distTo;
public AcyclicSP(EdgeWeightedDigraph G, int s) {
edgeTo = new DirectedEdge[G.V()];
distTo = new double[G.V()];
for (int v = 0; v < G.V(); v++) {
distTo[v] = Double.POSITIVE_INFINITY;
}
distTo[s] = 0.0;
Topological top = new Topological(G);
for (int v : top.order()) {
relax(G, v);
}
}
/**
* 顶点的松弛
*/
private void relax(EdgeWeightedDigraph G, int v) {
for (DirectedEdge e : G.adj(v)) {
int w = e.to();
if (distTo[w] > distTo[v] + e.weight()) {
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
}
}
}
/**
* 最短路径树(SPT)的标准查询算法
*/
public double distTo(int v) {
return distTo[v];
}
public boolean hasPathTo(int v) {
return distTo(v) < Double.POSITIVE_INFINITY;
}
public Iterable<DirectedEdge> pathTo(int v) {
if (!hasPathTo(v)) {
return null;
}
Stack<DirectedEdge> path = new Stack<>();
for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) {
path.push(e);
}
return path;
}
}
二、优先级限制下的并行任务调度问题的关键路径方法
2.1 最长路径
无环加权有向图中的单点最长路径
package com.cmh.algorithm.graph;
import edu.princeton.cs.algs4.Stack;
/**
* 无环加权有向图中的单点最长路径
* Author:起舞的日子
* Date: 2020/4/26 下午9:24
*/
public class AcyclicLP {
/**
* 无环加权有向图中的单点最长路径
* 给定一副无环加权有向图(边的权重可能为负数)和一个起点s,回答"是否存在一条从s到给定的顶点v的路径?"
* 如果有,找出最长(总权重最大)的那条路径等类似问题。
*/
private DirectedEdge[] edgeTo;
private double[] distTo;
public AcyclicLP(EdgeWeightedDigraph G, int s) {
edgeTo = new DirectedEdge[G.V()];
distTo = new double[G.V()];
for (int v = 0; v < G.V(); v++) {
distTo[v] = Double.NEGATIVE_INFINITY;
}
distTo[s] = 0.0;
Topological top = new Topological(G);
for (int v : top.order()) {
relax(G, v);
}
}
/**
* 顶点的松弛
*/
private void relax(EdgeWeightedDigraph G, int v) {
for (DirectedEdge e : G.adj(v)) {
int w = e.to();
if (distTo[w] < distTo[v] + e.weight()) {
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
}
}
}
/**
* 最短路径树(SPT)的标准查询算法
*/
public double distTo(int v) {
return distTo[v];
}
public boolean hasPathTo(int v) {
return distTo(v) < Double.POSITIVE_INFINITY;
}
public Iterable<DirectedEdge> pathTo(int v) {
if (!hasPathTo(v)) {
return null;
}
Stack<DirectedEdge> path = new Stack<>();
for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) {
path.push(e);
}
return path;
}
}
2.2 优先级限制下的并行任务调度
package com.cmh.algorithm.graph;
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
/**
* 优先级限制下的并行任务调度问题的关键路径方法
* Author:起舞的日子
* Date: 2020/4/26 下午11:04
*/
public class CPM {
public static void main(String[] args) {
int N = StdIn.readInt();
StdIn.readLine();
EdgeWeightedDigraph G;
G = new EdgeWeightedDigraph(2 * N + 1);
int s = 2 * N, t = 2 * N + 1;
for (int i = 0; i < N; i++) {
String[] a = StdIn.readLine().split("\\s+");
double duration = Double.parseDouble(a[0]);
G.addEdge(new DirectedEdge(i, i + N, duration));
G.addEdge(new DirectedEdge(s, i, 0.0));
G.addEdge(new DirectedEdge(i + N, t, 0.0));
for (int j = 1; j < a.length; j++) {
int successor = Integer.parseInt(a[j]);
G.addEdge(new DirectedEdge(i + N, successor, 0.0));
}
}
AcyclicLP lp = new AcyclicLP(G, s);
StdOut.println("Start times:");
for (int i = 0; i < N; i++) {
StdOut.printf("%4d : %5.1f\n", i, lp.distTo(i));
}
StdOut.printf("Finish time: %5.1f\n", lp.distTo(t));
}
/**
* 这里实现的任务调度问题的关键路径方法将问题规约为:寻找无环加权有向图的最长路径问题
*/
}
三、附件
3.1 Topological 拓扑排序
构造方法有扩展
package com.cmh.algorithm.graph;
import edu.princeton.cs.algs4.StdOut;
/**
* 拓扑排序
* Author:起舞的日子
* Date: 2020/4/25 上午11:48
*/
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 Topological(EdgeWeightedDigraph G) {
EdgeWeightedDirectedCycle finder = new EdgeWeightedDirectedCycle(G);
if (!finder.hasCycle()) {
DepthFirstOrder dfs = new DepthFirstOrder(G);
}
}
public Iterable<Integer> order() {
return order;
}
public boolean isDAG() {
return order != null;
}
public static void main(String[] args) {
String filename = args[0];
String separator = args[1];
SymbolDigraph sg = new SymbolDigraph(filename, separator);
Topological top = new Topological(sg.G());
for (int v : top.order) {
StdOut.println(sg.name(v));
}
}
}
3.2 EdgeWeightedDirectedCycle 新增
加权有向图中寻找有向环
package com.cmh.algorithm.graph;
import edu.princeton.cs.algs4.Stack;
/**
* 加权有向图中寻找有向环
* Author:起舞的日子
* Date: 2020/4/26 下午10:10
*/
public class EdgeWeightedDirectedCycle {
private boolean[] marked;
private DirectedEdge[] edgeTo;
private boolean[] onStack;
private Stack<DirectedEdge> cycle;
public EdgeWeightedDirectedCycle(EdgeWeightedDigraph G) {
marked = new boolean[G.V()];
onStack = new boolean[G.V()];
edgeTo = new DirectedEdge[G.V()];
for (int v = 0; v < G.V(); v++) {
if (!marked[v]) {
dfs(G, v);
}
}
assert check();
}
private void dfs(EdgeWeightedDigraph G, int v) {
onStack[v] = true;
marked[v] = true;
for (DirectedEdge e : G.adj(v)) {
int w = e.to();
if (cycle != null) {
return;
} else if (!marked[w]) {
edgeTo[w] = e;
dfs(G, w);
} else if (onStack[w]) {
cycle = new Stack<>();
DirectedEdge f = e;
while (f.from() != w) {
cycle.push(f);
f = edgeTo[f.from()];
}
cycle.push(f);
return;
}
}
onStack[v] = false;
}
public boolean hasCycle() {
return cycle != null;
}
public Iterable<DirectedEdge> cycle() {
return cycle;
}
public boolean check() {
if (hasCycle()) {
DirectedEdge first = null, last = null;
for (DirectedEdge e : cycle()) {
if (first == null) {
first = e;
}
if (last != null) {
if (last.to() != e.from()) {
System.err.printf("cycle edges %s and %s not incident\n", last, e);
return false;
}
}
last = e;
}
if (last.to() != first.from()) {
System.err.printf("cycle edges %s and %s not incident\n", last, first);
return false;
}
}
return true;
}
}
3.3 DepthFirstOrder 深度优先搜索
构造方法有扩展
package com.cmh.algorithm.graph;
import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.StdOut;
/**
* 有向图中基于深度优先搜索的顶点排序
* Author:起舞的日子
* Date: 2020/4/25 上午11:40
*/
public class DepthFirstOrder {
/**
* 优先级限制下的调度问题等价于 计算有向无环图中的所有顶点的拓扑顺序
*/
private boolean[] marked;
private int[] pre;
private int[] post;
/**
* 所有顶点的前序排列
*/
private Queue<Integer> preorder;
/**
* 所有顶点的后续排列
*/
private Queue<Integer> postorder;
private int preCounter;
private int postCounter;
/**
* 所有顶点的逆后序排列
*/
private Stack<Integer> reversePost;
public DepthFirstOrder(Digraph G) {
pre = new int[G.V()];
post = new int[G.V()];
preorder = new Queue<>();
postorder = new Queue<>();
reversePost = new Stack<Integer>();
marked = new boolean[G.V()];
for (int v = 0; v < G.V(); v++) {
if (!marked[v]) {
dfs(G, v);
}
}
assert check();
}
public DepthFirstOrder(EdgeWeightedDigraph G) {
pre = new int[G.V()];
post = new int[G.V()];
preorder = new Queue<>();
postorder = new Queue<>();
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) {
preorder.enqueue(v);
marked[v] = true;
for (int w : G.adj(v)) {
if (!marked[w]) {
dfs(G, w);
}
}
postorder.enqueue(v);
reversePost.push(v);
}
private void dfs(EdgeWeightedDigraph G, int v) {
marked[v] = true;
pre[v] = preCounter++;
preorder.enqueue(v);
for (DirectedEdge e : G.adj(v)) {
int w = e.to();
if (!marked[w]) {
dfs(G, w);
}
}
postorder.enqueue(v);
post[v] = postCounter++;
}
public int pre(int v) {
validateVertex(v);
return pre[v];
}
public int post(int v) {
validateVertex(v);
return post[v];
}
public Iterable<Integer> pre() {
return preorder;
}
public Iterable<Integer> post() {
return postorder;
}
public Iterable<Integer> reversePost() {
Stack<Integer> reverse = new Stack<>();
for (int v : postorder) {
reverse.push(v);
}
return reverse;
}
private boolean check() {
int r = 0;
for (int v : post()) {
if (post(v) != r) {
StdOut.println("post(v) and post() inconsistent");
return false;
}
r++;
}
r = 0;
for (int v : pre()) {
if (pre(v) != r) {
StdOut.println("pre(v) and pre() inconsistent ");
return false;
}
}
return true;
}
private void validateVertex(int v) {
int V = marked.length;
if (v < 0 || v >= V) {
throw new IllegalArgumentException("vertex " + v + " is not between - and " + (V - 1));
}
}
}
四、源码
老地方
https://github.com/cmhhcm/my2020.git
写晕了…
好了,再会。