▶ 书中第四章部分程序,包括在加上自己补充的代码,包括无向图连通分量,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. system.Data.Entity.Infrastructure.DbUpdateConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0) 问题

    页面控件没有做限制.提交后还可以继续点击,造成了在短时间内的多次请求.查看日志两次错误在200ms之内. 错误信息 system.Data.Entity.Infrastructure.DbUpdate ...

  2. Python关于self用法重点分析

    在介绍Python的self用法之前,先来介绍下Python中的类和实例…… 我们知道,面向对象最重要的概念就是类(class)和实例(instance),类是抽象的模板,比如学生这个抽象的事物,可以 ...

  3. 关于“最小的K个数”问题

    从一堆无序的数中(共n个数)找到最小的K个数,这也算是一道比较经典的题目了,关于这道题目的解法,一般有几种: 方法1:先对所有的数据进行排序,然后直接找出前K个数来,即最小的K个数.时间复杂度为O(N ...

  4. 【Git】Git使用--常用命令

    查看所有分支 git branch -a 查看本地分支 git branch 切换分支 git checkout test demo git checkout release_1.3.1 (切换到re ...

  5. web环境中微信JS-SDK配置

    一.公众号相关设置 首先,在公众号中进行JS安全域名的设置,在公众号设置-功能设置中选择JS接口安全域名,点击设置进入设置对话框.按照要求逐步进行,完成设置. 二.页面请求发送与处理 引入所需js: ...

  6. 使用Softmax回归将神经网络输出转成概率分布

    神经网络解决多分类问题最常用的方法是设置n个输出节点,其中n为类别的个数.对于每一个样例,神经网络可以得到一个n维数组作为输出结果.数组中的每一个维度(也就是每一个输出节点)对应一个类别,通过前向传播 ...

  7. Butter Knife 使用方法

    获取控件 @InjectView(R.id.image_show_password)ImageView image_show_password; 控件事件 @OnClick(R.id.btn_subm ...

  8. Jmeter(三十二)Jmeter Question 之 “自定义函数开发”

    “技术是业务的支撑”,已经不是第一次听到这句话,因为有各种各样的需求,因此衍生了许多各种各样的技术.共勉! 前面有提到提到过Jmeter的安装目录结构,也提到Jmeter的常用函数功能,有部分工作使用 ...

  9. [UE4]时序问题

    时序问题 有依赖关系的两段代码,执行时间不确定,所引起的问题. 举例 1.Shooter引用了Weapon,Weapon引用了Shooter.射击者需要持有枪,枪需要判断是否有主人(枪的持有者).在枪 ...

  10. sqlserver创建表

    --创建学员信息数据表 use StudentManageDB go if exists(select * from sysobjects where name='Students') drop ta ...