c++ 拓扑排序

news2024/11/29 16:57:02

概念

拓扑排序是一种线性排序算法,主要用于有向无环图 (DAG, Directed Acyclic Graph) 中,对顶点进行排序,使得对于每一条边 u→v,顶点 u 都排在顶点 v之前。

特点

  • 适用于有向无环图。

  • 拓扑排序的结果不唯一(如果有多种线性排序方式满足条件)。

  • 常用于任务调度、依赖关系解析、课程安排等场景。

算法

基于 Kahn 算法 (入度法)

  • 核心思想:依次找出入度为 0 的节点,移除它们以及相关边,直到图中没有节点。
  • 步骤:
    1. 统计所有节点的入度。
    2. 将入度为 0 的节点入队。
    3. 每次从队列中取出一个节点,加入结果序列,并将它指向的节点入度减 1。
    4. 如果入度变为 0,则加入队列。
    5. 直到队列为空,若结果序列包含所有节点,则排序成功。

基于深度优先搜索 (DFS)

  • 核心思想:对每个节点进行深度优先搜索,节点访问完成后,逆序加入结果。
  • 步骤:
    1. 用一个数组记录节点是否被访问。
    2. 对每个未访问的节点进行 DFS。
    3. 每次访问完成一个节点后,将其压入栈中。
    4. 最后将栈中元素依次弹出,得到排序结果。

实例

使用 Kahn 算法(c++)

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

// 拓扑排序函数
vector<int> topologicalSortKahn(int n, vector<vector<int>>& adj) {
    vector<int> inDegree(n, 0); // 记录每个节点的入度
    for (auto& edges : adj) {
        for (int v : edges) {
            inDegree[v]++;
        }
    }
    
    queue<int> q;              // 存放入度为 0 的节点
    for (int i = 0; i < n; i++) {
        if (inDegree[i] == 0) {
            q.push(i);
        }
    }
    
    vector<int> result;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        result.push_back(u);   // 加入结果
        
        for (int v : adj[u]) { // 移除节点 u 的所有边
            inDegree[v]--;
            if (inDegree[v] == 0) {
                q.push(v);
            }
        }
    }
    
    if (result.size() != n) {
        throw runtime_error("Graph has a cycle!"); // 图中存在环
    }
    
    return result;
}

// 主函数
int main() {
    int n = 6; // 节点数
    vector<vector<int>> adj = {
        {2, 3},    // 节点 0 指向 2, 3
        {3, 4},    // 节点 1 指向 3, 4
        {},        // 节点 2 无指向
        {5},       // 节点 3 指向 5
        {5},       // 节点 4 指向 5
        {}         // 节点 5 无指向
    };
    
    try {
        vector<int> result = topologicalSortKahn(n, adj);
        cout << "拓扑排序结果: ";
        for (int v : result) {
            cout << v << " ";
        }
    } catch (const exception& e) {
        cout << e.what() << endl;
    }
    
    return 0;
}

使用 DFS 方法(c++)

#include <iostream>
#include <vector>
#include <stack>

using namespace std;

// 深度优先搜索
void dfs(int node, vector<vector<int>>& adj, vector<bool>& visited, stack<int>& stk) {
    visited[node] = true;
    for (int v : adj[node]) {
        if (!visited[v]) {
            dfs(v, adj, visited, stk);
        }
    }
    stk.push(node); // 当前节点访问完成后入栈
}

// 拓扑排序函数
vector<int> topologicalSortDFS(int n, vector<vector<int>>& adj) {
    vector<bool> visited(n, false);
    stack<int> stk;
    
    for (int i = 0; i < n; i++) {
        if (!visited[i]) {
            dfs(i, adj, visited, stk);
        }
    }
    
    vector<int> result;
    while (!stk.empty()) {
        result.push_back(stk.top());
        stk.pop();
    }
    
    return result;
}

// 主函数
int main() {
    int n = 6; // 节点数
    vector<vector<int>> adj = {
        {2, 3},    // 节点 0 指向 2, 3
        {3, 4},    // 节点 1 指向 3, 4
        {},        // 节点 2 无指向
        {5},       // 节点 3 指向 5
        {5},       // 节点 4 指向 5
        {}         // 节点 5 无指向
    };
    
    vector<int> result = topologicalSortDFS(n, adj);
    cout << "拓扑排序结果: ";
    for (int v : result) {
        cout << v << " ";
    }
    
    return 0;
}

课程表安排问题

我们有 nnn 门课程,编号为 0,1,…,n−1。有一些课程的先修要求,比如修读课程 1之前必须修读课程 0。这些要求可以表示为一个有向图,判断是否可以完成所有课程,并给出一个可行的学习顺序。

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

// 拓扑排序函数 (基于 Kahn 算法)
vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {
    vector<vector<int>> adj(numCourses); // 邻接表
    vector<int> inDegree(numCourses, 0); // 入度数组

    // 构建图和入度
    for (auto& prereq : prerequisites) {
        adj[prereq.second].push_back(prereq.first);
        inDegree[prereq.first]++;
    }

    queue<int> q; // 存放入度为 0 的节点
    for (int i = 0; i < numCourses; i++) {
        if (inDegree[i] == 0) {
            q.push(i);
        }
    }

    vector<int> result; // 存储拓扑排序结果
    while (!q.empty()) {
        int course = q.front();
        q.pop();
        result.push_back(course);

        for (int next : adj[course]) {
            inDegree[next]--;
            if (inDegree[next] == 0) {
                q.push(next);
            }
        }
    }

    // 如果结果中包含所有课程,返回排序;否则返回空
    return (result.size() == numCourses) ? result : vector<int>();
}

// 主函数
int main() {
    int numCourses = 6; // 总课程数
    vector<pair<int, int>> prerequisites = {
        {1, 0}, {2, 1}, {3, 1}, {4, 2}, {5, 3}, {5, 4}
    };

    vector<int> order = findOrder(numCourses, prerequisites);

    if (order.empty()) {
        cout << "无法完成所有课程(存在环)!" << endl;
    } else {
        cout << "课程学习顺序: ";
        for (int course : order) {
            cout << course << " ";
        }
        cout << endl;
    }

    return 0;
}

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

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

相关文章

小程序-基于java+SpringBoot+Vue的戏曲文化苑小程序设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

mybatis plus如何使用mybatis xml拼接sql

在 MyBatis Plus 中&#xff0c;如果你想使用 MyBatis 的 XML 文件来拼接 SQL&#xff0c;可以结合使用 MyBatis 和 MyBatis Plus 的功能。MyBatis Plus 是一个增强 MyBatis 的工具&#xff0c;它提供了很多便捷的操作&#xff0c;但有时你可能需要使用 XML 文件来定义更复杂的…

【uniapp】轮播图

前言 Uniapp的swiper组件是一个滑块视图容器组件&#xff0c;可以在其中放置多个轮播图或滑动卡片。它是基于微信小程序的swiper组件进行封装&#xff0c;可以在不同的平台上使用&#xff0c;如微信小程序、H5、App等。 效果图 前端代码 swiper组件 <template><vi…

Python爬虫爬取数据报错

报错&#xff1a; Error fetching the URL: (Connection aborted., ConnectionResetError(10054, 远程主机强迫关闭了一个现有的连接。, None, 10054, None)) 报错原因&#xff1a; 目标服务器限制&#xff1a; 目标网站可能已经检测到你的请求来自自动化工具&#xff08;如爬虫…

人工智能与传统控制系统的融合发展

在这个科技快速迭代的时代&#xff0c;人工智能技术正以前所未有的速度改变着我们的生活。在控制系统领域&#xff0c;AI技术的引入为传统控制带来了新的发展机遇和挑战。然而&#xff0c;这并不意味着传统控制将被完全取代&#xff0c;相反&#xff0c;AI与传统控制的深度融合…

shell综合

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

什么是串联谐振

比如有一个由电阻、电容和电感的串联电路中&#xff0c;存在一个频率能使这个电路的电流最大&#xff0c;这个现象就叫谐振。 那么这个频率是多少呢&#xff1f; 交流电频率与电路固有频率一致时&#xff0c;它就能发生谐振&#xff0c;此时这个电路的电流是最大的 这个固有频…

韦东山stm32hal库--定时器喂狗模型按键消抖原理+实操详细步骤

一.定时器按键消抖的原理: 按键消抖的原因: 当我们按下按键的后, 端口从高电平变成低电平, 理想的情况是, 按下, 只发生一次中断, 中断程序只记录一个数据. 但是我们使用的是金属弹片, 实际的情况就是如上图所示, 可能会发生多次中断,难道我们要记录3/4次数据吗? 答:按键按下…

雨云服务器搭建docker且用docker部署kali服务器教程

雨云 - 新一代云服务提供商 介绍 大家好今天教大家如何使用雨云的服务器安装docker并且用docker搭建kali服务器&#xff0c;实现大家做黑客的梦。 性价比比较高的云服务器提供参考&#xff1a;雨云 - 新一代云服务提供商 优惠码&#xff1a;MzkxODI4 什么是kali Kali L…

SQL进阶——JOIN操作详解

在数据库设计中&#xff0c;数据通常存储在多个表中。为了从这些表中获取相关的信息&#xff0c;我们需要使用JOIN操作。JOIN操作允许我们通过某种关系&#xff08;如相同的列&#xff09;将多张表的数据结合起来。它是SQL中非常重要的操作&#xff0c;广泛应用于实际开发中。本…

分析JHTDB数据库的Channel5200数据集的数据(SciServer服务器)

代码来自https://github.com/idies/pyJHTDB/blob/master/examples/channel.ipynb %matplotlib inline import numpy as np import math import random import pyJHTDB import matplotlib.pyplot as plt import time as ttN 3 T pyJHTDB.dbinfo.channel5200[time][-1] time …

数据分析:彩票中奖号码分析与预测

预测双色球彩票的中奖号码是一个典型的随机事件&#xff0c;因为每个号码的出现概率是独立的&#xff0c;且历史数据并不能直接用于预测未来的开奖结果。然而&#xff0c;我们可以通过统计分析来了解号码的分布规律&#xff0c;从而提供一些可能的参考。 样例数据【点击下载】…

详细分析 npm run build 基本知识 | 不同环境不同命令

目录 前言1. 基本知识2. 构建逻辑 前言 关于部署服务器的知识推荐阅读&#xff1a;npm run build部署到云服务器中的Nginx&#xff08;图文配置&#xff09; 1. 基本知识 npm run 是 npm 的一个命令&#xff0c;用于运行 package.json 中定义的脚本&#xff0c;可以通过 “s…

Jpype调用jar包

需求描述 ​   公司要求使用python对接口做自动化测试&#xff0c;接口的实现是Java&#xff0c;部分接口需要做加解密&#xff0c;因此需要使用python来调用jar包来将明文加密成密文&#xff0c;然后通过http请求访问接口进行测试。 如何实现 1.安装Jpype ​   首先我…

Realtek网卡MAC刷新工具PG8168.exe Version:2.34.0.4使用说明

本刷新工具虽然文件名叫PG8168.EXE&#xff0c;但不是只有RTL8168可用&#xff0c;是这一个系列的产品都可以使用。实验证明RTL8111也可以使用。 用法&#xff1a; PG8168 [/h][/?][/b][/c HexOffsetHexValue][/d NICNumber][/l][/r][/w][/v] [/# NICNumber] [/nodeidHexNOD…

【Unity】Unity编辑器扩展,替代预制体上重复拖拽赋值

今天做游戏时有个需求&#xff0c;游戏中需要给不同年份不同月份的奖牌制定不一样的非规则形状&#xff0c;其中形状为100个像素组成的不同图形&#xff0c;并且按照从1-100路径一个个解锁&#xff0c;所以需要全部手动放置。但是手动放置好后&#xff0c;发现再一个个挂到脚本…

c语言的qsort函数理解与使用

介绍&#xff1a;qsort 函数是 C 标准库中用于排序的快速排序算法函数。它的用法非常灵活&#xff0c;可以对任意类型的元素进行排序&#xff0c;只要提供了比较函数即可。 qsort 函数原型及参数解释&#xff1a; void qsort ( void* base, //指向要排序的数组的首元素…

【力扣】125. 验证回文串

问题描述 思路详情 本题目的重点是对java中字符串的各种API用法的掌握理解 1.首先只保留字母和数字 1.1可以使用正则表达式1.2 Character.isLetterOrDight(ch) &#xff0c;但是这个只能单个字符判断2.将大写字母全部小写3.验证是否为回文串 代码 通过正则表达式 &#xff…

JavaEE---计算机是如何工作的?

1.了解冯诺依曼体系结构 2.CPU的核心概念,CPU的两个重要指标(核心数和频率) 3.CPU执行指令的流程(指令表,一条一条指令,取指令,解析指令,执行指令) 4.操作系统核心概念(管理硬件,给软件提供稳定的运行环境) 5.进程的概念(运行起来的程序和可执行文件的区别) 6.进程的管理(…

gitee:创建仓库,存入本地文件至仓库

一、git下载 git:下载与安装-CSDN博客https://blog.csdn.net/weixin_46001736/article/details/144107485?sharetypeblogdetail&sharerId144107485&sharereferPC&sharesourceweixin_46001736&spm1011.2480.3001.8118 二、创建仓库 1、主页面->右上角新增…