▶ 书中第四章部分程序,加上自己补充的代码,图的环相关

● 无向图中寻找环

 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.Stack; public class class01
{
private boolean[] marked;
private int[] edgeTo;
private Stack<Integer> cycle; // 用来存储环的顶点 public class01(Graph G)
{
if (hasSelfLoop(G))
return;
if (hasParallelEdges(G))
return;
marked = new boolean[G.V()];
edgeTo = new int[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v])
dfs(G, -1, v); // 首次调用时参数 u 要赋成非定点编号的值
}
} private void dfs(Graph G, int u, int v) // 深度优先探索,传入起点 v 及其父顶点 u
{
marked[v] = true;
for (int w : G.adj(v))
{
if (cycle != null) // 已经找到环,停止搜索(只搜一个就停)
return;
if (!marked[w])
{
edgeTo[w] = v;
dfs(G, v, w);
}
else if (w != u) // 顶点 w 已经遍历,且边 w-v 不是来边 u-v,说明成环
{
cycle = new Stack<Integer>();
for (int x = v; x != w; x = edgeTo[x]) // 这里 w 指向该环在本次遍历中最早遇到的顶点
cycle.push(x);
cycle.push(w);
cycle.push(v);
}
}
} private boolean hasSelfLoop(Graph G) // 自环
{
for (int v = 0; v < G.V(); v++)
{
for (int w : G.adj(v))
{
if (v == w)
{
cycle = new Stack<Integer>();
cycle.push(v);
cycle.push(v);
return true;
}
}
}
return false;
} private boolean hasParallelEdges(Graph G) // 平行边
{
marked = new boolean[G.V()];
for (int v = 0; v < G.V(); v++)
{
for (int w : G.adj(v))
{
if (marked[w])
{
cycle = new Stack<Integer>();
cycle.push(v);
cycle.push(w);
cycle.push(v);
return true;
}
marked[w] = true;
}
for (int w : G.adj(v)) // 恢复遍历前的状态,一遍其他顶点进行检查
marked[w] = false;
}
return false;
} public boolean hasCycle()
{
return cycle != null;
} public Iterable<Integer> cycle() // 将环用迭代器方式输出
{
return cycle;
} public static void main(String[] args)
{
In in = new In(args[0]);
Graph G = new Graph(in);
class01 finder = new class01(G);
if (finder.hasCycle())
{
for (int v : finder.cycle())
StdOut.print(v + " ");
StdOut.println();
}
else
StdOut.println("\n<main> Graph is aycyclic.\n");
}
}

● 有向图中找环

 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.Stack; public class class01
{
private boolean[] marked;
private int[] edgeTo;
private boolean[] onStack; // 记录递归顺序,递归回退时需要恢复(marked 不恢复)
private Stack<Integer> cycle; public class01(Digraph G)
{
marked = new boolean[G.V()];
edgeTo = new int[G.V()];
onStack = new boolean[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v] && cycle == null)
dfs(G, v);
}
} private void dfs(Digraph G, int v) // 有向图不用传递起点的父顶点
{
onStack[v] = true; // 进入新节点时两个变量都要记录
marked[v] = true;
for (int w : G.adj(v))
{
if (cycle != null)
return;
if (!marked[w])
{
edgeTo[w] = v;
dfs(G, w);
}
else if (onStack[w]) // 用 onStack 来检测本次递归是否已经遍历过顶点 w
{
cycle = new Stack<Integer>();
for (int x = v; x != w; x = edgeTo[x])
cycle.push(x);
cycle.push(w);
cycle.push(v);
}
}
onStack[v] = false; // 递归回退,恢复搜索痕迹
} public boolean hasCycle()
{
return cycle != null;
} public Iterable<Integer> cycle()
{
return cycle;
} public static void main(String[] args)
{
In in = new In(args[0]);
Digraph G = new Digraph(in);
class01 finder = new class01(G);
if (finder.hasCycle())
{
for (int v : finder.cycle())
StdOut.print(v + " ");
StdOut.println();
}
else
StdOut.println("\n<main> Graph is aycyclic.\n");
}
}

● 有向图中找环,广度优先搜索,非递归

 package package01;

 import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.DigraphGenerator;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.Queue; public class class01
{
private Stack<Integer> cycle; public class01(Digraph G)
{
int[] indegree = new int[G.V()];
for (int v = 0; v < G.V(); v++)
indegree[v] = G.indegree(v);
Queue<Integer> queue = new Queue<Integer>();
for (int v = 0; v < G.V(); v++) // 所有入度为 0 的点入队,可作为遍历的起点,且绝对不是环的顶点
{
if (indegree[v] == 0)
queue.enqueue(v);
}
for (; !queue.isEmpty();) // 广度优先遍历,反复将 “入度为 0 的顶点的相邻顶点” 的入度减 1,相当于砍掉所有的链
{
int v = queue.dequeue();
for (int w : G.adj(v))
{
indegree[w]--;
if (indegree[w] == 0)
queue.enqueue(w);
}
}
int[] edgeTo = new int[G.V()]; // 父顶点数组是局部变量就够了
int root = -1; // 初始化为非顶点的编号
for (int v = 0; v < G.V(); v++) // 遍历顶点,如果还有顶点有入度,说明存在环,将其顶点赋给 root
{
if (indegree[v] == 0) // 跳过链中的顶点
continue;
else
root = v;
for (int w : G.adj(v)) // 顺着入度仍大于 0 的顶点往前爬
{
if (indegree[w] > 0)
edgeTo[w] = v;
}
}
if (root != -1) // root 被覆盖,说明存在环,root 值为最后一个换的最后一个顶点
{
cycle = new Stack<Integer>();
//for (boolean[] visited = new boolean[G.V()]; !visited[root]; visited[root] = true, root = edgeTo[root]);
// 源码中维护了最后一个环的顶点集,但是以后再也没有用到,删掉
cycle.push(root);
for (int v = edgeTo[root]; v != root; v = edgeTo[v])
cycle.push(v);
//cycle.push(root); 多压一次根节点
}
} public boolean hasCycle()
{
return cycle != null;
} public Iterable<Integer> cycle()
{
return cycle;
} public static void main(String[] args)
{
int V = Integer.parseInt(args[0]); // 生成 DAG G(V,E),然后再添上 F 条边
int E = Integer.parseInt(args[1]);
int F = Integer.parseInt(args[2]);
Digraph G = DigraphGenerator.dag(V, E);
for (int i = 0; i < F; i++)
{
int v = StdRandom.uniform(V);
int w = StdRandom.uniform(V);
G.addEdge(v, w);
}
StdOut.println(G);
class01 finder = new class01(G);
if (finder.hasCycle())
{
for (int v : finder.cycle())
StdOut.print(v + " ");
StdOut.println();
}
else
StdOut.println("\n<main> Graph is aycyclic.\n");
}
}

《算法》第四章部分程序 part 6的更多相关文章

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

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

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

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

  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 10

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,包括无向图连通分量,Kosaraju - Sharir 算法.Tarjan 算法.Gabow 算法计算有向图的强连通分量 ● 无向图连通分量 pack ...

  7. 《算法》第四章部分程序 part 9

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,两种拓扑排序的方法 ● 拓扑排序 1 package package01; import edu.princeton.cs.algs4.Digraph ...

  8. 《算法》第四章部分程序 part 17

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,无环图最短 / 最长路径通用程序,关键路径方法(critical path method)解决任务调度问题 ● 无环图最短 / 最长路径通用程序 pa ...

  9. 《算法》第四章部分程序 part 13

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,图的前序.后序和逆后续遍历,以及传递闭包 ● 图的前序.后序和逆后续遍历 package package01; import edu.princeto ...

  10. 《算法》第四章部分程序 part 12

    ▶ 书中第四章部分程序,包括在加上自己补充的代码,图的几种补充数据结构,包括无向 / 有向符号图,有权边结构,有边权有向图 ● 无向符号图 package package01; import edu. ...

随机推荐

  1. Myeclipse 2017安装

    一.下载 Myeclipse官网下载地址:http://www.myeclipsecn.com/download/ 二.安装 安装详细步骤省略,仅仅是一路下一步即可,博主默认安装到了C盘,注意:安装完 ...

  2. mongodb 安装(官方说明链接)

    这里面有很全的 https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/ 先截个图吧,都是标准的流程,按照操作,是可以安 ...

  3. 【NIO】之IO和NIO的区别

    在Java1.4之前的版本,Java对I/O的支持并不完善,开发人员在开发高性能I/O程序的时候,会面临以下几个问题: 1.没有数据缓存区,I/O性能存在问题 2.没有C/C++通道的概念,输入和输出 ...

  4. 前端安全之CSRF

     一.跨站点请求伪造(CSRF)       什么是csrf呢? 借助用户的身份去做有损用户利益(一些事情)的事情. 怎么实现跨站点请求伪造呢? 1.伪造者通过创造一个带有<a href=&qu ...

  5. bzoj4480: [Jsoi2013]快乐的jyy

    [问题描述] 给定两个字符串A和B,表示JYY的两个朋友的名字.我们用A(i,j)表示A 字符串中从第i个字母到第j个字母所组成的子串.同样的,我们也可以定义B(x,y). JYY发现两个朋友关系的紧 ...

  6. 服务网关zuul之一:入门介绍

    通过之前几篇Spring Cloud中几个核心组件的介绍,我们已经可以构建一个简略的(不够完善)微服务架构了.比如下图所示: 我们使用Spring Cloud Netflix中的Eureka实现了服务 ...

  7. Java学习---流与文件

    实验10  流与文件操作 一.实验目的 理解和掌握JAVA程序中实现输入/输出的技术和有关的类与方法. 二.实验要求 掌握File类常用的方法 掌握标准数据流及Scanner类的应用. 掌握文件输入输 ...

  8. jQuery的遍历

    jQuery有众多的方法,但是有些方法过于累赘,所以就精简了一些重要的方法,记住这些方法的(名字,功能,参数) jQuery的遍历 1.add() 将元素添加到集合中 2.children() 返回被 ...

  9. Zabbix 卸载包 采用yum方式

  10. HTTP 2.0 原理详细分析

    HTTP 2.0是在SPDY(An experimental protocol for a faster web, The Chromium Projects)基础上形成的下一代互联网通信协议.HTT ...