【算法】【图论】拓扑排序_图网络节点优先级排序-程序员宅基地

技术标签: 算法  # 算法  图论  

相关概念:

  • 拓扑排序,就是在有向图中找到一条拓扑序列,序列中排列在前的节点相较于排列在后的节点有更高的优先级。
    • 首先要理解优先级,优先级是有向图中边的方向性带来的结果,如 a -> b 表示节点 a 指向节点 b,则可以认为 a 的优先级优于 b,在特定语境下可以说 a 必须在 b 之前完成,如下图所示,该有向图的拓扑排序结果为 1432541325在这里插入图片描述

    • 拓扑排序算法可以用来判断有向图是否有环,可以生成拓扑序列。

      • 网络中可能存在多个拓扑排序结果。
      • 如果网络中不存在环,则结果中会包括所有节点。
      • 但如果网络中存在环,则结果中可能会缺少某些节点。
  • 图可以有不同的表现形式,如邻接矩阵表示形式和邻接表表示形式,有向图的邻接表表示形式。
    • 简单来说,假设图中有 m 个节点,邻接表就是维护一个 m 大小的数组 vec,其中数组元素为当前节点的外接节点链表。【例如在网络中 a -> ba -> c,则数组中 a 节点对应的位置就保存一个链表 b => c,相当于 vec(a) => b => c (此处用 => 表示链表指针,bc 在链表中的顺序不分先后)】
    • 也就是说,数组中第 i 个元素(记住数组元素为链表)保存的是网络中第 i 个节点的 one-hop neighbor。【即节点跳一步就可以达到的邻居节点】
  • 进行后续算法的介绍前,还需要理解节点的入度
    • 当前节点的入度,指的就是网络中有多少节点直接指向当前节点。【如上图所示,节点 3 的入度为 2
    • 可用一个数组统计每个节点的入度。

核心思路:

  • 拓扑排序问题有两种解决方案:Kahn 算法dfs 着色解法

  • Kahn 算法的核心思想就是每次删除一个当前入度为 0 的节点,并删除该节点对链出邻居的影响(即其指向邻居的入度数减一)。

    • Kahn 算法需要图的邻接表形式。
    • 可以用 stackqueue 来保存当前入度为 0 的节点。【即用 stackqueue 维护当前入度为 0 的点集】
    • 用上面的图来理解,Kahn 算法的第一步就是把节点 1 带来的影响消去,留下其余的节点和边作为子图进行拓扑排序:在这里插入图片描述
  • dfs 着色解法即利用深度优先算法的方法以任意顺序遍历图中所有节点,但遍历的过程中需要对节点着色

    • 不同的颜色代表访问的程度深浅,过程中遇到不同颜色的节点会有相应的处理;需要一个数组维护网络中节点的颜色,颜色的状态可以为 0-11
      • -1 表示访问过,如果重复遇上,则说明存在环。
    • 最后需要把结果数组进行 reverse 操作,得出来的才是拓扑序列。【因为 dfs 着色解法会优先深度搜索到优先级最低的节点,所以最先存入结果数组的就是优先级最低的节点,最后结果数组就需要翻转过来才可】

算法步骤:

  • Kahn 算法的算法步骤如下:【假设用栈来维护点集】

    1. 初始化,所有入度为 0 的节点入栈。
    2. 每次从栈中 pop 出一个节点放入结果数组。
      1. 同时该节点指向的所有节点都需要进行入度减一操作。
      2. 如果邻居的入度变为 0 则邻居入栈。
    3. 不断重复 2 过程直到栈为空。
    4. 如果结果数组个数为网络中节点个数,则说明网络中无环,否则说明网络中有环。
  • dfs 着色解法的算法步骤如下:

    1. 初始化,所有点染色为 0
    2. 遍历每个节点进入 dfs 函数。【如果要遍历的点颜色不为 0,则不进入递归】
      1. 假设当前节点为 x,将其染色为 -1,接着遍历 x 的邻居。
      2. 假若当前邻居 y 的颜色为 0 ,则递归进入 dfs(y);假若当前邻居 y 的颜色为 -1,则说明网络中存在环。
      3. 假若当前节点 x 的所有邻居都走完且没有发现环,则把 x 保存进结果数组中。
    3. 将结果数组进行翻转操作。

实现代码:

  • Kahn 算法的实现代码如下:
#include <iostream>
#include <stack>
#include <vector>
using namespace std;

vector<int> kahn_algorithm(vector<vector<int>> &graph, vector<int> &in, int n) {
    
  vector<int> ans;
  stack<int> stk;

  // 第一步,初始化
  for (int i = 1; i <= n; ++i)
    if (in[i] == 0)
      stk.push(i);
  
  // 第二步,从栈中一直 pop 出元素并存放到结果中
  while (!stk.empty()) {
    
    int x = stk.top();
    stk.pop();
    ans.push_back(x);
    for (auto &next : graph[x])
      if (--in[next] == 0)
        stk.push(next);
  }
  return ans;
}

int main() {
    
  int m, n; // 输入 m 条边,n 个节点,节点标记为 1~n
  cin >> m >> n;
  vector<int> in(n + 1);            // 入度数组
  vector<vector<int>> graph(n + 1); // 邻接表
  for (int i = 0; i < m; ++i)       // 输入 m 的边的具体情况
  {
    
    int a, b;
    cin >> a >> b;
    ++in[b];
    graph[a].push_back(b);
  }
  vector<int> ans = kahn_algorithm(graph, in, n);
  for (int num : ans)
    cout << num << " ";
  cout << endl;
  return 0;
}
  • dfs 着色解法的实现代码如下:
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

vector<int> ans;

bool dfs(vector<vector<int>>& graph, vector<int>& color, int x)
{
    
  color[x] = -1;
  for(int y : graph[x])
  {
    
    if(color[y] == -1 or (color[y] == 0 and dfs(graph, color, y) == false)) // 有环
      return false;
  }
  color[x] = 1;
  ans.push_back(x);
  return true;
}

bool dfs_color(vector<vector<int>> &graph, int n)
{
    
  vector<int> color(n+1, 0);
  for(int x = 1; x <= n; ++x) if(color[x] == 0)
  {
    
    if(dfs(graph, color, x) == false) return false;
  }
  reverse(ans.begin(), ans.end());
  return true;
}

int main() {
    
  int m, n; // 输入 m 条边,n 个节点,节点标记为 1~n
  cin >> m >> n;
  vector<vector<int>> graph(n + 1); // 邻接表
  for (int i = 0; i < m; ++i)       // 输入 m 条边的具体情况
  {
    
    int a, b;
    cin >> a >> b;
    graph[a].push_back(b);
  }

  if(dfs_color(graph, n) == false)
  {
    
    cout << "有环" << endl;
    return 0;
  }

  for (int num : ans)
    cout << num << " ";
  cout << endl;
  return 0;
}

部分题目:

  • leetcode - 剑指 Offer Ⅱ 115.重建序列
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44705592/article/details/126104703

智能推荐

ASP.NET绘制折线图---(2)获取数据-程序员宅基地

文章浏览阅读229次。1.从数据库中获取数据 建立数据库连接并查询,可以专门新建一个C#类来提高代码复用率,也可以只写一个函数。下面的代码是我写的一个C#类SQL_Connection,放在App_Code文件夹下,专门用来执行数据库操作,代码如下:using System;using System.Collections.Generic;using System.Linq;..._.net 连接数据库 绑定 折线图

JS 输出从1到100之间所有不能被3整除的数;并输出这些整数的和_用java输出1-10000不能被3整除的数-程序员宅基地

文章浏览阅读3.8k次。输出从1到100之间所有不能被3整除的数;并输出这些整数的和代码图: var sum = 0; for (var num = 0; num < 100; num++) { if (num % 3 != 0) sum += num; console.log(sum);做法如下..._用java输出1-10000不能被3整除的数

SQL Plus工具的使用_sol plus工具使用详情-程序员宅基地

文章浏览阅读2k次,点赞2次,收藏6次。一. SQL Plus是一个字符界面工具,所有功能均以命令行的方式执行,需要涉及并使用部分常用的DOS命令,doc命令如下:命令提示符程序的启动和退出。菜单中输入cmd进入命令提示符程序退出cmd:可以直接输入exit命令,按回车键可退出命令提示符程序。改变当前路径。a)在命令行状态下,如果行左侧不是C:>,表示当前工作路径不是根目录(如图1-21所示),可执行..._sol plus工具使用详情

【OPNET】Recoverable Error, Process model (aodv_rte) compilation failed_opnet reverable err 错误-程序员宅基地

文章浏览阅读1.9k次,点赞3次,收藏5次。现象:在仿真自带模型过程中遇到了下面这个问题:(2019-10-21 16:32:30: 如果运行的是自己的工程,看你需要把这个stdmod给删掉才能用!)<<< Recoverable Error >>>Process model (aodv_rte) compilation failedErrors given in file (C:\User..._opnet reverable err 错误

Java基础+集合(含答案)_2. q: 下列哪个集合类是线程安全的,适用于多线程环境下的字符串拼接?-程序员宅基地

文章浏览阅读1k次。一、Java基础系列面试题1. JDK 和 JRE 有什么区别?jre是java程序运行的环境,jdk是java开发所需要的环境,jdk包含jre2.== 和 equals 的区别是什么==和Object的equals都是比较的地址,但有一些类会重写equals方法,string中会比较两个字符串的值3. 两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?不对,hashcode和equals是两个方法,没有重写的equals比较的是对象的地址值,_2. q: 下列哪个集合类是线程安全的,适用于多线程环境下的字符串拼接?

监听器Listener_listener监听器-程序员宅基地

文章浏览阅读1.6k次,点赞4次,收藏13次。(1) 实现了特定接口的类为监听器,用来监听另一个java类的方法调用或者属性改变; (2)当被监听的对象发生了方法调用或者属性改变后,监听器的对应方法就会立即执行。 监听器涉及到以下几个组成部分: 1、事件源:被监听的对象,即:request、session、servletContext三大域对象。 2、监听器:监听事件源对象,事件源对象状态的变化都会触发监听器 3、注册_listener监听器

随便推点

出海应用如何把控数据隐私边界?| Google Play 开发者播客节目 · 第四期-程序员宅基地

文章浏览阅读551次。本期简介在 Cambridge Analytica 事件的影响下,关于消费者隐私的法律越来越严格,处罚力度也不断加大。确保您的游戏或应用有着良好的隐私政..._这篇文章对你有帮助吗?作为一名程序工程师

dpdk l2fwd 应用流程分析-程序员宅基地

文章浏览阅读431次。intMAIN(int argc, char **argv){ struct lcore_queue_conf *qconf; struct rte_eth_dev_info dev_info; int ret; uint8_t nb_ports; uint8_t nb_ports_available; uint8_t portid, l..._cause: all available ports are disabled. please set portmask.

上下文切换,你确定了解吗?-程序员宅基地

文章浏览阅读1.7k次,点赞4次,收藏14次。上下文是指进程(或线程)的运行环境,它包括了当前执行的代码位置、寄存器内容、栈指针、内存映像、打开的文件、网络连接等状态信息。而上下文切换则是指当一个进程由于某种原因需要放弃 CPU 的控制权时,操作系统将它的上下文保存下来,并恢复另一个进程的上下文以便让其继续执行。_上下文切换

基于SSM+VUE的药品销售管理系统的设计与实现-程序员宅基地

文章浏览阅读2.5k次,点赞3次,收藏59次。研究的背景与意义随着互联网技术的快速发展,网络时代的到来,网络信息也将会改变当今社会。各行各业在日常企业经营管理等方面也在慢慢的向规范化和网络化趋势汇合。药店药品销售系统的信息化程度体现在将互联网与信息技术应用于经营与管理,以现代化工具代替传统手工作业。无疑,使用网络信息化管理使信息管理更先进、更高效、更科学,信息交流更迅速。对于之前药店系统的管理,大部分都是使用传统的人工方式去管理,这样导致了管理效率低下、出错频率高。而且,时间一长的话,积累下来的数据信息不容易保存,对于查询、更新还有维护会带来不少问

Centos8上安装中文字符集zh_CN.UTF-8_zh_cn.utf8和zh_cn.utf-8-程序员宅基地

文章浏览阅读5.7k次。先查看当前的字符集locale #查看环境字符集locale -a #查看平台所有字符集$ echo $LANGen_US.UTF-8安装中文字符集 yum install glibc-common yum install -y langpacks-zh_CN vim /etc/locale.conf # 修改locale.conf文件 LANG=zh_CN.utf8 source /etc/locale.conf查看修改后字符集$ echo $LANGzh_CN._zh_cn.utf8和zh_cn.utf-8

navicat查看mysql连接数据库_使用Navicat for MySQL数据库连接服务器中的MySQL服务-程序员宅基地

文章浏览阅读2.8k次。本文主要向大家介绍了使用Navicat for MySQL数据库连接服务器中的MySQL服务,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助。第一步:登录mysql服务器,新建一个用户。在mysql安装中,默认的有root用户,但是root用户的默认连接Host也是localhost或127.0.0.1,也就是限制了root用户作为本地连接使用。登录mysql服务进入到mys..._navicat怎么查看已连数据库的账号连接信息