《算法》第四章部分程序 part 7
▶ 书中第四章部分程序,包括在加上自己补充的代码,图中找欧拉环
● 无向图中寻找欧拉环
package package01; import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.GraphGenerator;
import edu.princeton.cs.algs4.Graph;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.BreadthFirstPaths; public class class01
{
private Stack<Integer> cycle = new Stack<Integer>(); // 用栈来保存欧拉环的路径,栈空表示无欧拉环 private static class Edge // 图的边的结构
{
private final int v;
private final int w;
private boolean isUsed; public Edge(int v, int w)
{
this.v = v;
this.w = w;
isUsed = false;
} public int other(int vertex)
{
if (vertex == v)
return w;
if (vertex == w)
return v;
throw new IllegalArgumentException("\n<other> No such vertex.\n");
}
} public class01(Graph G)
{
if (G.E() == 0) // 至少有 1 条边
return;
for (int v = 0; v < G.V(); v++) // 欧拉环要求每个顶点的度均为偶数
{
if (G.degree(v) % 2 != 0)
return;
}
Queue<Edge>[] adj = (Queue<Edge>[]) new Queue[G.V()]; // 把邻接表每个链表转化为一个队列,以便遍历(其实可以不要?)
for (int v = 0; v < G.V(); v++)
adj[v] = new Queue<Edge>();
for (int v = 0; v < G.V(); v++)
{
boolean selfEdge = false; // 用于判断奇偶
for (int w : G.adj(v))
{
if (v == w)
{
if (!selfEdge) // 考虑某个顶点的链表出现 2k 次自己,实际只有 k 条自边,即 k 个自环
{
Edge e = new Edge(v, w);
adj[v].enqueue(e);
adj[w].enqueue(e);
}
selfEdge = !selfEdge;
}
else if (v < w) // 控制索引只添加向后顶点的边,防止重复计数
{
Edge e = new Edge(v, w);
adj[v].enqueue(e);
adj[w].enqueue(e);
}
}
}
Stack<Integer> stack = new Stack<Integer>(); // 用栈保存遍历顺序,也即欧拉环中顶点顺序
stack.push(nonIsolatedVertex(G)); // 第一个非孤立点压栈
for (cycle = new Stack<Integer>(); !stack.isEmpty();)
{
int v = stack.pop();
for (; !adj[v].isEmpty();) // 深度优先遍历
{
Edge edge = adj[v].dequeue();
if (edge.isUsed)
continue;
edge.isUsed = true; // 从 adj[v] 处改 edge(v,w) 则 adj[w] 相应顶点也变化,保证每条边只修改一次,防止回头路
stack.push(v);
v = edge.other(v);
}
cycle.push(v); // 遍历完成,把终点入栈
}
if (cycle.size() != G.E() + 1) // 存储了遍历顺序,起点终点记两次,所以比边数多 1
cycle = null;
} public Iterable<Integer> cycle()
{
return cycle;
} public boolean hasEulerianCycle()
{
return cycle != null;
} private static int nonIsolatedVertex(Graph G) // 寻找图上的第一个非独立点,也即度数大于 0 的点
{
for (int v = 0; v < G.V(); v++)
{
if (G.degree(v) > 0)
return v;
}
return -1;
} private static boolean satisfiesNecessaryAndSufficientConditions(Graph G) // 用欧拉环的充要条件进行判定
{
if (G.E() == 0) // 至少有一条边
return false;
for (int v = 0; v < G.V(); v++) // 每个顶点的度是偶数
{
if (G.degree(v) % 2 != 0)
return false;
}
BreadthFirstPaths bfs = new BreadthFirstPaths(G, nonIsolatedVertex(G)); // 图是连通的
for (int v = 0; v < G.V(); v++)
{
if (G.degree(v) > 0 && !bfs.hasPathTo(v))
return false;
}
return true;
} private static void unitTest(Graph G, String description)
{
System.out.printf("\n%s--------------------------------\n", description);
StdOut.print(G);
class01 euler = new class01(G);
System.out.printf("Eulerian cycle: ");
if (euler.hasEulerianCycle())
{
for (int v : euler.cycle())
System.out.printf(" %d", v);
}
System.out.println();
} public static void main(String[] args)
{
int V = Integer.parseInt(args[0]);
int E = Integer.parseInt(args[1]); Graph G1 = GraphGenerator.eulerianCycle(V, E);
unitTest(G1, "Eulerian cycle"); Graph G2 = GraphGenerator.eulerianPath(V, E);
unitTest(G2, "Eulerian path"); Graph G3 = new Graph(V);
unitTest(G3, "Empty graph"); Graph G4 = new Graph(V);
int v4 = StdRandom.uniform(V);
G4.addEdge(v4, v4);
unitTest(G4, "Single self loop"); Graph H1 = GraphGenerator.eulerianCycle(V / 2, E / 2); // 把两个欧拉环连在一起
Graph H2 = GraphGenerator.eulerianCycle(V - V / 2, E - E / 2);
int[] perm = new int[V];
for (int i = 0; i < V; i++)
perm[i] = i;
StdRandom.shuffle(perm);
Graph G5 = new Graph(V);
for (int v = 0; v < H1.V(); v++)
{
for (int w : H1.adj(v))
G5.addEdge(perm[v], perm[w]);
}
for (int v = 0; v < H2.V(); v++)
{
for (int w : H2.adj(v))
G5.addEdge(perm[V / 2 + v], perm[V / 2 + w]);
}
unitTest(G5, "Union of two disjoint cycles"); Graph G6 = GraphGenerator.simple(V, E);
unitTest(G6, "Random simple graph");
}
}
● 有向图中寻找欧拉环,只注释了与上面不同的地方
package package01; import java.util.Iterator;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.DigraphGenerator;
import edu.princeton.cs.algs4.Graph;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.BreadthFirstPaths; public class class01
{
private Stack<Integer> cycle = new Stack<Integer>(); public class01(Digraph G)
{
if (G.E() == 0) // 至少有一条边
return;
for (int v = 0; v < G.V(); v++) // 要求每个店的入度等于出度(否则至多为欧拉环路径,不能是环)
{
if (G.outdegree(v) != G.indegree(v))
return;
}
Iterator<Integer>[] adj = (Iterator<Integer>[]) new Iterator[G.V()]; // 使用迭代器而不是队列来存储
for (int v = 0; v < G.V(); v++)
adj[v] = G.adj(v).iterator();
Stack<Integer> stack = new Stack<Integer>();
for (stack.push(nonIsolatedVertex(G)); !stack.isEmpty();)
{
int v = stack.pop();
for (; adj[v].hasNext(); v = adj[v].next())
stack.push(v);
cycle.push(v);
}
if (cycle.size() != G.E() + 1)
cycle = null;
} public Iterable<Integer> cycle()
{
return cycle;
} public boolean hasEulerianCycle()
{
return cycle != null;
} private static int nonIsolatedVertex(Digraph G)
{
for (int v = 0; v < G.V(); v++)
{
if (G.outdegree(v) > 0)
return v;
}
return -1;
} private static boolean satisfiesNecessaryAndSufficientConditions(Digraph G) // 用欧拉环的充要条件进行判定
{
if (G.E() == 0) // 至少有 1 条边
return false;
for (int v = 0; v < G.V(); v++) // 每个顶点的入度等于出度
{
if (G.outdegree(v) != G.indegree(v))
return false;
}
Graph H = new Graph(G.V()); // 把图做成无向的,判断所有顶点连通
for (int v = 0; v < G.V(); v++)
{
for (int w : G.adj(v))
H.addEdge(v, w);
}
BreadthFirstPaths bfs = new BreadthFirstPaths(H, nonIsolatedVertex(G));
for (int v = 0; v < G.V(); v++)
{
if (H.degree(v) > 0 && !bfs.hasPathTo(v))
return false;
}
return true;
} private static void unitTest(Digraph G, String description)
{
System.out.printf("\n%s--------------------------------\n", description);
StdOut.print(G);
class01 euler = new class01(G);
System.out.printf("Eulerian cycle: ");
if (euler.hasEulerianCycle())
{
for (int v : euler.cycle())
System.out.printf(" %d", v);
}
StdOut.println();
} public static void main(String[] args)
{
int V = Integer.parseInt(args[0]);
int E = Integer.parseInt(args[1]); Digraph G1 = DigraphGenerator.eulerianCycle(V, E);
unitTest(G1, "Eulerian cycle"); Digraph G2 = DigraphGenerator.eulerianPath(V, E);
unitTest(G2, "Eulerian path"); Digraph G3 = new Digraph(V);
unitTest(G3, "Empty digraph"); Digraph G4 = new Digraph(V);
int v4 = StdRandom.uniform(V);
G4.addEdge(v4, v4);
unitTest(G4, "single self loop"); Digraph H1 = DigraphGenerator.eulerianCycle(V / 2, E / 2);
Digraph H2 = DigraphGenerator.eulerianCycle(V - V / 2, E - E / 2);
int[] perm = new int[V];
for (int i = 0; i < V; i++)
perm[i] = i;
StdRandom.shuffle(perm);
Digraph G5 = new Digraph(V);
for (int v = 0; v < H1.V(); v++)
{
for (int w : H1.adj(v))
G5.addEdge(perm[v], perm[w]);
}
for (int v = 0; v < H2.V(); v++)
{
for (int w : H2.adj(v))
G5.addEdge(perm[V / 2 + v], perm[V / 2 + w]);
}
unitTest(G5, "Union of two disjoint cycles"); Digraph G6 = DigraphGenerator.simple(V, E);
unitTest(G6, "Simple digraph");
/*
Digraph G7 = new Digraph(new In("eulerianD.txt"));
unitTest(G7, "4-vertex Eulerian digraph from file");
*/
}
}
《算法》第四章部分程序 part 7的更多相关文章
- 《算法》第四章部分程序 part 19
▶ 书中第四章部分程序,包括在加上自己补充的代码,有边权有向图的邻接矩阵,FloydWarshall 算法可能含负环的有边权有向图任意两点之间的最短路径 ● 有边权有向图的邻接矩阵 package p ...
- 《算法》第四章部分程序 part 18
▶ 书中第四章部分程序,包括在加上自己补充的代码,在有权有向图中寻找环,Bellman - Ford 算法求最短路径,套汇算法 ● 在有权有向图中寻找环 package package01; impo ...
- 《算法》第四章部分程序 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. ...
随机推荐
- ArrayBlcokingQueue,LinkedBlockingQueue与Disruptor三种队列对比与分析
一.基本介绍 ArrayBlcokingQueue,LinkedBlockingQueue是jdk中内置的阻塞队列,网上对它们的分析已经很多,主要有以下几点: 1.底层实现机制不同,ArrayBlco ...
- regasm 无法定位输入程序集
c# 写的DLL是32位的,在64位机器上注册时提示 无法定位输入程序集 方法1: 使用绝对路径: "%windir%\Microsoft.NET\Framework\v2.0.50727\ ...
- UML类图快速入门篇
1.关联 1.1双向关联: C1-C2:指双方都知道对方的存在,都可以调用对方的公共属性和方法. 在GOF的设计模式书上是这样描述的:虽然在分析阶段这种关系是适用的,但我们觉得它对于描述设计模式内的类 ...
- mac打开文件提示文件已经坏了的修改
10.12下面,mac做了安全的限制,不能打开任意的文件,需要解除限制 sudo spctl --master-disable
- 让Netbeans 8.0 支持 Python 编程
发布人 admin. 分类 Python, 技术手札 Netbeans版本:Netbeans 8.0 for PHP Python版本:python 2.7.8 1.添加插件更新中心 打开Netbea ...
- 无需AutoCAD,用C#生成DWG文件
是一个类库:Teigha.NET for .DWG 利用它就可以在无需安装AutoCAD软件的情况下,生成.读取DWG文件,适合那些导入导出的场合. Teigha曾用名OpenDWG .DWGdire ...
- P1507 NASA的食物计划
传送 01背包又进化了,它变成了二维背包. 既然它多了一个维度,那么我们的f[j]也变成了二维数组f[i][j],其中i表示费用1,j表示费用2 核心方程也相应的变成了f[i][j]=max(f[i- ...
- K近邻(K Nearest Neighbor-KNN)原理讲解及实现
算法原理 K最近邻(k-Nearest Neighbor)算法是比较简单的机器学习算法.它采用测量不同特征值之间的距离方法进行分类.它的思想很简单:如果一个样本在特征空间中的k个最近邻(最相似)的样本 ...
- [转][C#]Environment 类
当执行 Environment.GetEnvironmentVariables() 时,可以得到以下结果(受所安装软件影响,每台电脑都不一样) Count = ["SystemDrive&q ...
- 大数据:Parquet文件存储格式【转】
一.Parquet的组成 Parquet仅仅是一种存储格式,它是语言.平台无关的,并且不需要和任何一种数据处理框架绑定,目前能够和Parquet适配的组件包括下面这些,可以看出基本上通常使用的查询引擎 ...