数据结构-考研难点代码突破 (C++实现有向无环图的拓扑排序)

news2024/10/5 3:18:05

文章目录

  • 1. AOE网
  • 2. 拓扑排序
    • C++代码

1. AOE网

AOV网∶若用DAG 图(有向无环图)表示一个工程,其顶点表示活动,用有向边<Vi,Vj>表示活动 Vi必须先于活动Vj进行的这样一种关系,则将这种有向图称为顶点表示活动的网络,记为 AOV网。

在AOV网中,活动Vi是活动Vj的直接前驱,活动Vj是活动Vi的直接后继,这种前驱和后继关系具有传递性,且任何活动V不能以它自己作为自己的前驱或后继

2. 拓扑排序

拓扑排序∶在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序∶

  1. 每个顶点出现且只出现一次。
  2. 若顶点A在序列中排在顶点B的前面,则在图中不存在从顶点B到顶点A的路径。

或定义为∶
拓扑排序是对有向无环图的顶点的一种排序,它使得若存在一条从顶点A到顶点
B的路径,则在排序中顶点B出现在顶点A的后面。

每个AOV 网都有一个或多个拓扑排序序列。
对一个AOV 网进行拓扑排序的算法有很多,下面介绍比较常用的一种方法的步骤:

  1. 从AOV网中选择一个没有前驱的顶点并输出。
  2. 从网中删除该顶点和所有以它为起点的有向边。
  3. 重复1和2直到当前的AOV网为空或当前网中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。

eg:
在这里插入图片描述
拓扑排序结果1,2,4,3,5

C++代码

图采用邻接表储存,

由于输出每个顶点的同时还要删除以它为起点的边,故采用邻接表存储时拓扑排序的时间复杂度为O(V+E),采用邻接矩阵存储时拓扑排序的时间复杂度为 O(V2

#include <iostream>
#include <vector>
#include <assert.h>
#include <unordered_map>
#include <stack>

template <class w>
struct Edge
{
    int srcPos = -1;
    w weight; // 权值
    Edge<w> *next;
    Edge(int _srcPos, const w &_weight) : srcPos(_srcPos), weight(_weight), next(nullptr) {}
};

// v:节点的值,w节点的权值 flag==false为无向图
template <class v, class w, bool flag = false>
class linkTable
{
    typedef Edge<w> Edge;

private:
    std::vector<Edge *> _matrix;          // 邻接表
    std::unordered_map<v, int> _indexMap; // 保存图节点对应邻接表数组的下标
    std::vector<v> _points;               // 顶点集合

    int _getPointPos(const v &point)
    {
        typename std::unordered_map<v, int>::iterator pos = _indexMap.find(point);
        if (pos == _indexMap.end())
            return -1; // 没找到
        return pos->second;
    }

public:
    linkTable(const std::vector<v> &src)
    {
        int size = src.size();
        assert(size > 0);
        _points.resize(size);
        for (int i = 0; i < size; i++)
        {
            _points[i] = src[i];
            _indexMap[src[i]] = i;
        }
        _matrix.resize(size, nullptr);
    }

    // 添加边的关系,储存入度边
    void AddEdge(const v &src, const v &dst, const w &weight)
    {
        int posSrc = _getPointPos(src);
        int posDst = _getPointPos(dst);
        assert(posSrc >= 0 && posSrc >= 0);

        // 构建Edge,头插到数组上
        Edge *edge = new Edge(posSrc, weight);
        edge->next = _matrix[posDst];
        _matrix[posDst] = edge;

        if (!flag)
        {
            // 无向图,两条边都要构建
            edge = new Edge(posSrc, weight);
            edge->next = _matrix[posSrc];
            _matrix[posSrc] = edge;
        }
    }

    // 打印邻接表信息
    void PrintGraph()
    {
        for (int i = 0; i < _matrix.size(); i++)
        {
            Edge *edge = _matrix[i];
            while (edge != nullptr)
            {
                std::cout << _points[i] << "<-";
                std::cout << _points[edge->srcPos] << "权值:" << edge->weight << std::endl;
                edge = edge->next;
            }
            std::cout << "--------------------------------" << std::endl;
        }
    }

    //---------------------------拓扑排序-----------------
    /**
     * @brief 返回图的拓扑排序是否完成
     *
     * @param buff 有向无环图的拓扑排序结果数组
     * @return true 这个图是完成了拓扑排序
     * @return false 这个图带环,没有拓扑排序
     */
    bool Topological(std::vector<v> &buff)
    {
        std::stack<v> st;
        std::vector<bool> visit(_points.size(), false); // 判断这个节点是否被访问过
        // 储存入度为0节点
        for (size_t i = 0; i < _matrix.size(); ++i)
        {
            if (_matrix[i] == nullptr)
            {
                st.push(i);
                visit[i] = true;
            }
        }

        int count = 0;
        while (!st.empty())
        {
            int top = st.top();
            buff.push_back(_points[top]);
            st.pop();
            count++;

            // 将所有top指向的顶点度-1,如果度为0继续入栈
            for (size_t i = 0; i < _matrix.size(); i++)
            {
                if (_matrix[i] != nullptr)
                {
                    Edge *node = _matrix[i];
                    Edge *prev = nullptr;
                    while (node != nullptr)
                    {
                        if (node->srcPos == top)
                        {
                            if (prev == nullptr)
                            {
                                // 头删
                                _matrix[i] = node->next;
                                delete node;
                                node = _matrix[i];
                            }
                            else
                            {
                                prev->next = node->next;
                                delete node;
                                node = prev->next;
                            }
                        }
                        else
                        {
                            prev = node;
                            node = node->next;
                        }
                    }
                }
            }
            // 储存入度为0节点
            for (size_t i = 0; i < _matrix.size(); ++i)
            {
                if (_matrix[i] == nullptr && visit[i] == false)
                {
                    st.push(i);
                    visit[i] = true;
                }
            }
        }
        return _points.size() == count;
    }
};
#include "topological.h"

int main(int argc, char const *argv[])
{
    linkTable<char, int, true> graph({'1', '2', '3', '4', '5'});
    graph.AddEdge('1', '4', 1);
    graph.AddEdge('1', '2', 1);
    graph.AddEdge('2', '4', 1);
    graph.AddEdge('2', '3', 1);
    graph.AddEdge('4', '3', 1);
    graph.AddEdge('4', '5', 1);
    graph.AddEdge('3', '5', 1);
    graph.PrintGraph();
    std::vector<char> buff;
    std::cout << graph.Topological(buff) << "\n";
    for (auto num : buff)
    {
        std::cout << num << " ";
    }
    return 0;
}

在这里插入图片描述

对一个AOV网,如果采用下列步骤进行排序,则称之为逆拓扑排序∶

  1. 从AOV网中选择一个没有后继(出度为0)的顶点并输出。
  2. 从网中删除该顶点和所有以它为终点的有向边。
  3. 重复1和2直到当前的AOV网为空。

用拓扑排序算法处理 AOV网时,应注意以下问题;

  1. 入度为零的顶点,即没有前驱活动的或前驱活动都已经完成的顶点,工程可以从这个顶点所代表的活动开始或继续。
  2. 若一个顶点有多个直接后继,则拓扑排序的结果通常不唯一,但若各个顶点已经排在一个线性有序的序列中,每个顶点有唯一的前驱后继关系,则拓扑排序的结果是唯一的。
  3. 由于AOV网中各顶点的地位平等,每个顶点编号是人为的,因此可以按拓扑排序的结果重新编号,生成 AOV 网的新的邻接存储矩阵,这种邻接矩阵可以是三角矩阵但对于一般的图来说,若其邻接矩阵是三角矩阵,则存在拓扑序列;反之则不一定成立

使用DFS算法递归的遍历一个无环有向图,在退出递归时输出响应的顶点,这样得到的序列时这个图的逆拓扑排序序列

图的深度优先遍历有一个特点:
当一个顶点的子结点都被访问完了,该顶点才会结束访问,并开始向上回溯访问它的父结点的其它子结点。

这意味着,一个顶点的结束访问时间与其子结点的结束访问时间存在先后关系,而这个顺序刚好与拓扑排序是相反的!

简单地说,在深度优先遍历中,顶点A要结束访问的前提是其子结点B、C、D…都被访问完了,而在拓扑排序中,事件A完成之后其后面的事件B、C、D…才可以继续进行。这也就是上面的深度优先搜索为什么要记录结点被访问完成的次序,因为这个次序倒过来就是拓扑排序的顺序!

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

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

相关文章

基于主从博弈的智能小区代理商定价策略及电动汽车充电管理(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

C语言贪吃蛇大作战

C语言贪吃蛇大作战 贪吃蛇大作战 1997 年&#xff0c;诺基亚公司发布了贪吃蛇游戏&#xff0c;并将其内置于诺基亚 6110 手机中&#xff0c;使这款游戏迅速风靡全球&#xff0c;成为一代经典。一般的观点认为&#xff0c;贪吃蛇是手机游戏的鼻祖。 与传统单人贪吃蛇不同的是&…

【时间之外】系统管人,能行?(冷眼旁观连载之一)

目录 写作初心 在用工具 某微 某道 某书 工具痛点 某微痛点 某道痛点 某书痛点 总结一下&#xff1a;功能复杂 2023年观察计划 最大痛点 效果跟踪 未完待续 写作初心 2022年应该是这一生中值得纪念的一年&#xff0c;疫情封控自不必说&#xff0c;对于个人而言&a…

traefik gateway api

背景 在使用istio后开始考虑网关了&#xff0c;istio已经有自己的网关&#xff0c;为什么还要另外找一个别的网关&#xff0c;参考了好几个文章大致结论是&#xff0c;istio的网关功能不够强大&#xff0c;下图红色的部分是istio网关暂时缺失的&#xff0c;所以我的结论是在is…

Monorepo 下 Git 工作流的最佳实践

作者&#xff1a;林宜丙 背景 没有哪一种 Git 工作流是银弹&#xff0c;合适的 Git 工作流往往取决于项目的代码规模、协作人数、应用场景等&#xff1b;本次分享先从适合小型 Monorepo 的 Feature branch 工作流开始分享&#xff0c;接着分享适用于中大型 Monorepo 的 Trunk…

头歌:Ping客户端创建原始套接字(底部附全关完整答案)

头歌实践教学平台 (educoder.net)为Ping客户端创建一个原始类型的套接字原始套接字套接字&#xff08;socket&#xff09;是一个抽象层网络应用程序可以通过它发送或接收数据&#xff0c;可对其进行像文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中&a…

<C++>二叉树进阶

文章目录为什么要学这一节1. 二叉搜索树1.1 二叉搜索树概念1.2 二叉搜索树操作1.3 二叉搜索树的实现1.4 二叉搜索树的应用1.5 二叉搜索树的性能分析2. 经典题目2.1 最近公共祖先2.2 从前序与中序遍历序列构造二叉树2.3 二叉树的前序遍历&#xff08;非递归&#xff09;为什么要…

计算机组成原理复习:数据的表示和运算

计算机组成原理复习&#xff1a;数据的表示和运算2. 数据的表示和运算2.1 数制与编码2.1.1 数制&#xff1a;进位计数制及其相互转换2.1.2 编码&#xff1a;数值数据的编码与表示2.1.2.1 逻辑型数据2.1.2.2 字符型数据 之 ASCII码2.1.2.3 数值型数据 之 BCD码2.1.3 校验码——奇…

Part类 -- 上传文件

Part类 -- 上传文件一、核心方法1.1 HttpServletRequest 类方法1.2 Part 类方法二、代码示例前端搭配 form 表单&#xff1a;form input type “file”&#xff0c;允许通过浏览器选中一个文件上传给服务器。 Servlet 就支持处理这种上传文件的请求&#xff0c;把这个请求到的文…

安科瑞智能操控无线测温装置在江苏某化工产业园项目的应用

安科瑞 李亚俊 1 概述 江苏富强新材料有限公司是中国企业500强——山东金岭集团在江苏淮安盐化新材料产业园区投资设立的盐化工企业。公司将利用淮安丰富的盐矿资源和优越的发展环境&#xff0c;投资200亿元&#xff0c;建设120万吨/年离子膜烧碱项目、70万吨/年甲烷氯化物项…

vue新春游戏-拼手速抢车票小游戏,学习玩乐两不误,春节小游戏,新年小游戏

ue新春游戏-拼手速抢车票&#xff0c;老规矩&#xff0c;体验地址&#xff1a;http://game.pkec.net/word-ticket/。 写这个主要是前几天群里运营老师说咋没人写抢车票的&#xff0c;再加上我上一篇文章上了掘金一周&#xff0c;听说多上几次有证书&#xff0c;我还没搞到过掘金…

Go语言 函数传递:值传递 和 虚假的 “引用传递”

前言 其实从变量本身来说&#xff0c;go只有值传递&#xff0c;函数内的修改不会影响函数外。但有一种特例是指针&#xff0c;go可以传指针给函数&#xff0c;指针指向申请出来的实际内存&#xff0c;也就是保存元素的内存&#xff0c; 这样在函数内的修改&#xff0c;可以影响…

就算是TOP程序员,也有这些坏习惯

绝大多数程序员在职业生涯中&#xff0c;多多少少都会养成一些坏习惯&#xff0c;今天就来说一说身边最常见的一些坏习惯&#xff0c;也给刚入行的新朋友们提个醒&#xff0c;少走一些弯路。 那么&#xff0c;就让我们开始吧&#xff01; 1.不注意适当休息 比如日常工作时、…

B端产品-登录功能设计

在项目启动初期&#xff0c;基本大家都是先从账号体系先开始着手设计的&#xff0c;那么B端的登录功能如何设计呢&#xff1f; 一、需求分析 B端的产品的用户基本上可以分为内部员工和客户&#xff0c;如果是客户使用&#xff0c;基本上都会有注册功能&#xff1b;如果是内部员…

C# Console.Read读取回车和换行

C#的Console.Read函数&#xff1a; 读取缓冲区中的数据&#xff0c;读取到一个字符时停止。 C#的Console.ReadLine函数 读取缓冲区中的数据&#xff0c;遇到回车时停止。 Enter键&#xff1a; 将行数据输入缓冲区&#xff0c;并且将回车符和换行符 加入缓冲区。 注意 Consol…

【H5UI库和二维码】一.H5UI库;二.加密技术;三.二维码

目录​​​​​​​ 一.H5UI库 1.使用方法&#xff1a; &#xff08;1&#xff09;页面中引入css文件 &#xff08;2&#xff09;页面中引入js文件 2.组件的用法 &#xff08;2&#xff09;按钮的使用&#xff1a;button。有三种类型&#xff08;primary、danger、defaul…

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《3》

前面了解到Faster R-CNN在实验中的效果很不错&#xff0c;以及对论文做了一个大概的了解&#xff0c;对此有兴趣的伙伴们也可以先浏览前面两篇文章&#xff1a;MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《1》MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《…

【Node】Node.js安装与配置(详细步骤)

Node.js安装与配置&#xff08;详细步骤&#xff09;一、安装Node.js1.1 下载1.2 安装1.3 环境变量二、验证是否安装成功三、修改模块下载位置3.1 查看npm默认存放位置3.2 在 nodejs 安装目录下&#xff0c;创建 “node_global” 和 “node_cache” 两个文件夹3.3 修改默认文件…

xd卡数据丢失原因和三种数据恢复方法介绍

xd卡适用于富士/奥林巴斯相机品牌&#xff0c;它能配合各式读卡器&#xff0c;方便的与个人电脑连接传输图像视频等。但是xd卡与其他类型的存储卡一样容易因各种原因出现数据丢失。如果您的xd卡出现了数据丢失的问题&#xff0c;不妨了解下这里给大家分析的xd卡数据丢失原因和具…

解决仓库产品管理痛点,选对条码工具至关重要

" 最近我们仓库管理遇到了很多问题&#xff0c;一方面 我们进出库的产品数量非常庞大&#xff0c;目前公司的条码扫描系统识别效率非常低&#xff0c;只能单个产品进行扫描&#xff0c;经常需要加班加点出入库&#xff0c;而且有些产品条码比较特殊&#xff0c;现在的扫描…