拓扑排序[讲课留档]

news2024/12/24 2:13:04

拓扑排序

拓扑排序要解决的问题是给一个有向无环图的所有节点排序

即在 A O E AOE AOE网中找关键路径

前置芝士!
  • 有向图:有向图中的每一个边都是有向边,即其中的每一个元素都是有序二元组。在一条有向边 ( u , v ) (u,v) (u,v)中,称 u u u v v v直接前驱 v v v u u u直接后继
  • 有向无环图 ( D i r e c t e d   A c y c l i c   G r a p h ) (Directed\ Acyclic\ Graph) (Directed Acyclic Graph):没有有向环,但不保证原图变成无向图时无环。


  • D A G DAG DAG的性质:能拓扑的图一定是 D A G DAG DAG D A G DAG DAG一定能拓扑。( A O E 和 A O V AOE和AOV AOEAOV网都是 D A G DAG DAG)
  • 度:顶点 v v v的度数是与 v v v关联的边数。有向图中没有度的概念。
  • 入度、出度:入度是指以该顶点为终点的有向边数量;顶点的出度是指以顶点为起点的有向边数量。无向图中没有出入度的概念。
举个例子!

我们可以拿大学排课的例子来描述这个过程,比如学习大学课程中有:程序设计,算法语言,高等数学,离散数学,编译技术,数据结构,数据库系统等。当我们想要学习数据结构的时候,就必须先学会离散数学编译技术算法语言

这些课程就相当于几个顶点 u u u, 顶点之间的有向边 ( u , v ) (u,v) (u,v)就相当于学习课程的顺序。教务处安排这些课程,使得在逻辑关系符合的情况下排出课表,就是拓扑排序的过程。

但是如果某一天排课的老师打瞌睡了,说想要学习数据结构,还得先学操作系统,而操作系统的前置课程又是数据结构,那么到底应该先学哪一个(不考虑同时学习的情况)?在这里数据结构操作系统间就出现了一个环,显然你现在没办法弄清楚你需要先学什么了,于是你也没办法进行拓扑排序了。

拓扑排序即,在一个 D A G DAG DAG(有向无环图)中,我们将图中的顶点以线性方式进行排序,使得对于任何的顶点u到v的有向边 ( u , v ) (u,v) (u,v), 都可以有 u u u v v v的前面。

严谨来说,给定一个 D A G DAG DAG,如果从 i i i j j j有边,则认为 j j j依赖于 i i i。如果 i i i j j j有路径( i i i可达 j j j ),则称 j j j间接依赖 i i i。拓扑排序的目标是将所有节点排序,使得排在前面的节点不能依赖于排在后面的节点

理论存在!
  1. 从图中选择一个入度为零的点。
  2. 记录该顶点,从图中删除此顶点及其所有的出边。

重复上面两步,直到所有顶点都输出,拓扑排序完成,此时我们可以得到一个点的出队顺序(遍历顺序)这个序列叫拓扑序;或者图中不存在入度为零的点,此时说明图是有环图,拓扑排序无法完成。

拓扑排序需要遍历整张图,假设该图为 G ( V , E ) G(V,E) G(V,E),获得拓扑序的时间复杂度大约是O(V+E)

实践开始!
void topu_sort() {
   	queue<int>q;
	for (int i = 1; i <= n; ++i) {
		if (!d[i])q.push(i);
	}
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		ans.push_back(now);
		int len = vec[now].size();
		for (int i = 0; i < len; ++i) {
			int to = vec[now][i];
			d[to]--;
			if (!d[to]) q.push(to);
		}
	}
   /*
   for(auto x:vec[now]){
   	d[x]--;
   	if(!d[x]) q.push(x);
   }
   */
}
例题

拓扑模板:B3644 【模板】拓扑排序 / 家谱树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 150;
int n;
vector<int> e[N];
int d[N];
vector<int> ans;

void topu() {
    queue<int> q;
    for (int i = 1; i <= n; ++i) {
        if (d[i] == 0) q.push(i);
    }
    while (!q.empty()) {
        int now = q.front();q.pop();
        ans.push_back(now);
        for (auto x: e[now]) {
            d[x]--;
            if (d[x] == 0) q.push(x);
        }
    }
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        int x;
        while (cin >> x) {
            if (x == 0) break;
            d[x]++;
            e[i].push_back(x);
        }
    }
    topu();
    for (auto x: ans) {
        cout << x << " ";
    }
    cout << '\n';
    return 0;
}
提高
拓扑构造:Problem - E - Codeforces
解析:

先忽略所有输入的无向边(但是不忽略点),对当前的有向图拓扑排序,如果给定的有向图中已经不是 D A G DAG DAG显然是一定不成立也无法构造的;如果当前的图是 D A G DAG DAG,那么拓扑完我们可以得到一个或多个拓扑序,每个拓扑序都是线性的,这也代表当选定一个拓扑序,在所有点中任选两个点,他们一定有已知确定先后顺序,我们按照这样的顺序去构造,建边(每次令拓扑序靠前的指向靠后的)即可保证不会出现拓扑序靠前的点依赖于靠后的点,则不会出现有向环。

AC代码:
pii ans[N];
vector<int>e[N];
int d[N];
int n,m,order=0;
int ord[N];

bool topu(){
   queue<int>q;
   for(int i=1;i<=n;++i){
       if(!d[i]){
           q.push(i);
       }
   }
   while(!q.empty()){
       int t=q.front();q.pop();
       ord[t]=++order;
       for(auto x:e[t]){
           d[x]--;
           if(!d[x])q.push(x);
       }
   }
   if(order!=n)return 0;
   return 1;
}

void work() {
   cin>>n>>m;
   int tot=0;
   order=0;
   for(int i=1;i<=n;++i){
       d[i]=0;e[i].clear();
       ans[i].fi=ans[i].se=0;
   }
   for(int i=1;i<=m;++i){
       int z,u,v;cin>>z>>u>>v;
       if(z){
           e[u].pb(v);
           d[v]++;
       }else{
           ans[++tot].fi=u;
           ans[tot].se=v;
       }
   }
   if(!topu()){
       cout<<"No\n";return;
   }
   cout<<"Yes\n";
   for(int i=1;i<=tot;++i){
       if(ord[ans[i].fi]>ord[ans[i].se])swap(ans[i].se,ans[i].fi);
       cout<<ans[i].fi<<" "<<ans[i].se<<'\n';
   }
   for(int i=1;i<=n;++i){
       if(e[i].size()){
           for(auto x:e[i]){
               cout<<i<<" "<<x<<'\n';
           }
       }
   }
}

课后练习:P1347 排序 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

总结:

拓扑排序是图论中非常基础的算法,大多时候作为工具,或者一些进阶算法的预处理内容,非常简单,同时需要熟练掌握。

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

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

相关文章

数字营销执行团队的重要性及如何配置

在当今数字化商业环境中&#xff0c;数字营销执行团队的重要性不言而喻。 其重要性主要体现在以下几点&#xff1a;一是能精准触达目标客户&#xff0c;借助数据分析实现精准营销&#xff0c;提高营销效率和效果。二是紧跟数字技术发展潮流&#xff0c;及时采用新的营销手段和…

基于OpenCV与Keras的停车场车位自动识别系统

本项目旨在利用计算机视觉技术和深度学习算法&#xff0c;实现对停车场车位状态的实时自动识别。通过摄像头监控停车场内部&#xff0c;系统能够高效准确地辨认车位是否被占用&#xff0c;为车主提供实时的空闲车位信息&#xff0c;同时为停车场管理者提供智能化的车位管理工具…

【JVM-04】线上CPU100%

【JVM-04】线上CPU100% 1. 如何排查 1. 如何排查 ⼀般CPU100%疯狂GC&#xff0c;都是死循环的锅&#xff0c;那怎么排查呢&#xff1f;先进服务器&#xff0c;⽤top -c 命令找出当前进程的运⾏列表按⼀下 P 可以按照CPU使⽤率进⾏排序显示Java进程 PID 为 2609 的java进程消耗…

清新简约卡片风格蓝紫渐变色短视频苹果CMS模板

首途第三十三套清新简约卡片风格蓝紫渐变色短视频模板&#xff0c;一套苹果CMSV10主题。 这套主题是简约风格&#xff0c;以纯洁的白色和深邃的紫色为主色调&#xff0c;为您提供了一种清新、时尚的浏览体验。 在这个简洁而美丽的界面中&#xff0c;您可以轻松畅享各种精彩短…

【微服务】微服务之Feign 与 Ribbon

文章目录 强烈推荐引言优点Feign示例什么是Ribbon&#xff1f;Ribbon 的优点Netflix Feign 和 Ribbon整合Feign 与 Ribbon 的关系Feign 与 Ribbon 结合使用的示例配置文件&#xff08;application.yml&#xff09;说明&#xff1a; Feign 与 Ribbon 结合使用的应用场景1. 动态服…

Win10系统登录界面两个相同用户名的处理方法

在Windows 10系统中只设置了一个帐户&#xff0c;即本地帐户Administrator&#xff0c;但由于一些应用对帐户的要求&#xff0c;会切换到微软帐户登录&#xff0c;而切换之后&#xff0c;在登录界面就会显示微软帐户的名称&#xff0c;故Windows 10系统在登录界面会显示两个相同…

windows网络进阶之listen参数含义

目录 一、前言 二、listen参数 三、实战案例 1. 使用Sleep函数让线程暂停 2. 案例代码展示 3. 编译生成exe文件 一、前言 一、前言 在Windows网络编程中&#xff0c;在使用TCP时&#xff0c;listen函数它是一个重要的关键的步骤&#xff0c;使得套接字能够接收传…

【C++】使用C++在线程中动态记录数据到外部文件

在现代软件开发中&#xff0c;多线程编程已成为处理并发任务、提高程序性能的重要手段。而在多线程环境下&#xff0c;如何有效地管理和记录数据&#xff0c;尤其是将动态生成的数据安全地写入外部文件&#xff0c;是许多应用程序必须面对的问题。本文将深入探讨如何在C中使用多…

FastApi中的常见请求类型

FastApi中的常见请求类型 后端开发语言中&#xff0c;我钟情于node&#xff0c;高效的异步处理真是让我眼前一亮&#xff0c;同时&#xff0c;简单易懂的语法也让我非常倾心 但是但是&#xff0c;因为考虑要写一个深度学习算法的后端接口&#xff0c;所以不得不选用python作为…

传神论文中心|第15期人工智能领域论文推荐

在人工智能领域的快速发展中&#xff0c;我们不断看到令人振奋的技术进步和创新。近期&#xff0c;开放传神&#xff08;OpenCSG&#xff09;社区发现了一些值得关注的成就。传神社区本周也为对AI和大模型感兴趣的读者们提供了一些值得一读的研究工作的简要概述以及它们各自的论…

【linux】虚拟机安装 BCLinux-R8-U4-Server-x86_64

目录 一、概述 1.1移动云Linux系统订阅服务 CLS 1.2 大云天元操作系统BC-Linux 二、安装 一、概述 1.1移动云Linux系统订阅服务 CLS 移动云Linux系统订阅服务 CLS &#xff08;Cloud Linux Service&#xff09;为使用BC-Linux操作系统的用户提供标准维保服务以及高级技术支…

JVM原理(九):JVM虚拟机工具之可视化故障处理工具

1. JHSDB:基于服务性代理的调试工具 JHSDB是一款基于服务性代理实现的进程外调试工具。 服务性代理是HotSpot虚拟机中一组用于映射Java虚拟机运行信息的、主要基于Java语言实现的API集合。 2. JConsole:Java监视与管理控制台 JConsole是一款基于JMX的可视化监视、管理工具。…

矩阵、混剪、大盘,3大功能升级优化!助力企业高效管理!

在数字化转型的浪潮中&#xff0c;企业对于工具与技术的需求愈发强烈。 为满足市场需求&#xff0c;本月【云略】为各企业上线了便捷功能&#xff0c;赋能企业经营决策和业务增长。 矩阵管理 √【矩阵号管理】抖音支持设置城市IP 内容管理 √【混剪任务】支持关联智能发布计…

模拟 ADC 的前端

ADC 的 SPICE 模拟 反复试验的方法将信号发送到 ADC 非常耗时&#xff0c;而且可能有效也可能无效。如果转换器捕获电压信息的关键时刻模拟输入引脚不稳定&#xff0c;则无法获得正确的输出数据。SPICE 模型允许您执行的步是验证所有模拟输入是否稳定&#xff0c;以便没有错误…

百家讲坛 | 裴伟伟:企业中安全团队应当如何反馈漏洞

作者简介&#xff1a;裴伟伟&#xff0c;洞源实验室创始人&#xff0c;国家网安基地网络安全行业专家&#xff0c;网安加社区特聘专家&#xff0c;持有CISSP、PMP证书&#xff0c;曾在HITCON、可信云大会、开源产业大会等安全论坛发表演讲。曾任国内某安全实验室负责人、某互金…

3.js - 色调映射(renderer.toneMapping)

// ts-nocheck// 引入three.js import * as THREE from three// 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls// 导入lil.gui import { GUI } from three/examples/jsm/libs/lil-gui.module.min.js// 导入tween import * as TWEEN…

Stable DIffusion 线稿上色+风格迁移教程,建议收藏!

前言 Stable Diffusion 线稿上色与风格迁移教程。 欢迎来到Stable Diffusion的线稿上色与风格迁移教程&#xff01;在这个教程中&#xff0c;我们将引导你如何使用Stable Diffusion技术&#xff0c;将你的线稿作品进行上色&#xff0c;并迁移到不同的艺术风格。让我们开始吧&a…

双端队列广搜——AcWing 175. 电路维修

双端队列广搜 定义 双端队列广搜&#xff08;Breadth-First Search with a Deque&#xff09;是一种图或树的遍历算法变体&#xff0c;它利用了双端队列&#xff08;Deque&#xff0c;全称Double Ended Queue&#xff0c;允许在其两端进行插入和删除操作&#xff09;作为数据…

CentOS7源码安装nginx并编写服务脚本

华子目录 准备下载nginx源码包关闭防火墙关闭selinux安装依赖环境 解压编译安装测试编写服务脚本&#xff0c;通过systemctl实现服务启动与关闭测试 准备 下载nginx源码包 在源码安装前&#xff0c;我们得先下载nginx源码包https://nginx.org/download/这里我下载的是nginx-1…

PHP景区旅游多商户版微信小程序系统源码

解锁景区新玩法&#xff01;​ 引言&#xff1a;一站式旅行新体验 厌倦了传统景区的单调游览&#xff1f;想要一次旅行就能体验多种风情&#xff1f;那么&#xff0c;“景区旅游多商户版”绝对是你的不二之选&#xff01;这个创新模式将景区内多个商户资源整合&#xff0c;为…