▶ 书中第四章部分程序,包括在加上自己补充的代码,包括无向图连通分量,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的更多相关文章

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

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

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

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

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

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

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

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

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

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

  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. RichEdit选中文字右键菜单的实现

    procedure TForm1.RichEdit1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: ...

  2. Ubuntu16.04系统重装***

    首先准备一个Live CD,就是Ubuntu的安装盘. 备份原理就是将系统文件压缩打包.由于Linux系统所有都是文件,故,只需要将系统打包即可.当然,必须除了当前系统运行中的文件以及临时文件. 打包 ...

  3. WCF类型共享技巧【转载】

    调用过WCF服务的同学可能都会遇到这样的问题,同一个实体类型,不同的服务Visual Studio生成了不同的版本,例如Service1.User和Service2.User,对于C#来说,这是两个不 ...

  4. 【ActiveMQ】之安全机制(一)管控台安全设置

    ActiveMQ 管控台基于jetty,默认端口8161,默认用户名,密码都是admin,这样的安全配置过于弱化,所以我们需要修改一下 1.修改端口 找到conf/jetty.xml文件里面这一段配置 ...

  5. gerrit配置和使用

    参考http://www.cnblogs.com/tesky0125/p/5973642.html 1.安装gerrit replication插件 mkdir ~/tmp cp gerrit-2.1 ...

  6. Flume的基本概念

    Flume 概念 Flume 最早是Cludera提供的日志收集系统,后贡献给Apache.所以目前是Apache下的项目,Flume支持在日志系统中指定各类数据发送方,用于收集数据. Flume 是 ...

  7. 服务网关zuul之三:zuul统一异常处理

    我们详细介绍了Spring Cloud Zuul中自己实现的一些核心过滤器,以及这些过滤器在请求生命周期中的不同作用.我们会发现在这些核心过滤器中并没有实现error阶段的过滤器.那么这些过滤器可以用 ...

  8. Python——pandas数据处理(python programming)

  9. Spring Boot 学习视频

    1. Spring Boot  项目实战 ----- 技术栈博客企业前后端 链接:https://pan.baidu.com/s/1hueViq4 密码:4ma8 2.Spring Boot  项目实 ...

  10. 猴哥来了-游戏开发记录17-微信排行榜bug

    上线后排行榜bug 1.排序算法 const dataSorter = (gameDatas, field = Consts.OpenDataKeys.LevelKey) => {  let d ...