《算法》第四章部分程序 part 10
▶ 书中第四章部分程序,包括在加上自己补充的代码,包括无向图连通分量,Kosaraju - Sharir 算法、Tarjan 算法、Gabow 算法计算有向图的强连通分量
● 无向图连通分量
package package01; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.Graph;
import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.EdgeWeightedGraph;
import edu.princeton.cs.algs4.Edge; public class class01
{
private boolean[] marked;
private int[] id; // 连通分量的标号
private int[] size; // 连通分量顶点数
private int count; // 连通分量数 public class01(Graph G)
{
marked = new boolean[G.V()];
id = new int[G.V()];
size = new int[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v])
{
dfs(G, v);
count++;
}
}
} public class01(EdgeWeightedGraph G) // 有边权的图,算法相同,数据类型不同
{
marked = new boolean[G.V()];
id = new int[G.V()];
size = new int[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v])
{
dfs(G, v);
count++;
}
}
} private void dfs(Graph G, int v)
{
marked[v] = true;
id[v] = count; // 深度优先搜索时,顶点分类,连通分量尺寸更新
size[count]++;
for (int w : G.adj(v))
{
if (!marked[w])
dfs(G, w);
}
} private void dfs(EdgeWeightedGraph G, int v)
{
marked[v] = true;
id[v] = count;
size[count]++;
for (Edge e : G.adj(v))
{
int w = e.other(v);
if (!marked[w])
dfs(G, w);
}
} public int id(int v)
{
return id[v];
} public int size(int v)
{
return size[id[v]];
} public int count()
{
return count;
} public boolean connected(int v, int w)
{
return id[v] == id[w];
} public static void main(String[] args)
{
In in = new In(args[0]);
Graph G = new Graph(in);
class01 cc = new class01(G);
int m = cc.count();
Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m]; // 每个连通分量放入一个队列
for (int i = 0; i < m; i++)
components[i] = new Queue<Integer>();
for (int v = 0; v < G.V(); v++)
components[cc.id(v)].enqueue(v); StdOut.println(m + " components");
for (int i = 0; i < m; i++)
{
for (int v : components[i])
StdOut.print(v + " ");
StdOut.println();
}
}
}
● Kosaraju - Sharir 算法计算有向图的强连通分量
package package01; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.DepthFirstOrder; public class class01
{
private boolean[] marked;
private int[] id;
private int[] size;
private int count; public class01(Digraph G)
{
DepthFirstOrder dfs = new DepthFirstOrder(G.reverse()); // 对 G 的逆图进行深度优先搜索
marked = new boolean[G.V()];
id = new int[G.V()];
size = new int[G.V()];
for (int v : dfs.reversePost()) // 使用 G 逆图 dfs 序对 G 进行深度优先搜索
{
if (!marked[v])
{
dfs(G, v);
count++;
}
}
} private void dfs(Digraph G, int v)
{
marked[v] = true;
id[v] = count;
size[count]++;
for (int w : G.adj(v))
{
if (!marked[w])
dfs(G, w);
}
} public int id(int v)
{
return id[v];
} public int size(int v)
{
return size[id[v]];
} public int count()
{
return count;
} public boolean strongConnected(int v, int w)
{
return id[v] == id[w];
} public static void main(String[] args)
{
In in = new In(args[0]);
Digraph G = new Digraph(in);
class01 scc = new class01(G);
int m = scc.count();
Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m];
for (int i = 0; i < m; i++)
components[i] = new Queue<Integer>();
for (int v = 0; v < G.V(); v++)
components[scc.id(v)].enqueue(v); StdOut.println(m + " components");
for (int i = 0; i < m; i++)
{
for (int v : components[i])
StdOut.print(v + " ");
StdOut.println();
}
}
}
● Tarjan 算法计算有向图的强连通分量
package package01; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.Stack; public class class01
{
private boolean[] marked;
private int[] id;
private int[] size;
private int[] low; // 遍历序中顶点 v 的最小深度
private int pre; // 优先级(遍历顶点时不断维护)
private int count;
private Stack<Integer> stack; public class01(Digraph G)
{
marked = new boolean[G.V()];
id = new int[G.V()];
size = new int[G.V()];
low = new int[G.V()];
stack = new Stack<Integer>();
for (int v = 0; v < G.V(); v++) // 正常顺序深度优先遍历图
{
if (!marked[v])
dfs(G, v);
}
} private void dfs(Digraph G, int v)
{
marked[v] = true;
low[v] = pre++; // 更新递归深度
int min = low[v]; // 记录 v 所在的的递归深度
stack.push(v);
for (int w : G.adj(v))
{
if (!marked[w])
dfs(G, w);
if (low[w] < min) // 寻找 v 邻居的最小深度,low[w] < min 说明找到了后向边(v->w 关于栈中的元素构成有向环)
min = low[w];
}
if (min < low[v]) // 改写 v 的最小深度,终止递归(沿着栈一路改写回去,直到栈中首次出现有向环的元素为止(再往前的顶点满足 low[x] < min),进入吐栈环节
{
low[v] = min;
return;
}
int sizeTemp = 0;
for (int w = -1; w != v; sizeTemp++) // 从栈顶吐到 v 为止,都是一个强连通分量,这里 v 是有向环首元再往前一格的顶点
{
w = stack.pop();
id[w] = count; // 标记吐出的强连通分量
low[w] = G.V(); // 将已经记录了的连通分量的深度改为图的顶点数(递归可能的最大深度)
}
size[count] = sizeTemp;
count++; // 增加连通分量计数
} public int id(int v)
{
return id[v];
} public int size(int v)
{
return size[id[v]];
} public int count()
{
return count;
} public boolean strongConnected(int v, int w)
{
return id[v] == id[w];
} public static void main(String[] args)
{
In in = new In(args[0]);
Digraph G = new Digraph(in);
class01 scc = new class01(G);
int m = scc.count();
Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m];
for (int i = 0; i < m; i++)
components[i] = new Queue<Integer>();
for (int v = 0; v < G.V(); v++)
components[scc.id(v)].enqueue(v); StdOut.println(m + " components");
for (int i = 0; i < m; i++)
{
for (int v : components[i])
StdOut.print(v + " ");
StdOut.println();
}
}
}
● Gabow 算法计算有向图的强连通分量
package package01; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.Stack; public class class01
{
private boolean[] marked;
private int[] id;
private int[] size;
private int[] preOrder; // 记录每个顶点的遍历深度
private int pre;
private int count;
private Stack<Integer> stack1;
private Stack<Integer> stack2; // 用栈来代替 Tarjan 算法中的 low 数组 public class01(Digraph G)
{
marked = new boolean[G.V()];
id = new int[G.V()];
size = new int[G.V()];
preOrder = new int[G.V()];
stack1 = new Stack<Integer>();
stack2 = new Stack<Integer>();
for (int v = 0; v < G.V(); v++)
id[v] = -1;
for (int v = 0; v < G.V(); v++)
{
if (!marked[v])
dfs(G, v);
}
} private void dfs(Digraph G, int v)
{
marked[v] = true;
preOrder[v] = pre++; // 更新递归深度,一旦写入就不改变了
stack1.push(v); // 同时压两个栈
stack2.push(v);
for (int w : G.adj(v))
{
if (!marked[w])
dfs(G, w);
else if (id[w] == -1) // 已经遍历过 w,且 w 不属于任何连通分量
for (; preOrder[stack2.peek()] > preOrder[w]; stack2.pop()); // 把 stack2 中深度大于 w 的顶点全部吐掉(直到栈顶等于有向环的首个元素为止)
}
if (stack2.peek() == v) // 该式在递归回退到栈中首次出现有向环元素的那层时成立,此时 stack2 顶为有向环首元,stack1 顶为有向环末元
{ // 注意此时 stack2 顶层下(stack1 和 stack2 共有且相等)可能还有其他元素,是不属于非有向环部分的遍历路径
stack2.pop(); // stack2 退到首元的上一个元
int sizeTemp = 0;
for (int w = -1; w != v; sizeTemp++) // 同样的方法从 stack1 中逐渐吐栈,计算连通分量元素
{
w = stack1.pop();
id[w] = count;
}
size[count] = sizeTemp;
count++;
}
} public int id(int v)
{
return id[v];
} public int size(int v)
{
return size[id[v]];
} public int count()
{
return count;
} public boolean strongConnected(int v, int w)
{
return id[v] == id[w];
} public static void main(String[] args)
{
In in = new In(args[0]);
Digraph G = new Digraph(in);
class01 scc = new class01(G);
int m = scc.count();
Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m];
for (int i = 0; i < m; i++)
components[i] = new Queue<Integer>();
for (int v = 0; v < G.V(); v++)
components[scc.id(v)].enqueue(v); StdOut.println(m + " components");
for (int i = 0; i < m; i++)
{
for (int v : components[i])
StdOut.print(v + " ");
StdOut.println();
}
}
}
《算法》第四章部分程序 part 10的更多相关文章
- 《算法》第四章部分程序 part 18
▶ 书中第四章部分程序,包括在加上自己补充的代码,在有权有向图中寻找环,Bellman - Ford 算法求最短路径,套汇算法 ● 在有权有向图中寻找环 package package01; impo ...
- 《算法》第四章部分程序 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 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. ...
随机推荐
- Windows Phone Splash Screen
Why to use splash screen? Typically, you should use a splash screen in your app only if your app is ...
- 解决Windows远程桌面连接每次都提示输入密码的问题,远程桌面记不住密码
FROM:http://www.veryhuo.com/a/view/80444.html Windows 远程桌面连接几乎每天都用,所以使用的方便性非常重要.如果你经常用,也许会发现在某些系统中,每 ...
- CentOS 7.4 初次手记:第二章 CentOS安装步骤
第二章 CentOS安装步骤... 18 第一节 下载... 18 第二节 分区参考... 18 第三节 安装... 19 I Step 1:引导... 19 II Step 2:配置... 20 I ...
- Python3 读、写Excel文件
首先,简单介绍一下EXECL中工作簿和工作表的区别: 工作簿的英文是BOOK(WORKBOOK),工作表的英文是SHEET(WORKSHEET). 一个工作簿就是一个独立的文件 一个工作簿里面可以有1 ...
- Ubuntu 14.10 下使用IDEA开发Spark应用
1 环境准备 1.1 下载IDEA,可在官网下载 1.2 IDEA与Eclipse有点不同,IDEA中的New Projects相当于Eclipse中的workspace,New Module才是新建 ...
- hadoop-n.x.y-src.tar.gz 、hadoop-n.x.y-src.tar.gz.asc 、hadoop-n.x.y-src.tar.gz.md5 、hadoop-n.x.y-src.tar.gz.mds是什么?
不多说,直接上干货! 我这里,以hadoop-2.6.0为例. hadoop-n.x.y.tar.gz.mds,此mds文件是为了检验在下载和移动文件过程中文件的完整性. 通过验证文件的md5值去检验 ...
- csv文件操作
1.python2中: import csv infos = [ ['peter','male'], ['marry','female'], ['johon','male'], ['rose','fe ...
- JavaScript之图片操作5
本次的图片操作是要实现模仿天猫淘宝的放大镜效果,如下图所示: 其实现原理其实很简单,主要就是定位的运用,在上面的图中,左边是一个div,它的大小就是左边图片的大小,我们称为左窗口(原图),红色部分我们 ...
- Java-Runoob-高级教程-实例-方法:02. Java 实例 – 输出数组元素
ylbtech-Java-Runoob-高级教程-实例-方法:02. Java 实例 – 输出数组元素 1.返回顶部 1. Java 实例 - 输出数组元素 Java 实例 以下实例演示了如何通过重 ...
- 利用 AWS Elastic Beanstalk 部署 Wordpress 环境
1. 准备 wordpress 代码 从https://wordpress.org/download/ 将代码压缩包下载到本地, 解压,会有一个 wordpress 目录 复制 wp-config-s ...