【数据结构与算法】第十五、十六章:有向图、拓扑排序(检测环、顶点排序)

本文主要是介绍【数据结构与算法】第十五、十六章:有向图、拓扑排序(检测环、顶点排序),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

15、有向图

15.1、有向图的定义及相关术语

定义:有向图是一副具有方向性的图,是由一组顶点和一组有方向的边组成的,每条方向的边都连着一对有序的顶点

  • 出度

    由某个顶点指出的边的个数称为该顶点的出度

  • 入度

    指向某个顶点的边的个数称为该顶点的入度

  • 有向路径

    由一系列顶点组成,对于其中的每个顶点都存在一条有向边,从它指向序列中的下一个顶点

  • 有向环

    一条至少含有一条边,且起点和终点相同的有向路径

在这里插入图片描述

一副有向图中两个顶点v和w可能存在以下四种关系:

  1. 没有边相连
  2. 存在从v到w的边 v—>w
  3. 存在从w到v的边 w—>v
  4. 既存在w到v的边,也存在v到w的边,即双向连接

在这里插入图片描述

理解有向图是一件比较简单的,但如果要通过眼睛看出复杂有向图中的路径就不是那么容易了

15.2、有向图API设计

在这里插入图片描述

在api中设计了一个反向图,因为有向图的实现中,用adj方法获取出来的是由当前顶点v指向的其他顶点,如果能得到其反向图,就可以很容易得到指向v的其他顶点

package chapter15;import chapter03.Queue;/*** @author 土味儿* Date 2021/9/15* @version 1.0* 有向图*/
public class Digraph {/*** 顶点数量*/private final int vNum;/*** 边数量*/private int eNum;/*** 邻接表*/private Queue<Integer>[] adj;/*** 构造器** @param vNum*/public Digraph(int vNum) {// 初始化顶点数量this.vNum = vNum;// 初始化边数量this.eNum = 0;// 初始化邻接表this.adj = new Queue[vNum];// 初始化邻接表中的空队列for (int i = 0; i < vNum; i++) {this.adj[i] = new Queue<Integer>();}}/*** 得到顶点数量** @return*/public int getVNum() {return vNum;}/*** 得到边数量** @return*/public int geteNum() {return eNum;}/*** 添加一条边v-w** @param v* @param w*/public void addEdge(int v, int w) {// 因为是有向图,w加入到v的链表中this.adj[v].enQueue(w);//this.adj[w].enQueue(v);// 边数量加1eNum++;}/*** 获取顶点v指出的边的所有顶点** @param v* @return*/public Queue<Integer> adj(int v) {return this.adj[v];}/*** 该图的反向图** @return*/private Digraph reverse() {// 创建有向图对象Digraph r = new Digraph(vNum);// 遍历所有的顶点for (int v = 0; v < vNum; v++) {// 遍历当前顶点的邻接表for (Integer w : adj(v)) {// 在反向有向图中填加反向边r.addEdge(w,v);}}return r;}
}

16、拓扑排序

在现实生活中,经常会同一时间接到很多任务去完成,但是这些任务的完成是有先后次序的。以学习java学科为例,需要学习很多知识,但是这些知识在学习的过程中是需要按照先后次序来完成的。从java基础,到jsp/servlet,到ssm,到springboot等是个循序渐进且有依赖的过程。在学习jsp前要首先掌握java基础和html基础,学习ssm框架前要掌握jsp/servlet之类才行

在这里插入图片描述

为了简化问题,使用整数为顶点编号的标准模型来表示这个案例:

在这里插入图片描述

此时如果某个同学要学习这些课程,就需要指定出一个学习的方案,只需要对图中的顶点进行排序,让它转换为一个线性序列,就可以解决问题,这时就需要用到一种叫拓 扑排序的算法。

拓扑排序: 给定一副有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素,此时就可以明确的表示出每个顶点的优先级

下列是一副拓扑排序后的示意图:

在这里插入图片描述

16.1、检测有向图中的环

如果学习x课程前必须先学习y课程,学习y课程前必须先学习z课程,学习z课程前必须先学习x课程,那么一定是有问题了,就没有办法学习了,因为这三个条件没有办法同时满足。其实这三门课程x、y、z的条件组成了一个环:

在这里插入图片描述

因此,如果要使用拓扑排序解决优先级问题,首先得保证图中 没有环的存在

1)检测有向环的API设计

在这里插入图片描述

2)检测有向环实现

在API中添加了onStack[] 布尔数组,索引为图的顶点,当深度搜索时:

  1. 如果当前顶点正在搜索,则把对应的onStack数组中的值改为true,标识进栈
  2. 如果当前顶点搜索完毕,则把对应的onStack数组中的值改为false,标识出栈
  3. 如果即将要搜索某个顶点,但该顶点已经在栈中,则图中有环

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package chapter16;import chapter14.Graph;
import chapter15.Digraph;/*** @author 土味儿* Date 2021/9/16* @version 1.0* 有向环检测*/
public class DirectedCycle {/*** 记录某个顶点是否被搜索过* 索引表示顶点* 值true:搜索过* 值false:未搜索过*/private boolean[] marked;/*** 记录图中是否有环*/private boolean hasCycle;/*** 索引代表顶点,使用栈的思想* 记录当前顶点有没有已经处于正在搜索的有向路径上*/private boolean[] onStack;/*** 构造器* 创建一个检测环对象,检测图g中是否有环** @param g*/public DirectedCycle(Digraph g) {// 初始化marked数组this.marked = new boolean[g.getVNum()];// 初始化hasCyclethis.hasCycle = false;// 初始化onStack数组this.onStack = new boolean[g.getVNum()];// 找到图中每一个顶点,让每一个顶点作为入口,调用dfs方法for (int v = 0; v < g.getVNum(); v++) {// 如果当前顶点没有被搜索过,就调用dfsif (!marked[v]) {dfs(g, v);}}}/*** 基于深度优先搜索,检测图g中是否有环** @param g* @param v*/private void dfs(Digraph g, int v) {// 把顶点v表示为已搜索过marked[v] = true;// 把顶点v进栈onStack[v] = true;// 进行深度搜索for (Integer w : g.adj(v)) {// 如果当前顶点w没有被搜索过,就递归调用dfsif(!marked[w]){dfs(g,w);}// 判断当前顶点w是否在栈中,如果已经在栈中,说明之前处于正在搜索状态,现在再次搜索,证明已经有环了if(onStack[w]){hasCycle = true;return;}}// 当前顶点出栈onStack[v] = false;}/*** 判断图中是否有环** @return*/public boolean hasCycle() {return hasCycle;}
}

16.2、基于深度优先的顶点排序

如果要把图中的顶点生成线性序列其实是一件非常简单的事,之前使用了多次深度优先搜索,会发现其实深度优先搜索有一个特点,那就是在一个连通子图上,每个顶点只会被搜索一次,如果能在深度优先搜索的基础上,添加一行代码,只需要将搜索的顶点放入到线性序列的数据结构中,就能完成这件事

1)顶点排序API设计

在这里插入图片描述

2)顶点排序实现

在API的设计中,添加了一个栈reversePost用来存储顶点, 当深度搜索图时,每搜索完毕一个顶点,把该顶点放入到reversePost中,这样就可以实现顶点排序

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package chapter16;import chapter03.Stack;
import chapter15.Digraph;/*** @author 土味儿* Date 2021/9/16* @version 1.0* 深度优先顶点排序*/
public class DepthFirstOrder {/*** 记录某个顶点是否被搜索过* 索引表示顶点* 值true:搜索过* 值false:未搜索过*/private boolean[] marked;/*** 使用栈,存储顶点序列*/private Stack<Integer> reversePost;/*** 构造器* 创建一个顶点排序对象,生成顶点线性序列** @param g*/public DepthFirstOrder(Digraph g) {// 初始化markedthis.marked = new boolean[g.getVNum()];// 初始化reversePostthis.reversePost = new Stack<Integer>();// 遍历图中每一个顶点,让每个顶点作为入口,进行深度优先搜索for (int v = 0; v < g.getVNum(); v++) {if(!marked[v]){dfs(g,v);}}}/*** 获取顶点线性序列** @return*/public Stack<Integer> getReversePost() {return reversePost;}/*** 基于深度优先,生成顶点线性序列** @param g* @param v*/private void dfs(Digraph g, int v) {// 把顶点v表示为已搜索过this.marked[v] = true;// 进行深度搜索for (Integer w : g.adj(v)) {// 如果当前顶点没有被搜索过,递归dfsif(!marked[w]){dfs(g,w);}}// 让顶点v进栈reversePost.push(v);}
}

16.3、拓扑排序实现

基于一幅图,先检测有没有环,如果没有环, 则调用顶点排序即可

  • API

在这里插入图片描述

  • 代码
package chapter16;import chapter03.Stack;
import chapter15.Digraph;/*** @author 土味儿* Date 2021/9/16* @version 1.0* 拓扑排序*/
public class TopoLogical {/*** 顶点的拓扑排序*/private Stack<Integer> order;/*** 构造器* 构建拓扑排序对象* @param g*/public TopoLogical(Digraph g) {// 创建一个有向环检测对象DirectedCycle cycle = new DirectedCycle(g);// 判断是否有环;如果没有环,创建一个顶点排序对象,进行排序if(!cycle.hasCycle()){order = new DepthFirstOrder(g).getReversePost();}}/*** 判断g图中是否有环* @return*/public boolean isCycle(){return order == null;}/*** 获取拓扑排序的所有顶点* @return*/public Stack<Integer> getOrder(){return this.order;}
}
  • 测试
package chapter16;import chapter15.Digraph;
import org.junit.Test;/*** @author 土味儿* Date 2021/9/16* @version 1.0* 拓扑排序测试*/
public class TopoLogicalTest {@Testpublic void test(){// 准备有向图Digraph digraph = new Digraph(6);digraph.addEdge(0,2);digraph.addEdge(0,3);digraph.addEdge(1,3);digraph.addEdge(2,4);digraph.addEdge(3,4);digraph.addEdge(4,5);// 通过拓扑排序对有向图进行顶点排序TopoLogical topoLogical = new TopoLogical(digraph);// 输出顶点for (Integer w : topoLogical.getOrder()) {System.out.print(w +" ");}}
}
1 0 3 2 4 5 

这篇关于【数据结构与算法】第十五、十六章:有向图、拓扑排序(检测环、顶点排序)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/942794

相关文章

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

烟火目标检测数据集 7800张 烟火检测 带标注 voc yolo

一个包含7800张带标注图像的数据集,专门用于烟火目标检测,是一个非常有价值的资源,尤其对于那些致力于公共安全、事件管理和烟花表演监控等领域的人士而言。下面是对此数据集的一个详细介绍: 数据集名称:烟火目标检测数据集 数据集规模: 图片数量:7800张类别:主要包含烟火类目标,可能还包括其他相关类别,如烟火发射装置、背景等。格式:图像文件通常为JPEG或PNG格式;标注文件可能为X

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费