《算法》第四章部分程序 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. ...
随机推荐
- 关于sdk>=23的android版本权限的问题
在SDK23也就是Android6.0.1里编写调用系统通讯录读写权限的程序,在AndroidManifest.xml中,已经配置了 <uses-permission android:name= ...
- spring 整合Junit学习
测试一般是测试的局部功能,使用时需要自己写个测试相关的spring配置文件,比较费劲,所以常用的是纯配置的方式来实现测试. @RunWith(SpringJUnit4ClassRunner.class ...
- 关于linux中的system函数
Linux下使用system()函数一定要谨慎 https://blog.csdn.net/senen_wakk/article/details/51496322 system()正确应用 https ...
- C++11--Tuple类<tuple>
#include "stdafx.h" #include <iomanip> #include <condition_variable> #include ...
- 基于无锁队列和c++11的高性能线程池
基于无锁队列和c++11的高性能线程池线程使用c++11库和线程池之间的消息通讯使用一个简单的无锁消息队列适用于linux平台,gcc 4.6以上 标签: <无> 代码片段(6)[ ...
- flume-拦截器、channel选择器、sink组合sink处理器
1. Flume Interceptors Flume有能力修改/删除流程中的events.这是在拦截器(interceptor)的帮助下完成的.拦截器(Interceptors)是实现org.apa ...
- [转][PowerShell]ps执行重启IIS
来自:https://www.aliyun.com/jiaocheng/871477.html write-output 'Restarting IIS servers ............... ...
- mybatisz中一个可以替代between..and 的技巧
用mybatis进行时间段筛选时,如果,查询本日,本月的信息量,我们可以使用like concat()函数来替换between..and <select id="queryMyStaf ...
- 一次mysql数据关于union+concat用法的记录
SELECT CONCAT('SELECT COUNT(*) FROM ',table_name,' union all') FROM information_schema.tables WHERE ...
- APP中https证书有效性验证引发安全问题(例Fiddler可抓https包)
原文: https://blog.csdn.net/woddle/article/details/71175140 在实际项目代码审计中发现,目前很多手机银行虽然使用了https通信方式,但是只是简单 ...