首页 > 其他分享 >有向图的拓扑排序——DFS

有向图的拓扑排序——DFS

时间:2022-12-30 16:24:27浏览次数:59  
标签:node 有向图 nodeStack 拓扑 DFS 访问 visited 节点

有向图的拓扑排序——BFS这篇文章中,介绍了有向图的拓扑排序的定义以及使用广度优先搜索(BFS)对有向图进行拓扑排序的方法,这里再介绍另一种方法:深度优先搜索(DFS)。

算法

考虑下面这张图:

img

首先,我们需要维护一个栈,用来存放DFS到的节点。另外规定每个节点有两个状态:未访问(这里用蓝绿色表示)、已访问(这里用黑色表示)。

任选一个节点开始DFS,比如这里就从0开始吧。

img

首先将节点0的状态设为已访问,然后节点0的邻居(节点0的出边指向的节点)共有1个:节点2,它是未访问状态,于是顺下去访问节点2。

img

节点2的状态也设为已访问。节点2有3个邻居:3、4、5,都是未访问状态,不妨从3开始。一直这样访问下去,直到访问到没有出边的节点7。

img

节点7没有出边了,这时候就将节点7入栈。

img

退回到节点6,虽然6还有邻居,但是唯一的邻居节点7是已访问状态,也入栈。再次退回,节点4的两个邻居也都已访问,依旧入栈并后退。以此类推,退回到节点2。

img

节点2有3个邻居,其中节点3和4已访问,但是节点5还未访问,访问节点5。

img

接下来的步骤是一样的,不再赘述了,直接退回到节点0并将0入栈。

img

现在,从节点0开始的DFS宣告结束,但是图中还有未访问的节点:节点1,从节点1继续开始DFS。

img

节点1的邻居节点2已经访问过了,直接将节点1入栈。

img

至此,整个DFS过程宣告结束。从栈顶到栈底的节点序列1 0 2 5 3 4 6 7就是整个图的一个拓扑排序序列。

实现

这里同样使用类型别名node_t代表节点序号unsigned long long

using node_t = unsigned long long;

同样使用邻接表来存储图结构,整个图的定义如下:

class Graph {
    unsigned long long n;
    vector<vector<node_t>> adj;

protected:
    void dfs(node_t cur, vector<bool> &visited, stack<node_t> &nodeStack);

public:
    Graph(initializer_list<initializer_list<node_t>> list) : n(list.size()), adj({}) {
        for (auto &l : list) {
            adj.emplace_back(l);
        }
    }

    vector<node_t> toposortDfs();
};

DFS

函数dfs的参数及说明如下:

  • cur:当前访问的节点。
  • visited:存放各个节点状态的数组,false表示未访问,true表示已访问。初始化为全为false
  • nodeStack:存放节点的栈。

整个过程如下:

  1. 首先,我们需要将当前节点的状态设为已访问:
visited[cur] = true;
  1. 依次检查当前节点的所有邻居的状态:
for (node_t neighbor: adj[cur]) {
    // ...
}
  1. 如果某个节点已访问,则跳过。
if (visited[neighbor]) continue;
  1. 否则,递归的对该节点进行DFS:
dfs(neighbor, visited, nodeStack);
  1. 所有邻居检查完后,就将该节点入栈:
nodeStack.push(cur);

整个dfs函数的代码如下:

void Graph::dfs(node_t cur, vector<bool> &visited, stack<node_t> &nodeStack) {
    visited[cur] = true;
    for (node_t neighbor: adj[cur]) {
        if (visited[neighbor]) continue;
        dfs(neighbor, visited, nodeStack);
    }
    nodeStack.push(cur);
}

拓扑排序

我们需要初始化3个数据结构:

  • sort:存放拓扑排序序列的数组。
  • visited:见上文。
  • nodeStack:见上文。
vector<node_t> sort;
vector<bool> visited(n, false);
stack<node_t> nodeStack;

整个过程如下:

  1. 依次检查每个节点的状态,如果未访问,则从该节点开始进行DFS:
for (node_t node = 0; node < n; ++node) {
    if (visited[node]) continue;
    dfs(node, visited, nodeStack);
}
  1. 此时nodeStack已经存储了整个拓扑排序序列,我们只需要转移到sort数组并返回即可:
while (!nodeStack.empty()) {
    sort.push_back(nodeStack.top());
    nodeStack.pop();
}
return sort;

整个代码如下:

vector<node_t> Graph::toposortDfs() {
    vector<node_t> sort;
    vector<bool> visited(n, false);
    stack<node_t> nodeStack;
    for (node_t node = 0; node < n; ++node) {
        if (visited[node]) continue;
        dfs(node, visited, nodeStack);
    }
    while (!nodeStack.empty()) {
        sort.push_back(nodeStack.top());
        nodeStack.pop();
    }
    return sort;
}

测试

代码:

int main() {
    Graph graph{{2},
                {2},
                {3, 4, 5},
                {4},
                {6, 7},
                {4},
                {7},
                {}};
    auto sort = graph.toposortDfs();
    cout << "The topology sort sequence is:\n";
    for (const auto &node: sort) {
        cout << node << ' ';
    }
    return 0;
}

输出:

The topology sort sequence is:
1 0 2 5 3 4 6 7

复杂度分析

  • 时间复杂度:\(O(n+e)\),\(n\)为节点总数,\(e\)为边的总数。其中DFS的时间复杂度为\(O(n+e)\)。
  • 空间复杂度:\(O(n)\)(邻接表的空间复杂度为\(O(n+e)\),不计入在内),其中维护visited数组和nodeStack栈分别需要\(O(n)\)的额外空间。

标签:node,有向图,nodeStack,拓扑,DFS,访问,visited,节点
From: https://www.cnblogs.com/YWT-Real/p/17015184.html

相关文章

  • 分布式文件系统 - FastDFS 配置 Nginx 模块及上传测试
    一、安装Nginx和fastdfs-nginx-module安装Nginx请看:​​从零开始学Java-CentOS下安装Nginx​​,其实我只想放这一句话。但想想我还是一步一步写详细吧。1.下载Ngi......
  • 【Hadoop】hdfs dfs -test命令
    使用方法hdfsdfs-test-[defswrz]<path>:Answervariousquestionsabout<path>,withresultviaexitstatus.-dreturn0if<path>isadirectory.......
  • P1036 [NOIP2002 普及组] 选数(DFS + 不降原则)
    P1036[NOIP2002普及组]选数题意​ 在n个数里选k个数,有多少中选法,使得选出来的数的和为素数。不能重复选。思路​ n很小,直接爆搜,但是如果不使用不降原则的话,就......
  • .net core-利用PdfSharpCore 操作PDF实例
    .netcore-利用PdfSharpCore操作PDF实例 前序使用PdfSharpCore请注意使用XGraphics基类,与System.Drawing的Graphics类似,XGraphics提供XColor(颜色)、XPen(画笔)、XBru......
  • POJ 2531 Network Saboteur(DFS)
    POJ2531NetworkSaboteur题意​ 把n个节点分成AB两组,给出矩阵\(C_{i,j}\),求\(\sum{C_{i,j}}(i\inA,j\inB)\)的最大值。思路​ n很小,直接爆搜做。枚举一下......
  • 网络拓扑结构可视化呈现方案
    随着数字化进程的加速,企业网络中设备的数量日益快速增长,网络规模逐渐庞大,组网结构、IT环境变的无比复杂,需要花费大量的时间和资源去监测网络运行状态,诊断解决故障问题。面......
  • .net core-利用PdfSharpCore 操作PDF实例
    前序使用PdfSharpCore请注意使用XGraphics基类,与System.Drawing的Graphics类似,XGraphics提供XColor(颜色)、XPen(画笔)、XBrush(画刷)、XFont(字体)、XPoint(位置)等对象。提......
  • Polynomial Round 2022 E. Two Chess Pieces(dfs+dp)
    E.TwoChessPieces题目大意:给定n个节点的以1为根节点的有根树,现在在根节点上有两颗棋子,我们分别给他们规定了它们所必须经过的点,每次可以顺着树移动距离1,但是必须使得......
  • POJ--2386 Lake Counting(DFS)
    记录23:182022-12-25http://poj.org/problem?id=2386reference:《挑战程序设计竞赛(第2版)》2.1.4p33DescriptionDuetorecentrains,waterhaspooledinvariou......
  • 拓扑排序
    目录拓扑排序定义算法Kahn(卡恩)算法DFS算法应用应用1:Leetcode.207题目解题思路方法一:深度优先方法二:广度优先代码实现拓扑排序定义有向图的拓扑排序是对其顶点的一种线......