【拓扑系列】拓扑排序

news2024/9/21 16:42:55

【拓扑系列】拓扑排序

  • 前言
    • 认识有向无环图
    • 认识AOV网:顶点活动图
    • 拓扑排序
  • 1. 课程表
    • 1.1 题目来源
    • 1.2 题目描述
    • 1.3 题目解析
  • 2. 课程表 II
    • 2.1 题目来源
    • 2.2 题目描述
    • 2.3 题目解析
  • 3. LCR 114. 火星词典
    • 3.1 题目来源
    • 3.2 题目描述
    • 3.3 题目解析

前言

认识有向无环图

图中不会形成环状的有向图就是有向无环图。

在这里插入图片描述

入度和出度

  • 出度:一个顶点指向另一个节点的数量。
  • 入度:一个顶点有多少是指向它的。

在这里插入图片描述

认识AOV网:顶点活动图

  • AOV网(Activity On Vertex Network)‌是一个用顶点表示活动,有向边表示活动之间优先关系的有向图。在这个图中,顶点代表工程中的各个活动,而有向边则反映了这些活动之间的先后依赖关系。

AOV网的特点包括:

  1. 顶点表示活动‌:在AOV网中,每一个顶点都代表一个具体的活动,这些活动构成了工程的各个组成部分。
    ‌有向边表示优先关系‌:有向边用来表示活动之间的优先关系,即一个活动必须在另一个活动完成之后才能开始。这种关系通过有向边在图中进行表示,从而形成一个有向图。
  2. 无回路‌:AOV网中不允许存在回路,即不存在一个活动直接或间接地依赖于它自身的完成,这保证了活动的逻辑顺序和工程的可行性。
  3. AOV网的概念在现代化管理中非常有用,特别是在描述和分析一项工程的计划和实施过程中。通过将工程分解为多个小的子工程(即活动),并用有向图来表示这些活动之间的依赖关系,可以帮助理解和优化工程的执行流程。此外,拓扑排序是AOV网的一个重要应用,用于判断网中是否存在环,确保所有活动都能按照正确的顺序执行,避免出现无法完成的循环依赖‌。

拓扑排序

  • 拓扑排序‌是对一个有向无环图(DAG)进行排序,将图中的所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。这样的线性序列称为满足拓扑次序的序列,简称拓扑序列。‌12

  • 拓扑排序常用于确定一个依赖关系集中事物发生的顺序。例如,在项目管理中,可以确定项目各阶段之间的依赖关系,从而计算出项目的执行顺序。

  • 拓扑排序的实现方法通常使用深度优先搜索(DFS)或广度优先搜索(BFS)。在树结构中,拓扑排序可以简单地通过层次遍历实现。

我们用更直接的话来讲就是:拓扑排序的作用就是找到做事情的先后顺序,并且拓扑排序的结果不唯一。

拓扑排序的具体操作:

  1. 找出图中入度为0的节点,然后输出
  2. 删除这个节点的连接的边
  3. 重复步骤1,2操作,直到图中没有点或者没有入度为0的节点为止(可能存在环)。所以可以根据这个特性判断图中是否有环。

实现拓扑排序:借助队列,来一次bfs即可
4. 初始化:将所有入度为0的节点添加的队列中
5. 当队列不为空的时候,拿出队头元素添加到结果中,删除该元素相连的边,判断删完后与删除边相连接的点入度是否为0,如果为0添加到队列,然后一直循环。

1. 课程表

1.1 题目来源

207. 课程表

1.2 题目描述

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false

  1. 示例 1:
    输入:numCourses = 2, prerequisites = [[1,0]]
    输出:true
    解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
  2. 示例 2:
    输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
    输出:false
    解释:总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。

提示:
1 <= numCourses <= 2000
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i] 中的所有课程对 互不相同

1.3 题目解析

class Solution {
public:
    vector<vector<int>> edges;
    vector<int> in;
    queue<int> q;
    vector<int> ret;

    bool bfs(int n)
    {
        // 将入度数为0的放入队列
        for (int i = 0; i < n; i++)
        {
            if (in[i] == 0)
            {
                q.push(i);
            }
        }
        while (!q.empty())
        {
            int t = q.front();
            q.pop();
            ret.push_back(t);
            for (auto v : edges[t])
            {
                if(--in[v] == 0)
                {
                    q.push(v);
                }
            }
        }
        return ret.size() == n; // 判断是否有环
    }
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) 
    {
        in.resize(numCourses);
        edges.resize(numCourses);
        // 建图
        for (auto point : prerequisites)
        {
            edges[point[1]].push_back(point[0]);
            //计入计入入度数
            in[point[0]]++;
        }
        return bfs(numCourses);
    }
};

2. 课程表 II

2.1 题目来源

210. 课程表 II

2.2 题目描述

现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。

例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。

  1. 示例 1:
    输入:numCourses = 2, prerequisites = [[1,0]]
    输出:[0,1]
    解释:总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。
  2. 示例 2:
    输入:numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
    输出:[0,2,1,3]
    解释:总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
    因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3]
  3. 示例 3:
    输入:numCourses = 1, prerequisites = []
    输出:[0]

提示:
1 <= numCourses <= 2000
0 <= prerequisites.length <= numCourses * (numCourses - 1)
prerequisites[i].length == 2
0 <= ai, bi < numCourses
ai != bi
所有[ai, bi] 互不相同

2.3 题目解析

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) 
    {
        vector<vector<int>> edges(numCourses);
        vector<int> in(numCourses);
        vector<int> ret;
        queue<int> q;

        // 建图
        for (auto v : prerequisites)
        {
            edges[v[1]].push_back(v[0]);
            in[v[0]]++;
        }

        //将入度数为0的放入队列
        for (int i = 0; i < numCourses; i++)
        {
            if (in[i] == 0) q.push(i);
        }

        //深度遍历
        while (!q.empty())
        {
            int t = q.front();
            q.pop();
            ret.push_back(t);
            for (auto v : edges[t])
            {
                if (--in[v] == 0)
                    q.push(v);
            }
        }
        if (ret.size() == numCourses)
            return ret;
        else return vector<int>();
    }
};

3. LCR 114. 火星词典

3.1 题目来源

LCR 114. 火星词典

3.2 题目描述

现有一种使用英语字母的外星文语言,这门语言的字母顺序与英语顺序不同。

给定一个字符串列表 words ,作为这门语言的词典,words 中的字符串已经 按这门新语言的字母顺序进行了排序 。

请你根据该词典还原出此语言中已知的字母顺序,并 按字母递增顺序 排列。若不存在合法字母顺序,返回 “” 。若存在多种可能的合法字母顺序,返回其中 任意一种 顺序即可。

字符串 s 字典顺序小于 字符串 t 有两种情况:

在第一个不同字母处,如果 s 中的字母在这门外星语言的字母顺序中位于 t 中字母之前,那么 s 的字典顺序小于 t 。
如果前面 min(s.length, t.length) 字母都相同,那么 s.length < t.length 时,s 的字典顺序也小于 t 。

  1. 示例 1:
    输入:words = [“wrt”,“wrf”,“er”,“ett”,“rftt”]
    输出:“wertf”
  2. 示例 2:
    输入:words = [“z”,“x”]
    输出:“zx”
  3. 示例 3:
    输入:words = [“z”,“x”,“z”]
    输出:“”
    解释:不存在合法字母顺序,因此返回 “” 。
  4. 示例 4:
    输入:words = [“abc”,“ab”]
    输出:“”
    解释:不存在合法字母顺序,因此返回 “” 。

提示:
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i] 仅由小写英文字母组成

3.3 题目解析

这个题目的关键就在于要理解题目意思,题目意思是,两个字符进行对比,一旦遇到了不相同的字符,字符在前面的是小于字符在后面的,并且如果s1的长度小于s2的话并且s1的长度大于s2的长,如果s2等于s1的部分字符串,就类似于示例4,那么也是不符合的。
在这里插入图片描述
所以我们该怎么处理words字典呢,这里我们就可以直接使用两层for循环,依次进行比较,并且一旦找到了的话其实可以用图的方式进行来连接例如(a->b 就标识a小于b)于是有了这个思路我们就可以使用拓扑排序了。

上面的几题都是对int整形进行比较的,也是使用的vector来存放图,但是这里就不一样了,类型是char,所以这里我们一应该使用hash表来进行存储,即unordered_map<char, unordered_set< char >>,因为字典中会后重复的,所以我们的邻接表也应该使用一个hash来进行存储。
而至于我们存放入度数的话也不能直接使用vector或者数组来存储了,也应该使用hash来进行存储。

所以我们的具体步骤就是:

  1. 创建unordered_map<char, unordered_set< char>> edges来建图,创建unordered_map<char, int> in来存放入度数
  2. 遍历整个字典将初始化入度in
  3. 使用双指针遍历字典,建立图
  4. 深度遍历
class Solution {
public:
    unordered_map<char, unordered_set<char>> edges;
    unordered_map<char, int> in;
    queue<char> q;
    string ret;
    string alienOrder(vector<string>& words) 
    {
        for (auto &s : words)
        {
            for (auto c : s)
            {
                in[c] = 0;
            }
        }
        
        // 建图
        for (int i = 0; i < words.size(); i++)
        {
            for (int j = i + 1; j < words.size(); j++)
            {
                if(add(words[i], words[j]))
                    return "";
            }
        }

        // 将度为0的放入队列
        for (auto &[a, b] : in)
        {
            if (b == 0)
                q.push(a);
        }
		// 进行深度遍历
        while (!q.empty())
        {
            char t = q.front();
            q.pop();
            ret += t;
            for (auto v : edges[t])
            {
                if (--in[v] == 0)
                    q.push(v);
            }
        }
        std::cout << ret;
        if (ret.size() == in.size()) return ret;
        else return "";
    }

    bool add (string& s1, string& s2)
    {
        int n = min(s1.size(), s2.size());
        int i = 0;
        for (; i < n; i++)
        {
            if (s1[i] != s2[i])
            {
                char a = s1[i], b = s2[i];
                if (!edges.count(a) | !edges[a].count(b))
                {
                    edges[a].insert(b);
                    in[b]++;
                }
                break;
            }
        }
        if (i == s2.size() && i < s1.size()) return true; // 处理特殊情况,示例4
        else return false;
    }
};

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

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

相关文章

AI基础 L13 Constraint Satisfaction Problems I约束满足问题

Defining Constraint Satisfaction Problems A constraint satisfaction problem (CSP) consists of three components, X, D, and C: • X is a set of variables, {X1, . . . , Xn}. • D is a set of domains, {D1, . . . , Dn}, one for each variable • C is a set of co…

STM32+ESP8266 WiFi连接机智云平台APP远程控制教程

本文档将介绍如何用STM32ESP8266 WiFi模块从零开始连接上机智云&#xff0c;并通过APP进行远程控制。 机智云官网&#xff1a;机智云|智能物联网操作系统 (gizwits.com) 准备&#xff1a;STM32、ESP8266、手机、可上网的WiFi。 1.创建设备 1.1 注册登陆 请自行注册账号并登陆…

脱离八股文,真实开发中的延时处理需求实现思路(超时订单处理为例)

前言 咱们聊聊那些在开发过程中经常遇到的延时处理需求吧。比如说&#xff0c;网购时那些迟迟不付款的订单&#xff0c;或者是社交软件里那些需要稍后处理的消息&#xff0c;再或者是金融交易中那些需要等待确认的交易。这些都是咱们得搞定的活儿。 不过&#xff0c;很多时候&a…

JS面试真题 part3

JS面试真题 part3 11、bind、call、apply区别&#xff1f;如何实现一个bind12、JavaScript中执行上下文和执行栈是什么13、说说JavaScript中的事件模型14、解释下什么是事件代理&#xff1f;应用场景&#xff1f;15、说说你对闭包的理解&#xff1f;闭包使用场景 11、bind、cal…

make 程序规定的 makefile 文件的书写语法

&#xff08;1&#xff09; 常用的 gcc 选项&#xff1a; &#xff08;2&#xff09; make 的作用&#xff1a; &#xff08;3&#xff09; 搭建 make 的实验环境 &#xff0c; linux 的很简单&#xff0c; windows 的复杂一点&#xff1a; windows 上 make 环境的搭建&#…

python爬虫基础:了解html

编辑器vscode <!DOCTYPE html> <html><head><title>第一个网页</title></head><body><h1>字体</h1><h2>字体</h2><h3>字体</h3><p>Lorem, ipsum dolor sit amet consectetur adipisicing…

电商平台如何实现自动监控订单签收状态,加快资金划拨进程?

资金划拨作为交易流程的核心环节之一&#xff0c;直接关系到商家资金回笼的速度、消费者购物体验的满意度以及平台自身的信誉与稳定性。 区别于自营电商&#xff0c;电商平台入驻了许多第三方商家&#xff0c;为了保障交易安全和控制风险&#xff0c;在交易未完成之前&#xff…

Java 入门指南:Java 并发编程 —— 同步工具类 Semephore(信号量)

文章目录 同步工具类Semephore核心功能限制并发访问量公平与非公平策略灵活性与适应性 常用方法使用示例 同步工具类 JUC&#xff08;Java.util.concurrent&#xff09;是 Java 提供的用于并发编程的工具类库&#xff0c;其中包含了一些通信工具类&#xff0c;用于在多个线程之…

Spring和MyBatis常见面试题总结

文章目录 1 Spring 基础1.1 说一下你对 Spring 的理解&#x1f525;1.2 Spring,Spring MVC,Spring Boot 之间什么关系?1.3 Spring 框架中用到了哪些设计模式&#xff1f;&#x1f525;1.4 说说自己对于 Spring MVC 了解?1.5 Spring MVC 的核心组件有哪些&#xff1f;1.6 Spri…

flutter开发实战-GoRouter路由go与push区别实践

flutter开发实战-GoRouter路由go与push区别实践 GoRouter是一个flutter的声明性路由包&#xff0c;使用路由器API提供一个方便的、基于url的API&#xff0c;用于在不同屏幕之间导航。可以定义URL模式、使用URL导航、处理深度链接以及许多其他与导航相关的场景。 之前使用了Go…

Vue3封装table表格右键菜单功能

1) 效果&#xff0c;右键单击单元格&#xff0c;打开菜单弹窗: 点击菜单选项&#xff0c;可选择只读/编辑&#xff0c;可在只读/编辑方法中&#xff0c;拿到该行列表格的数据&#xff0c;进行相关操作 2) 思路 1、右键菜单组件 出现的时机&#xff0c;是右键单击table表格row-…

移动安全需求分析与安全保护工程

移动应用安全威胁与需求分析 移动应用系统组成&#xff1a; 移动应用&#xff1a;简称App 通信网络&#xff1a;无线网络&#xff0c;移动通信网络及互联网 应用服务端&#xff1a;由相关服务器构成&#xff0c;负责处理来自App相关信息或数据 移动应用安全分析 Android系统…

【0基础】制作HTML网页小游戏——贪吃蛇(附详细解析)

我在昨天的文章&#xff08;贪吃蛇HTML源码&#xff09;里面分享了网页版贪吃蛇小游戏的源码&#xff0c;今天就来给大家详细讲解一下每部分代码是如何运作的&#xff0c;以及以后要如何美化贪吃蛇的UI界面&#xff0c;在哪里修改等。 目录 一、代码运作 1、HTML结构: 2、C…

数组的常用算法

数组是同类型数据的集合。便于整体处理数据&#xff0c;数组操作的主要算法有&#xff1a; 1求极值 2查找 3排序 2查找 cprimer plus第11.1节278--279页 4数组和指针的区别&#xff1a;数组表示法和指针表示法 数组表示法1 int a[4]{2,4,1,5}; for(int i0;i<4;i)cou…

JAVA 的excel数据批量导入解析 现在都用什么API工具 Apache POI 、EasyExcel 、easypoi有什么区别

&#x1f4dd;个人主页&#x1f339;&#xff1a;个人主页 ⏩收录专栏⏪&#xff1a;SpringBoot &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339;&#xff0c;让我们共同进步&#xff01; 在Java中&#xff0c;处理Excel数据批量导入解析时&#xff0c;常…

通讯录

在写一个程序前需要了解的知识&#xff0c;需要对通讯录的流程了如指掌&#xff0c;才能写出一个完整的程序 。 写一个主函数&#xff0c;里面包含菜单、对菜单的选择、以及循环。创建个人信息结构体&#xff0c;多人构成的结构体数组。分析每一个函数&#xff1a; 1).增加信…

webstorm修改主题色和配色常用插件(全部实用)包含主题、界面、开发效率等

Windows 用户打开setting 选择配色 更换主题看这里 效率插件 Rainbow Brackets 推荐理由&#xff1a;用各种鲜明的颜色显示括号&#xff0c;这样可以很容易分清楚括号配对问题。 Key promoter 推荐理由&#xff1a;只要是鼠标操作能够用快捷键替代的&#xff0c;Key Promoter…

若依漏洞综合利用工具

若依漏洞综合利用工具 安装与使用 该工具使用java开发&#xff0c;环境要求&#xff1a;JDK1.8版本 java -jar “文件名” 即可打开图形化界面。 注意查看"必看操作说明"模块。 1.首先下载好几个必要模块。 然后把openjfx-17.0.11_windows-x64_bin-sdk放在D盘根…

音视频入门基础:AAC专题(1)——AAC官方文档下载

一、AAC简介 高级音频编码&#xff08;英语&#xff1a;Advanced Audio Coding&#xff0c;AAC&#xff09;是有损音频压缩的专利数字音频编码标准&#xff0c;由Fraunhofer IIS、杜比实验室、贝尔实验室、Sony、Nokia等公司共同开发。出现于1997年&#xff0c;为一种基于MPEG…

【python因果推断库14】饮酒年龄 - 贝叶斯分析

目录 饮酒年龄 - 贝叶斯分析 主效应模型 交互模型 将连续变量以治疗阈值为中心 饮酒年龄 - 贝叶斯分析 这个例子使用了回归断点设计来探讨最低合法饮酒年龄&#xff08;在美国为21岁&#xff09;对全因死亡率的因果效应。数据集来自carpenter2009effect 的一项研究。 impo…