《算法》第四章部分程序 part 18
▶ 书中第四章部分程序,包括在加上自己补充的代码,在有权有向图中寻找环,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的更多相关文章
- 《算法》第四章部分程序 part 19
▶ 书中第四章部分程序,包括在加上自己补充的代码,有边权有向图的邻接矩阵,FloydWarshall 算法可能含负环的有边权有向图任意两点之间的最短路径 ● 有边权有向图的邻接矩阵 package p ...
- 《算法》第四章部分程序 part 16
▶ 书中第四章部分程序,包括在加上自己补充的代码,Dijkstra 算法求有向 / 无向图最短路径,以及所有顶点对之间的最短路径 ● Dijkstra 算法求有向图最短路径 package packa ...
- 《算法》第四章部分程序 part 15
▶ 书中第四章部分程序,包括在加上自己补充的代码,Kruskal 算法和 Boruvka 算法求最小生成树 ● Kruskal 算法求最小生成树 package package01; import e ...
- 《算法》第四章部分程序 part 14
▶ 书中第四章部分程序,包括在加上自己补充的代码,两种 Prim 算法求最小生成树 ● 简单 Prim 算法求最小生成树 package package01; import edu.princeton ...
- 《算法》第四章部分程序 part 10
▶ 书中第四章部分程序,包括在加上自己补充的代码,包括无向图连通分量,Kosaraju - Sharir 算法.Tarjan 算法.Gabow 算法计算有向图的强连通分量 ● 无向图连通分量 pack ...
- 《算法》第四章部分程序 part 9
▶ 书中第四章部分程序,包括在加上自己补充的代码,两种拓扑排序的方法 ● 拓扑排序 1 package package01; import edu.princeton.cs.algs4.Digraph ...
- 《算法》第四章部分程序 part 17
▶ 书中第四章部分程序,包括在加上自己补充的代码,无环图最短 / 最长路径通用程序,关键路径方法(critical path method)解决任务调度问题 ● 无环图最短 / 最长路径通用程序 pa ...
- 《算法》第四章部分程序 part 13
▶ 书中第四章部分程序,包括在加上自己补充的代码,图的前序.后序和逆后续遍历,以及传递闭包 ● 图的前序.后序和逆后续遍历 package package01; import edu.princeto ...
- 《算法》第四章部分程序 part 12
▶ 书中第四章部分程序,包括在加上自己补充的代码,图的几种补充数据结构,包括无向 / 有向符号图,有权边结构,有边权有向图 ● 无向符号图 package package01; import edu. ...
随机推荐
- XE5 Android 开发数据访问server端[转]
建立一个webservices stand-alone vcl application 作为手机访问的服务端 1.new->other->webservices 2.选择 stand-a ...
- ubuntu-docker入门到放弃(三)images镜像管理
docker虽然有公共的镜像管理hub,但是我们在日常的使用中,由于不同的业务场景,不同的架构,公共的镜像库不能满足需求,并且出于安全考虑,会搭建私有的docker hub镜像库来管理自己的image ...
- Gradle详细解析***
前言 对于Android工程师来说编译/打包等问题立即就成痛点了.一个APP有多个版本,Release版.Debug版.Test版.甚至针对不同APP Store都有不同的版本.在以前ROM的环境下, ...
- oracle--分组后获取每组数据第一条数据
SELECT * FROM (SELECT ROW_NUMBER() OVER(PARTITION BY cc.queuename ORDER BY cc.enroldate DESC) rn, cc ...
- 黄聪:移动应用抓包调试利器Charles
一.Charles是什么? Charles是在 Mac或Windows下常用的http协议网络包截取工具,是一款屌的不行的抓包工具,在平常的测试与调式过程中,掌握此工具就基本可以不用其他抓包工具了 ...
- 黄聪:PHP代码获取客户端IP地址经纬度及所在城市
echo $_SERVER['HTTP_HOST']; //echo $_SERVER['REQUEST_URI']; $getIp=$_SERVER["REMOTE_ADDR"] ...
- (转)java基础-反射
原文地址:https://blog.csdn.net/sinat_38259539/article/details/71799078 反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的C ...
- 第9章 应用层(1)_域名系统DNS
1. 域名系统DNS 1.1 主机名和域名的关系 (1)完全限定域名(Fully Qualified Domain Name, FQDN) = 主机名 + 域名 ①一个域名下可以有多个服务器/主机(如 ...
- Javascript-多个数组是否有一样值
//判断给出的所有数组 是否都有一样的值 function arrIsEqual(){ var array=[]; for(var i=0;i<arguments.length;i++){ ar ...
- Cache: a place for concealment and safekeeping.Cache: 一个隐藏并保存数据的场所
原文标题:Cache: a place for concealment and safekeeping 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限, ...