无向环

一个含有环的无向图如下所示,其中有两个环,分别是 0-2-1-0 和 2-3-4-2:

要检测无向图中的环,可以使用深度优先搜索。假设从顶点 0 出发,再走到相邻的顶点 2,接着走到顶点 2 相邻的顶点 1,由于顶点 0 和顶点 1 相邻,并且顶点 0 被标记过了,说明我们饶了一圈,所以无向图中存在环。虽然顶点 2 和顶点 1 相邻,但是并不能说明存在环,因为我们就是从顶点 2 直接走到顶点 1 的,这二者只有边的关系。算法如下所示:

package com.zhiyiyo.graph;

import com.zhiyiyo.collection.stack.LinkStack;
import com.zhiyiyo.collection.stack.Stack; /**
* 无向图中的环
*/
public class Cycle {
private boolean[] marked;
private Graph graph;
private boolean hasCycle; public Cycle(Graph graph) {
this.graph = graph;
marked = new boolean[graph.V()]; for (int v = 0; v < graph.V(); ++v) {
if (!marked[v]) {
dfs(v);
}
}
} private void dfs(int s) {
if (hasCycle()) return; Stack<Integer> vertexes = new LinkStack<>();
vertexes.push(s);
marked[s] = true; int lastVertex = s;
while (!vertexes.isEmpty()) {
int v = vertexes.pop(); for (int w : graph.adj(v)) {
if (!marked[w]) {
marked[w] = true;
vertexes.push(w);
} else if (w != lastVertex) {
hasCycle = true;
return;
}
} lastVertex = v;
}
} /**
* 图中是否有环
*/
public boolean hasCycle() {
return hasCycle;
}
}

有向环

有向图

有向图的实现方式和上一篇博客 《如何在 Java 中实现无向图》 中无向图的实现方式几乎一样,只是在添加边 v-w 时只在顶点 v 的链表上添加顶点 w,而不对顶点 w 的链表进行操作。如果把 LinkGraph 中成员变量的访问权限改成 protected,只需继承并重写 addEdge 方法即可:

package com.zhiyiyo.graph;

public class LinkDigraph extends LinkGraph implements Digraph {

    public LinkDigraph(int V) {
super(V);
} @Override
public void addEdge(int v, int w) {
adj[v].push(w);
E++;
} @Override
public Digraph reverse() {
Digraph digraph = new LinkDigraph(V());
for (int v = 0; v < V(); ++v) {
for (int w : adj(v)) {
digraph.addEdge(w, v);
}
}
return digraph;
}
}

检测算法

一个含有有向环的有向图如下所示,其中 5-4-3-5 构成了一个环:

这里使用递归实现的深度优先搜索来检测有向环。假设从顶点 0 开始走,一路经过 5、4、3 这三个顶点,最终又碰到了与顶点 3 相邻的顶点 5,这时候如果知道顶点 5 已经被访问过了,并且递归函数还被压在栈中,就说明深度优先搜索从顶点 5 开始走,又回到了顶点 5,也就是找到了有向环。算法如下所示:

package com.zhiyiyo.graph;

import com.zhiyiyo.collection.stack.LinkStack;
import com.zhiyiyo.collection.stack.Stack; /**
* 有向图中的环
*/
public class DirectedCycle {
private boolean[] marked;
private boolean[] onStack;
private int[] edgeTo;
private Graph graph;
private Stack<Integer> cycle; public DirectedCycle(Digraph graph) {
this.graph = graph;
marked = new boolean[graph.V()];
onStack = new boolean[graph.V()];
edgeTo = new int[graph.V()]; for (int v = 0; v < graph.V(); ++v) {
if (!marked[v]) {
dfs(v);
}
}
} private void dfs(int v) {
marked[v] = true;
onStack[v] = true; for (int w : graph.adj(v)) {
if (hasCycle()) return;
if (!marked[w]) {
marked[w] = true;
edgeTo[w] = v;
dfs(w);
} else if (onStack[w]) {
cycle = new LinkStack<>();
cycle.push(w);
for (int i = v; i != w; i = edgeTo[i]) {
cycle.push(i);
}
cycle.push(w);
}
} onStack[v] = false;
} /**
* 图中是否有环
*/
public boolean hasCycle() {
return cycle != null;
} /**
* 图中的一个环
*/
public Iterable<Integer> cycle() {
return cycle;
}
}

如何在 Java 中实现无向环和有向环的检测的更多相关文章

  1. 如何在 Java 中实现最小生成树算法

    定义 在一幅无向图 \(G=(V,E)\) 中,\((u, v)\) 为连接顶点 \(u\) 和顶点 \(v\) 的边,\(w(u,v)\) 为边的权重,若存在边的子集 \(T\subseteq E\ ...

  2. 如何在JAVA中实现一个固定最大size的hashMap

    如何在JAVA中实现一个固定最大size的hashMap 利用LinkedHashMap的removeEldestEntry方法,重载此方法使得这个map可以增长到最大size,之后每插入一条新的记录 ...

  3. 如何在java中使用sikuli进行自动化测试

    很早之前写过一篇介绍sikuli的文章.本文简单介绍如何在java中使用sikuli进自动化测试. 图形脚本语言sikuli sikuli IDE可以完成常见的单击.右击.移动到.拖动等鼠标操作,ja ...

  4. 如何在Java中调用Python代码

    有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调 ...

  5. 如何在java中跳出当前多重嵌套循环?有几种方法?

    如何在java中跳出当前多重嵌套循环?有几种方法? - 两种方法   - 1.在外层循环定义标记          ok:          for(int i=0;i<100;i++){    ...

  6. 用代码说话:如何在Java中实现线程

    并发编程是Java语言的重要特性之一,"如何在Java中实现线程"是学习并发编程的入门知识,也是Java工程师面试必备的基础知识.本文从线程说起,然后用代码说明如何在Java中实现 ...

  7. 如何在Java中测试类是否是线程安全的

    通过优锐课的java核心笔记中,我们可以看到关于如何在java中测试类是否线程安全的一些知识点汇总,分享给大家学习参考. 线程安全性测试与典型的单线程测试不同.为了测试一个方法是否是线程安全的,我们需 ...

  8. 如何在pyqt中自定义无边框窗口

    前言 之前写过很多关于无边框窗口并给窗口添加特效的博客,按照时间线罗列如下: 如何在pyqt中实现窗口磨砂效果 如何在pyqt中实现win10亚克力效果 如何在pyqt中通过调用SetWindowCo ...

  9. 如何在pyqt中给无边框窗口添加DWM环绕阴影

    前言 在之前的博客<如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果>中,我们实现了窗口的亚克力效果,同时也用SetWindowC ...

随机推荐

  1. java入土---markdown使用技巧

    markdown使用技巧 标题 "#" 为一级标题 "##" 为2级标题 可一直往下曾增加,最多六级标题 字体 加粗 **加粗** 加粗 倾斜 *倾斜* 倾斜 ...

  2. tensorflow源码解析之framework-resource

    目录 什么是resource 如何使用resource 如何管理resource 常用resource 其它结构 关系图 涉及的文件 迭代记录 1. 什么是resource 我们知道,TF的计算是由设 ...

  3. U8g2库的使用

    一.硬件介绍: 由于笔者这里只有0.96寸的OLED屏幕,那就讲讲最常用的0.96寸OLED屏幕吧. OLED介绍: OLED,即有机发光二极管( Organic Light Emitting Dio ...

  4. Python入门随记(1)

    1.IDE Interactive Development Enironment,交互式开发环境 2.AI artificial intelligence 3.Python是一种格式严明(严格缩进)的 ...

  5. OAuth2和JWT - 如何设计安全的API?

    JWT和OAuth2比较? 要比较JWT和OAuth2?首先要明白一点就是,这两个根本没有可比性,是两个完全不同的东西. JWT是一种认证协议        JWT提供了一种用于发布接入令牌(Acce ...

  6. 《前端运维》二、Nginx--1基本概念及安装

    一.Nginx基本概念 简单来说,Nginx就是一个代理服务器,什么是代理服务器呢?也就是当我们访问服务器的时候,请求不会直接请求到服务器,中间会有个代理,代理会预先于服务器处理这些请求,最后由代理决 ...

  7. 手把手带你使用ZigBee——通过爱智控制EFR32,以及 Simplicity Studio 使用过程中注意事项

    前言 兄弟们,我发现一个有意思的东西,我在爱智官网翻资料的时候,发现他们终于终于把官网文档的索引优化了!有一说一,真是方便不少,终于不再是一堆文档糊在一坨了. 另外我还发现他们居然做了一个EFR32的 ...

  8. P5017 [NOIP2018 普及组] 摆渡车

    P5017 [NOIP2018 普及组] 摆渡车 题目 P5017 思路 将实际问题抽象后,不难发现这是一个 区间 \(DP\) 我们不妨认为时间是一条数轴,每名同学按照到达时刻分别对应数轴上可能重合 ...

  9. sum 函数语法与应用

    一.sum 函数语法: SELECT SUM(expression )   FROM tables    WHERE predicates; expression 常量.列或函数,或者是算术.按位与字 ...

  10. 《前端运维》五、k8s--1安装与基本配置

    一.k8s基础概念与安装 k8s,即kubernetes是用于自动部署,扩展和管理容器化应用程序的开源系统.详细的描述就不多说了,官网有更详细的内容.简单来说,k8s,是一个可以操作多台机器调度部署镜 ...