如何在 Java 中实现无向环和有向环的检测
无向环
一个含有环的无向图如下所示,其中有两个环,分别是 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 中实现无向环和有向环的检测的更多相关文章
- 如何在 Java 中实现最小生成树算法
定义 在一幅无向图 \(G=(V,E)\) 中,\((u, v)\) 为连接顶点 \(u\) 和顶点 \(v\) 的边,\(w(u,v)\) 为边的权重,若存在边的子集 \(T\subseteq E\ ...
- 如何在JAVA中实现一个固定最大size的hashMap
如何在JAVA中实现一个固定最大size的hashMap 利用LinkedHashMap的removeEldestEntry方法,重载此方法使得这个map可以增长到最大size,之后每插入一条新的记录 ...
- 如何在java中使用sikuli进行自动化测试
很早之前写过一篇介绍sikuli的文章.本文简单介绍如何在java中使用sikuli进自动化测试. 图形脚本语言sikuli sikuli IDE可以完成常见的单击.右击.移动到.拖动等鼠标操作,ja ...
- 如何在Java中调用Python代码
有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调 ...
- 如何在java中跳出当前多重嵌套循环?有几种方法?
如何在java中跳出当前多重嵌套循环?有几种方法? - 两种方法 - 1.在外层循环定义标记 ok: for(int i=0;i<100;i++){ ...
- 用代码说话:如何在Java中实现线程
并发编程是Java语言的重要特性之一,"如何在Java中实现线程"是学习并发编程的入门知识,也是Java工程师面试必备的基础知识.本文从线程说起,然后用代码说明如何在Java中实现 ...
- 如何在Java中测试类是否是线程安全的
通过优锐课的java核心笔记中,我们可以看到关于如何在java中测试类是否线程安全的一些知识点汇总,分享给大家学习参考. 线程安全性测试与典型的单线程测试不同.为了测试一个方法是否是线程安全的,我们需 ...
- 如何在pyqt中自定义无边框窗口
前言 之前写过很多关于无边框窗口并给窗口添加特效的博客,按照时间线罗列如下: 如何在pyqt中实现窗口磨砂效果 如何在pyqt中实现win10亚克力效果 如何在pyqt中通过调用SetWindowCo ...
- 如何在pyqt中给无边框窗口添加DWM环绕阴影
前言 在之前的博客<如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果>中,我们实现了窗口的亚克力效果,同时也用SetWindowC ...
随机推荐
- PhpStrom 好用的代码小地图插件
类似SublimeText的Mini Map插件 ,废话不多直接上 安装 打开File -> Settings -> Plugins -> 搜索CodeGlance -> in ...
- React学习小结(一)
一.React的发展 facebook在构建instagram网站的时候遇见两个问题: 1.数据绑定的时候,大量操作真实dom,性能成本太高 2.网站的数据流向太混乱,不好控制 于是facebook起 ...
- 从读写角度,带你了解数仓的IO基本框架
摘要:本文从读取和写入的角度分别描述了行存和列存的IO模型,并对文件结构做了简单介绍. 本文分享自华为云社区<GaussDB(DWS)基本IO框架>,作者: Naibaoofficial. ...
- 13、mysql锁
mysql锁 事务的隔离性是通过锁来实现的.为保证数据的一致性,需要锁对并发事务操作进行控制.同时锁机制也为实现MySQL的各个隔离级别提供了保证. mysql并发事务访问相同的记录会出现什么问题(在 ...
- angular批量上传图片并进行校验
最近做一个项目,有一个批量上传图片,并校验是否是图片和上传最大数量的需求.下面分享一下我的想法,欢迎大神探讨. 首先是在html中支持批量上传和接收图片(mac下设置了之后不存在类型校验),下面上ht ...
- 巧用"记事本"让病毒无效运行
电脑中毒后,许多朋友会打开"进程管理器",将几个不太熟悉的程序关闭掉,但有时会碰到这种情况:关掉一个, 再去关闭另外一个时,刚才关闭的那个马上又运行了.再从注册表里先把启动项删除后 ...
- 搭建Loki、Promtail、Grafana轻量级日志系统(centos7)
搭建Loki.Promtail.Grafana轻量级日志系统(centos7)--简称PLG 需求 公司项目采用微服务的架构,服务很多,每个服务都有自己的日志,分别存放在不同的服务器上.当查找日志时需 ...
- zookeeper可视化WEB工具(zkui)搭建与配置
前提:zookeeper 可视化WEB工具zkui依赖java环境,因此需要安装jdk,同时zkui源码要Maven编译,需要安装apache-maven. JDK下载地址:https://www.o ...
- IIS短文件猜解
1.IIS短文件漏洞 Microsoft IIS 短文件/文件夹名称信息泄漏最开始由Vulnerability Research Team(漏洞研究团队)的Soroush Dalili在2010年8月 ...
- python练习册 每天一个小程序 第0012题
# -*-coding:utf-8-*- def test(content): text = content flag = 0 with open('filtered_words.txt') as f ...