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

news2024/11/27 15:29:59

文章目录

  • 前言
  • 一、邻接表
  • 二、代码
    • 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 中了。这种拓扑排序的结果如图:
在这里插入图片描述
拓扑排序在实际应用中,除了前文简单实现的游戏技能树外,还常用于工程中计划管理中,因为工程计划是有依赖关系的。比如在软件工程中,就可以用有向无环图表示源代码文件之间的依赖关系。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

湖南大学OS-2020(另一张)期末考试解析

【特别注意】 答案来源于wolf以及网络 是我在备考时自己做的&#xff0c;仅供参考&#xff0c;若有不同的地方欢迎讨论。 【试卷评析】 这张卷子很老了&#xff0c;我不知道具体的年份&#xff0c;部分题目可能有用。如果仔细研究应该会有所收获。 【试卷与答案】 一、选…

机器学习 | 决策树 Decision Tree | 概念向

参考视频&#xff1a;【小萌五分钟】机器学习 | 决策树 文章目录 &#x1f4da;决策树是什么&#xff08;根节点、叶子节点、分支、深度&#xff09;&#x1f4da;决策树&#xff1a;分类树——算法思想&#x1f4da;分类错误率&#xff0c;熵&#xff0c;基尼指数&#x1f407…

蓝库云|实体店搭建一套巡店管理系统,能让大型连锁店立竿见影

传统巡店工作存在许多问题特别是大型连锁店&#xff0c;包括工作效率低、数据收集不便捷、信息共享困难等。为了解决这些问题&#xff0c;蓝库云认为拥有一套巡店管理系统就显得非常重要了。 巡店管理系统具备以下特点&#xff1a;手机电脑数据同步、实时数据采集和记录、可定…

谷粒商城学习笔记(二):简介- 谷粒商城项目微服务架构图

目录 项目前置知识一、前后端分离开发&#xff0c;分为 内网部署 和 外网部署。二、用户是通过使用 客户端 来完成各种的功能三、网关的作用四、Sentiel组件五、Feign组件六、OAuth2.0认证中心七、SpringSecurity组件八、关于数据存储的解决方案九、定位bug十、注册中心十一、配…

IDEA 远程 Debug 调试,你可以不需要,但是要会

文章目录 前言配置IDEA设置启动脚本改造 细节细节1&#xff1a;停在本地断点&#xff0c;关闭程序后会继续执行吗细节2&#xff1a;jar包代码和本地不一致会怎么样&#xff1f;细节3&#xff1a;日志打印在哪里&#xff1f;细节4&#xff1a;调试时其他人会不会卡住&#xff1f…

湖南大学OS-2022期末考试解析

【特别注意】 答案来源于不确定的光子和wolf 是两位同学在备考时自己做的&#xff0c;仅供参考&#xff0c;若有不同的地方欢迎讨论。 【试卷评析】 这张卷子比较正&#xff0c;个人感觉能够代表接下来几年的考试方向。如果仔细研究应该会有所收获。 【试卷与答案】 一、&…

网络安全认证CISP、CISSP对比及分析

好多网络安全行业或者打算转行该行业的人在纠结学是考取CISP认证还是CISSP认证&#xff0c;作为从事多年网络安全培训认证业务的专家&#xff0c;对这两个认证还是非常了解的&#xff0c;下面是这两个认证的一个对比说明&#xff0c;希望能够帮助到大家更好地选择到适合自己的认…

湖南大学OS-2020期末考试解析

【特别注意】 答案来源于wolf以及网络 是我在备考时自己做的&#xff0c;仅供参考&#xff0c;若有不同的地方欢迎讨论。 【试卷评析】 这张卷子有点老了&#xff0c;部分题目可能有用。如果仔细研究应该会有所收获。 【试卷与答案】 一、选择题&#xff08;15%&#xff…

基于SSM+Vue的房屋租赁网站-求租合同设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

“混合办公”已来!

不知不觉中&#xff0c;你已经开启“未来办公模式”了。这听起来好像有些疯狂&#xff0c;但事实就是如此&#xff01; 试想一下&#xff0c;近三年&#xff0c;你是不是很少去办公室&#xff0c;但却没有耽误工作&#xff1f;你有多少次是在出差的途中开视频会议&#xff0c;…

5分钟了解Kubernetes Ingress和Gateway API

了解Kubernetes Ingress和Gateway API之间的差异&#xff0c;以实现有效的流量管理。原文: Kubernetes Ingress Vs Gateway API Ingress vs Gateway API 概述 Kubernetes如今被广泛应用于容器管理、微服务编排解决方案。对于如何控制微服务的入口流量&#xff0c;Kubernetes提供…

未来制鞋厂的智能化改造: RFID技术的应用

近年来&#xff0c;RFID&#xff08;射频识别&#xff09;的应用在生产、物流、零售等方面得到了广泛的应用。在制鞋厂中&#xff0c;RFID的应用也越来越受到关注。 RFID技术可以让制鞋厂实现对鞋子的全程跟踪&#xff0c;从原材料的采购到鞋子的成品出货&#xff0c;都可以实…

OpenAI 新发布GPT 最佳实践;WordPress新增 AI写作助手;国产语言模型TigerBot发布

&#x1f989; AI新闻 &#x1f680; WordPress新增Jetpack AI Assistant AI写作助手 摘要&#xff1a;Automattic公司宣布&#xff0c;为其WordPress新增AI写作助手–Jetpack AI Assistant。该工具可根据用户提示撰写博文、详情页、结构化列表和表格&#xff0c;支持多种口吻…

代码质量规范测量

圈复杂度介绍 圈复杂度(Cyclomatic complexity)是一种代码复杂度的衡量标准&#xff0c;在1976年由Thomas J. McCabe, Sr. 提出。在软件测试的概念里&#xff0c;圈复杂度用来衡量一个模块判定结构的复杂程度&#xff0c;数量上表现为线性无关的路径条数&#xff0c;即合理的预…

脑洞大开:“Excel+中文编程”衍生新型软件,WPS用户:自家孩子

中国人脑洞有多大&#xff1f; 有时候&#xff0c;你不得不承认&#xff0c;中国人的脑洞有时候真是让人意想不到&#xff1a; 你只知道Excel表格&#xff0c;你也知道中文编程&#xff0c;但是你有没有想过&#xff0c;用Excel和中文编程嫁接起来的话&#xff0c;会衍生出一…

sessionStorage、localStorage、cookie你真的会用么

文章目录 前言会话存储&#xff1a;本地存储&#xff1a; 一、sessionStorage介绍使用演示存储数据到sessionStorage从sessionStorage中获取数据删除sessionStorage中的某个键值对清空sessionStorage中的所有数据从Chrome查看sessionStorage的存储情况 二、localStorage介绍使用…

最新 Adobe Photoshop AI (Beta) 下载安装教程

Adobe最近推出了新的Adobe Photoshop AI&#xff08;Beta&#xff09;&#xff0c;这是一款融合了生成性AI和Adobe Firefly的新功能。 能够根据用户的自然语言提示&#xff0c;创建出极其出色的图像。这些提示可以用来添加内容、删除或替换图像的某部分&#xff0c;甚至扩展图…

Flutter:动画

前言 学习参考&#xff1a;老孟 flutter动画 基本上开发时使用的组件都有其动画&#xff0c;关于动画方面的知识&#xff0c;一般情况很少会用到。因此这里只学习关于动画的基本知识。 AnimationController Flutter中的AnimationController是一个用于控制动画的类。它可以控…

深入理解Qt多线程编程:QThread、QTimer与QAudioOutput的内在联系__Qt 事件循环

深入理解Qt多线程编程&#xff1a;QThread、QTimer与QAudioOutput的内在联系__QObject的主线程的事件循环 1. Qt多线程编程的基础1.1 QObject和线程&#xff08;QObject and Threads&#xff09;1.2 QThread的使用和理解&#xff08;Understanding and Using QThread&#xff0…

ubuntu下安装transition_amr_parser

ubuntu下安装transition_amr_parser transition_amr_parser是IBM公司开源的AMR paraing和AMR text-to-generation工具&#xff0c;在NLP领域中经常会用到&#xff0c;但是这个安装过程中可能会存在很多坑&#xff0c;transition_amr_parser的github主页安装教程不清晰&#xf…