▶ 书中第四章部分程序,包括在加上自己补充的代码,在有权有向图中寻找环,Bellman - Ford 算法求最短路径,套汇算法

● 在有权有向图中寻找环

 package package01;

 import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.DirectedEdge;
import edu.princeton.cs.algs4.EdgeWeightedDigraph;
import edu.princeton.cs.algs4.Stack; public class class01
{
private boolean[] marked;
private DirectedEdge[] edgeTo;
private boolean[] onStack; // 各顶点当前是否在搜索栈中,递归回退时要清空
private Stack<DirectedEdge> cycle; // 存储环,若空则表示图中不存在环 public class01(EdgeWeightedDigraph G)
{
marked = new boolean[G.V()];
edgeTo = new DirectedEdge[G.V()];
onStack = new boolean[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v])
dfs(G, v);
}
} private void dfs(EdgeWeightedDigraph G, int v)
{
marked[v] = true;
onStack[v] = true;
for (DirectedEdge e : G.adj(v))
{
int w = e.to();
if (cycle != null) // 已经有环了
return;
if (!marked[w])
{
edgeTo[w] = e;
dfs(G, w);
}
else if (onStack[w]) // 该点已经遍历过,且在栈中,即有环
{
cycle = new Stack<DirectedEdge>();
DirectedEdge f = e;
for (; f.from() != w; f = edgeTo[f.from()]) // 回退搜索栈压入 cycle 中,直到该环在搜索栈中的首元素 w 处
cycle.push(f);
cycle.push(f); // 压入环在搜索栈中的首元素 w
return;
}
}
onStack[v] = false; // 递归回退时栈要清空,但 marked 不清空
} public boolean hasCycle()
{
return cycle != null;
} public Iterable<DirectedEdge> cycle()
{
return cycle;
} public static void main(String[] args)
{
int V = Integer.parseInt(args[0]); // 新建有边权有向图 G(E,V),再添加 F 条边
int E = Integer.parseInt(args[1]);
int F = Integer.parseInt(args[2]);
EdgeWeightedDigraph G = new EdgeWeightedDigraph(V);
int[] vertices = new int[V];
for (int i = 0; i < V; i++)
vertices[i] = i;
StdRandom.shuffle(vertices);
for (int i = 0; i < E; i++)
{
int v = 1, w = 0;
for (; v >= w; v = StdRandom.uniform(V), w = StdRandom.uniform(V));
double weight = StdRandom.uniform();
G.addEdge(new DirectedEdge(v, w, weight));
}
for (int i = 0; i < F; i++)
{
int v = StdRandom.uniform(V), w = StdRandom.uniform(V);
double weight = StdRandom.uniform(0.0, 1.0);
G.addEdge(new DirectedEdge(v, w, weight));
} StdOut.println(G); // 原图
class01 finder = new class01(G); // 搜索环
if (finder.hasCycle())
{
StdOut.print("Cycle: ");
for (DirectedEdge e : finder.cycle())
StdOut.print(e + " ");
StdOut.println();
}
else
StdOut.println("No directed cycle");
}
}

● Bellman - Ford 算法求最短路径

 package package01;

 import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.DirectedEdge;
import edu.princeton.cs.algs4.EdgeWeightedDigraph;
import edu.princeton.cs.algs4.EdgeWeightedDirectedCycle;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.Queue; public class class01
{
private double[] distTo; // 起点到各顶点的距离
private DirectedEdge[] edgeTo; // 起点到各顶点的最后一条边
private boolean[] onQueue; // 各顶点当前是否在搜索队中
private Queue<Integer> queue; // 搜索队列
private int cost; // 调用函数 relax 的次数
private Iterable<DirectedEdge> cycle; // 存储负环,若空则表示图中不存在负环 public class01(EdgeWeightedDigraph G, int s)
{
distTo = new double[G.V()];
edgeTo = new DirectedEdge[G.V()];
onQueue = new boolean[G.V()];
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTo[s] = 0.0;
queue = new Queue<Integer>();
for (queue.enqueue(s), onQueue[s] = true; !queue.isEmpty() && !hasNegativeCycle();) // 存在负环则停止搜索
{
int v = queue.dequeue(); // 每次从搜索队列中拿走一个顶点,松弛以该顶点为起点的边
onQueue[v] = false;
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()) // 加入这条边会使起点到 w 的距离变短
{
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
if (!onQueue[w]) // 注意若 w 已经在搜索队列中则不做任何改变
{
queue.enqueue(w);
onQueue[w] = true;
}
}
if (cost++ % G.V() == 0) // 每当松弛了 V 的倍数次时检查是否存在负环
{
findNegativeCycle();
if (hasNegativeCycle())
return;
}
}
} private void findNegativeCycle() // 利用类 EdgeWeightedDirectedCycle 来找环
{
int V = edgeTo.length;
EdgeWeightedDigraph spt = new EdgeWeightedDigraph(V);
for (int v = 0; v < V; v++)
{
if (edgeTo[v] != null)
spt.addEdge(edgeTo[v]);
}
EdgeWeightedDirectedCycle finder = new EdgeWeightedDirectedCycle(spt);
cycle = finder.cycle();
} public boolean hasNegativeCycle()
{
return cycle != null;
} public Iterable<DirectedEdge> negativeCycle()
{
return cycle;
} public boolean hasPathTo(int v)
{
return distTo[v] < Double.POSITIVE_INFINITY;
} public double distTo(int v)
{
if (hasNegativeCycle())
throw new UnsupportedOperationException("\n<distTo> Negative cost cycle exists.\n");
return distTo[v];
} public Iterable<DirectedEdge> pathTo(int v)
{
if (hasNegativeCycle())
throw new UnsupportedOperationException("\n<pathTo> Negative cost cycle exists.\n");
if (!hasPathTo(v))
return null;
Stack<DirectedEdge> path = new Stack<DirectedEdge>();
for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()])
path.push(e);
return path;
} public static void main(String[] args)
{
In in = new In(args[0]);
int s = Integer.parseInt(args[1]);
EdgeWeightedDigraph G = new EdgeWeightedDigraph(in);
class01 sp = new class01(G, s);
if (sp.hasNegativeCycle())
{
for (DirectedEdge e : sp.negativeCycle())
StdOut.println(e);
}
else
{
for (int v = 0; v < G.V(); v++)
{
if (sp.hasPathTo(v))
{
StdOut.printf("%d to %d (%5.2f) ", s, v, sp.distTo(v));
for (DirectedEdge e : sp.pathTo(v))
StdOut.print(e + " ");
StdOut.println();
}
else
StdOut.printf("%d to %d no path\n", s, v);
}
}
}
}

● 套汇,本质是寻找负环

 package package01;

 import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.DirectedEdge;
import edu.princeton.cs.algs4.EdgeWeightedDigraph;
import edu.princeton.cs.algs4.BellmanFordSP; public class class01
{
private class01() {} public static void main(String[] args)
{
int V = StdIn.readInt(); // 顶点数
String[] name = new String[V];
EdgeWeightedDigraph G = new EdgeWeightedDigraph(V);
for (int v = 0; v < V; v++) // 建图
{
name[v] = StdIn.readString();
for (int w = 0; w < V; w++)
{
double rate = StdIn.readDouble();
DirectedEdge e = new DirectedEdge(v, w, -Math.log(rate));// 汇率取负对数,x1x2x3 < 1 -> -logx1 -logx2 -logx3 < 0
G.addEdge(e);
}
}
BellmanFordSP spt = new BellmanFordSP(G, 0); // 用 BF 算法找负环
if (spt.hasNegativeCycle())
{
double stake = 1000.0;
for (DirectedEdge e : spt.negativeCycle())
{
StdOut.printf("%10.5f %s ", stake, name[e.from()]);
stake *= Math.exp(-e.weight());
StdOut.printf("= %10.5f %s\n", stake, name[e.to()]);
}
}
else
StdOut.println("No arbitrage opportunity");
}
}

《算法》第四章部分程序 part 18的更多相关文章

  1. 《算法》第四章部分程序 part 19

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,有边权有向图的邻接矩阵,FloydWarshall 算法可能含负环的有边权有向图任意两点之间的最短路径 ● 有边权有向图的邻接矩阵 package p ...

  2. 《算法》第四章部分程序 part 16

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,Dijkstra 算法求有向 / 无向图最短路径,以及所有顶点对之间的最短路径 ● Dijkstra 算法求有向图最短路径 package packa ...

  3. 《算法》第四章部分程序 part 15

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,Kruskal 算法和 Boruvka 算法求最小生成树 ● Kruskal 算法求最小生成树 package package01; import e ...

  4. 《算法》第四章部分程序 part 14

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,两种 Prim 算法求最小生成树 ● 简单 Prim 算法求最小生成树 package package01; import edu.princeton ...

  5. 《算法》第四章部分程序 part 10

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,包括无向图连通分量,Kosaraju - Sharir 算法.Tarjan 算法.Gabow 算法计算有向图的强连通分量 ● 无向图连通分量 pack ...

  6. 《算法》第四章部分程序 part 9

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,两种拓扑排序的方法 ● 拓扑排序 1 package package01; import edu.princeton.cs.algs4.Digraph ...

  7. 《算法》第四章部分程序 part 17

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,无环图最短 / 最长路径通用程序,关键路径方法(critical path method)解决任务调度问题 ● 无环图最短 / 最长路径通用程序 pa ...

  8. 《算法》第四章部分程序 part 13

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,图的前序.后序和逆后续遍历,以及传递闭包 ● 图的前序.后序和逆后续遍历 package package01; import edu.princeto ...

  9. 《算法》第四章部分程序 part 12

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,图的几种补充数据结构,包括无向 / 有向符号图,有权边结构,有边权有向图 ● 无向符号图 package package01; import edu. ...

随机推荐

  1. 代码编辑器之sublime text插件

    sublimetext 学习资源:http://www.jianshu.com/p/d1b9a64e2e37 Sublime SFTP CTags – 让Sublime Text支持CTags. Si ...

  2. Emacs的一些事情(与Vi的争议及使用)

    一年成为Emacs高手(像神一样使用编辑器)推荐文章 http://ftp.gnu.org/gnu/emacs/windows/http://blog.csdn.net/redguardtoo/art ...

  3. msp430学习笔记-TA

    定时器,CCR2,CCR1三者共用一个中断向量 定时器A是一个16位的定时/计数器.它有3个捕获/比较寄存器:能支持多个时序控制.多个捕获/比较功能和多个PWM输出:有广泛的中断功能,中断可由计数器溢 ...

  4. ubuntu-docker入门到放弃(六)数据管理

    在docker的数据管理中,有两个概念: 1.数据卷 数据卷是一个可供容器使用的特殊目录,它绕过文件系统,可以提供很多有用的特性: 1.1 数据卷可以在容器之间共享和重用 1.2 对数据卷的修改会立刻 ...

  5. @@identity与scope_identity()函数的区别

    @@IDENTITY 和SCOPE_IDENTITY 返回在当前会话中的任何表内所生成的最后一个标识值. SCOPE_IDENTITY 只返回插入到当前作用域中的值: @@IDENTITY 不受限于特 ...

  6. 黄聪:bootstrapValidator验证成功,按钮变灰却无法提交的问题

    对于这个坑真心无语! 主要问题是按钮的id和name不能为submit! 改成别的就好了!

  7. postgresql 查看数据库,表,索引,表空间以及大小

    转载 http://blog.51yip.com/pgsql/1525.html 1,查看数据库 playboy=> \l //\加上字母l,相当于mysql的,mysql> show d ...

  8. 峰Redis学习(4)Redis 数据结构(List的操作)

    第四节:Redis 数据结构之List 类型 存储list: ArrayList使用数组方式 LinkedList使用双向链接方式   双向链接表中增加数据 双向链接表中删除数据   存储list常用 ...

  9. MATLAB的一些小技巧

    写论文要将图片保存为tiff格式,还要求dpi,还要标注,真是麻烦,下面的命令是最方便的程序化处理方式了 MATLAB text标注后 保存为 tiff 图片,图片到边框间无空白 clear all; ...

  10. centos7部署openvpn-2.4.6

    一.环境说明 返回主机的IP地址 # ip a | grep "scope global" | awk -F'[ /]+' '{print $3}' | head -1 [root ...