C++算法:有向无环图拓扑排序(领接链表)

文章目录

  • 前言
  • 一、邻接表
  • 二、代码
    • 1、生成图
    • 2、出度、入度计算
    • 3、拓扑排序
  • 总结


前言

前文有向无环图实现游戏技能树中我们使用了矩阵存储图的关系,可以称之为邻接矩阵。显然,链表也是可以实现的。在图结构入门一文中,我们也提到了链表存储的原理。本文我们就以链表形式来完成这一结构,并进行拓扑排序。


提示:以下是本篇文章正文内容,下面案例可供参考

一、邻接表

我们还是使用前文中的同一张图结构。
在这里插入图片描述

说是以链表形式存储,但顶点我们显然还是可以用一个数组来存储更方便,数组的下标即是顶点编号。那么数组相应下标中就可以用来存储本顶点的邻接表的第一个元素。实际就是一个数组中的链表。在C++中就是list<int> adj[n];,这里n表示顶点总数。

二、代码

当然,如果list<int> adj[n];这么写看着不舒服,也可以写成vector<list<int>> adj; 使用向量更直观一点,虽然理论上使用原生数组会略微快一点。实际使用如果顶点数量不多,应该没多大差距。图的邻接关系我们继续沿用前文的int arr[11][2] = {{0,1},{0,4},{1,5},{5,4},{4,7},{4,8},{3,6},{6,7},{8,7},{5,8},{2}};

1、生成图

代码如下(示例):

#include <iostream>
#include <list>
#include <stack>
#include <queue>

using namespace std;

class Graph{
    private:
        int vertex, idx=0; //顶点数、顶点下标 
        int* visited;    //存储是否已访问
        int* indegree;   //存储入度
        int* outdegree;  //存储出度
        list<int>* adj;  //存储邻接链表

    public:
        Graph(const int n, int arr[][2]){
            vertex = n;
            adj = new list<int>[vertex];          //这是数组中的链表
            visited = new int[vertex];                     
            indegree = new int[vertex];          
            outdegree = new int[vertex];
            for (int i=0; i<11; i++){               //生成邻接表
                adj[arr[i][0]].push_back(arr[i][1]);
            }
            adj[2].remove(0);    //补最后一个{2}的问题,默认会认成{2,0}
            find_indegree();
            find_outdegree();
        }
        ~Graph(){
            delete[] toposort;
            delete[] outdegree;
            delete[] indegree;
            delete[] visited;
            delete[] adj;
        }

这一部分与前文差大的差别就在于,没有了矩阵matrix,替换成了adj这个存储list的数组,以及生成邻接关系的代码,这里采用了adj[arr[i][0]].push_back(arr[i][1]);其实就是一个list。我们往数组相应下标(表示了顶点编号)的list中,添加此下标代表的顶点所连接到的顶点编号。这里要是理解不能的话,去看前文图结构入门中的存储原理图。
其余数组变量与前文是基本一样的,笔者这里也是代码复用了一波~

2、出度、入度计算

在拓扑排序前要先计算各顶点的出度和入度。在构造函数中,已经有了调用函数:

        void find_indegree(){
            for (int i=0; i<vertex; i++){
                for (auto v: adj[i]){
                    indegree[v]++;
                }
            }
        }

        void find_outdegree(){
            for (int i=0; i<vertex; i++){
                outdegree[i] = adj[i].size();
            }
        }

这里的计算方法和邻接矩阵不同,入度计算我们要去数组中找每个list是否有此顶点,有就代表被连接了。所以我们去统计每个list中所有顶点出现多少次即可。
出度计算就更容易了,直接去对应数组下标,此list的长度就是出度,代表此顶点连接了几个顶点。

3、拓扑排序

前文也演示了一种用深度优先搜索的拓扑排序方法,这里肯定不能再用同样的方法了。我们换成前面提到过的,从出度为0的顶点开始,每找到一个0出度的顶点,我们就将它剔除,并将与此顶点相连的所有顶点出度减一。循环操作,即可完成拓扑排序。
代码如下(示例):

        int* toposort = new int[9];   //生成排序完成存储的数组
        stack<int> s;    //栈用来倒顶点顺序
        queue<int> q;    //队列用来存储过程顶点
        void topo(){     //第一次搜索没有出度的顶点
            for (int i=0; i<vertex; ++i){
                if (outdegree[i]==0){
                    q.push(i);    //入队
                }
            }
            for (int i=0; i<vertex; ++i){   //循环搜索没有出度的顶点
                int tmp = q.front();  
                s.push(tmp);      //入栈
                q.pop();          //出队
                outdegree[tmp] = -1;   //用-1定义已访问
                for (int j=0; j<vertex; ++j){
                    for (int v : adj[j]){        //数组中的list中搜索
                        if (v == tmp){          //如果搜索到已剔除的顶点
                            outdegree[j]--;     //当前下标表示的顶点出度减1
                            if (outdegree[j] == 0){
                                q.push(j);      //减1后如果没有出度了,就入队
                            }
                        }
                    }
                }
            }
            while (s.size()){   //从栈中倒出到结果数组
                int tmp = s.top();
                s.pop();
                toposort[idx++] = tmp;
            }
        }

上面代码就是拓扑排序的过程,这就要比矩阵深度搜索的方法复杂得多了。笔者详细注释了代码的运行逻辑。总体思想就是:

  • 第一步先将所有没有出度的顶点压入队列中。
  • 第二次循环开始时,将前面队列中的顶点先取一个到栈中,相应的顶点出队。
  • 将这个被出队的顶点,表示为已排序。如果在链表中找到这个顶点,那么将连接到这个顶点的所有顶点的出度减1。
  • 如果减1出度后的顶点的没有出度了,那就把这顶点也压入队中。
  • 循环完成后,如果图结构中没有环,栈中就存储了所有顶点,将栈倒出即完成拓扑排序。

以上代码没有判断图是否有环,如果要加入判断。只需在出队前判断队列是否为空if q.empty(); 如果队列是空的就表示图中有环路。即使是子图存在环路也一样可以检测到。


总结

笔者在代码中也偷懒了不少地方,比如节点数,输入数据arr的长度有些地方直接写成数字了,当然这并不影响阅读与代码的运行。最后测试的结果与前文的排序结果是不一样的:

int main(){
    int arr[11][2] = {{0,1},{0,4},{1,5},{5,4},{4,7},{4,8},{3,6},{6,7},{8,7},{5,8},{2}};
    Graph t(9,arr);
    t.topo();
    for (int i=0; i<9; i++){
        cout << t.toposort[i] << " ";
    }

}
//0 1 5 4 3 8 6 7 2     排序结果

为了测试方便,直接将 toposort 数组写在 public 中了。这种拓扑排序的结果如图:
在这里插入图片描述
拓扑排序在实际应用中,除了前文简单实现的游戏技能树外,还常用于工程中计划管理中,因为工程计划是有依赖关系的。比如在软件工程中,就可以用有向无环图表示源代码文件之间的依赖关系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/27148.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这里推荐几个前端动画效果网站

1. AnimistaAnimista 是一个 CSS 动画/转场库和在线工具。它有许多现成的 CSS 动画片段可以直接使用,也可以在线定制动画。 网站地址:Animista - On-Demand CSS Animations Library 2. Animate.cssAnimate.css 是一个免费的 CSS 动画库,里面有 Attention Seekers 、 Bouncing E…

android 如何分析应用的内存(五)

android 如何分析应用的内存&#xff08;五&#xff09; 接上文 lldb的工具篇的GUI部分。分成两部分&#xff1a; vscode 的LLDBas的LLDB 接下来是as的LLDB as的LLDB 为了进行LLDB的调试&#xff0c;需要对as进行配置&#xff0c;事实上&#xff0c;每一个在AS中编辑的应…

王道考研计算机网络第一章知识点汇总

以上内容为1.1概念与功能的重点知识点 以下为1.2组成与分类&#xff1a; P2P模式下每台主机既可以是客户也可以是服务器&#xff0c;主机越多资源分享速度越快。 1.3标准化工作及相关组织 1.4性能指标 带宽只是指的是从主机内部往传输链路上投送数据的最大能力(从入口端放入数…

【RISCV】RISCV e-906实现Tickless

Tickless 最初设计的思想是,能被任务唤醒,也能被中断唤醒 参考文章: freeRTOS 低功耗模式 和 空闲任务 FreeRTOS源码分析与应用开发09:低功耗Tickless模式 FreeRTOS学习十(低功耗) 【STM32】NVIC与中断控制 之 sysTick定时器 M3,M4实现tickleess的做法: M3,M4的机制:…

【ROS2】使用摄像头功能包 usb_cam

1、准备工作 因为本人使用VirtualBox虚拟机运行的ROS2&#xff0c;所以首先要让摄像头可以在虚拟机中运行 1.1 安装VirtualBox扩展包 1&#xff09;下载地址&#xff1a;https://www.virtualbox.org/wiki/Downloads&#xff0c;注意扩展包的版本要和虚拟机的版本匹配 2&…

基于STM32F103C8T6的超声波测距——串口输出

文章目录 前言一、超声波模块介绍1、产品特点2、超声波模块的时序图 二、STM32CubeMx创建工程1、配置项目2、keil代码设置3、效果 三、总结四、参考资料 前言 环境&#xff1a; 1、硬件&#xff1a;stm32f103c8t6 核心板 2、软件&#xff1a;STM32CubeMX 6.4.0 3、软件&#xf…

世界研发管理组织在美国成立,中国籍研发管理专家江新安当选总干事

World R&D Management Organization世界研发管理组织&#xff08;WRDMO&#xff09;由来自世界各地的研发管理研究组织&#xff0c;创新技术研究机构&#xff0c;院校以及研发管理咨询机构联合发起。是一个具有开放性&#xff0c;无党派性&#xff0c;非营利性的国际先进研…

第七章 Electron Vue3实现音乐播放器

一、介绍 &#x1f351; &#x1f351; &#x1f351; 一个音乐播放器应该具备播放、暂停、上一首、下一首、播放模式&#xff08;单曲循环、列表循环、顺序播放……&#xff09;。除了这些比如还可以扩展进度条的展示、拖拽、音量大小的调节&#xff0c;如果资源允许的话可以…

企业工程项目管理系统源码-全面的工程项目管理

​ ​工程项目管理系统是指从事工程项目管理的企业&#xff08;以下简称工程项目管理企业&#xff09;受业主委托&#xff0c;按照合同约定&#xff0c;代表业主对工程项目的组织实施进行全过程或若干阶段的管理和服务。 如今建筑行业竞争激烈&#xff0c;内卷严重&#xff0c…

chatgpt赋能python:Python循环间隔-了解如何在循环中增加延时

Python循环间隔 - 了解如何在循环中增加延时 在Python编程中&#xff0c;循环是非常常见且重要的控制语句。 它使我们可以多次执行代码块。 但是&#xff0c;在有些情况下&#xff0c;您可能需要在循环之间增加一定的延时时间。 这就是Python循环间隔的概念。 在本文中&#x…

Linux系统下SQLite创建数据库, 建表, 插入数据保姆级教程

1,创建数据库: sqlite test.db 我这边是sqlite2版本, 直接使用命令sqlite test.db创建一个名称为test的数据库; test是你自定义是数据库名, 创建好数据库后, 接下来开始创建表格 2.创建表格, 就是常规的sql建表语句 CREATE TABLE ids_logs ( english_details TEXT, chines…

嵌入式软件工程师培训:提升技能、实现卓越

如果您对嵌入式培训感兴趣&#xff0c;以下是一些建议和关键点&#xff0c;可以帮助您进行嵌入式培训&#xff1a; 培训目标&#xff1a;明确确定您的嵌入式培训目标。是为了提升员工的技能水平&#xff0c;使他们能够承担更高级别的嵌入式开发工作&#xff0c;还是为了向非嵌入…

iOS App的打包和上架流程

转载&#xff1a;iOS App的打包和上架流程 - 掘金 1. 创建账号 苹果开发者账号几种开发者账号类型 个人开发者账号 费用&#xff1a;99 美元/年&#xff08;688.00元&#xff09;协作人数&#xff1a;仅限开发者自己不需要填写公司的邓百氏编码&#xff08; D-U-N-S Number…

网络安全:信息收集专总结【社会工程学】

前言 俗话说“渗透的本质也就是信息收集”&#xff0c;信息收集的深度&#xff0c;直接关系到渗透测试的成败&#xff0c;打好信息收集这一基础可以让测试者选择合适和准确的渗透测试攻击方式&#xff0c;缩短渗透测试的时间。 一、思维导图 二、GoogleHacking 1、介绍 利用…

大数据需要学习哪些内容?

大数据技术的体系庞大且复杂&#xff0c;每年都会涌现出大量新的技术&#xff0c;目前大数据行业所涉及到的核心技术主要就是&#xff1a;数据采集、数据存储、数据清洗、数据查询分析和数据可视化。 Python 已成利器 在大数据领域中大放异彩 Python&#xff0c;成为职场人追求…

甘孜州文化旅游产品市场营销策略研究_kaic

甘孜州文化旅游产品市场营销策略研究 摘要&#xff1a; 近年来&#xff0c;随着文化旅游的兴起&#xff0c;越来越多的旅游者渴望精神层面的满足&#xff0c;获得新奇的文化体验&#xff0c;而我国文化旅游仍处于单层次的观赏旅游。本文研究背景包括对旅游行业的背景介绍&#…

【第三章:链路层】

目录 知识框架No.0 引言No.1 功能零、基本功能概念一、封装成帧1、字符计数法2、字符填充法3、零比特填充法4、违规编码法 二、透明传输三、差错控制1、位错1.1、奇偶校验码1.2、循环冗余码CRC2、帧错2.1、海明码 四、流量控制1、停止-等待协议2、滑动窗口协议2.1、后退N帧协议…

【新星计划回顾】第五篇学习计划-数据库开启定时任务知识点

&#x1f3c6;&#x1f3c6;时间过的真快&#xff0c;这是导师回顾新星计划学习的第五篇文章&#xff01;本篇文章主要是承接上一篇学习计划&#xff0c;通过开启定时任务进行模拟生成数据&#xff0c;实际开发项目中&#xff0c;可能会用到其他方式&#xff01; 最近这段时间非…

Dockerfile应用的容器化

文章目录 Dockerfile应用的容器化应用的容器化——简介应用的容器化——详解单体应用容器化获取应用代码分析Dockerfile容器化当前应用/构建具体的镜像推送镜像到仓库运行应用程序测试总结 最佳实践利用构建缓存合并镜像 命令总结 Dockerfile应用的容器化 Docker 的核心思想是…

软件测试之路已不再是坦途

去年下半年才跳了槽&#xff0c;过程非常顺利&#xff0c;没有经历大家所说的工作荒的境地&#xff0c;所以一直没有直观地感受到软件测试就业形势到底有多严峻。 近来看到一些机构频频发出某某测试员在糟糕的就业形势下逆袭拿下XXW的某厂offer&#xff0c;然后推荐测试进阶课…