2021-1 拓扑排序的准备,深度优先搜索的三个顺序

这篇博客介绍了深度优先搜索(DFS)在有向无环图(DAG)中的应用,特别是如何通过DFS进行顶点排序以获取拓扑顺序。文章详细阐述了DFS的特性,包括每个顶点只会被访问一次,以及前序、后序和逆后序三种顺序。在有向无环图中,逆后序排列给出了拓扑顺序。文中还提供了伪代码和实际示例,展示了如何使用DFS实现三种排序,并给出了测试代码片段。
摘要由CSDN通过智能技术生成
DFS的特性
  • 每个顶点只会被访问一次
  • dfs()的参数保存在一个数据结构中就能访问到图中的所有顶点
有用的三个顺序
  • 前序:递归之前,顶点加入队列 pre()
  • 后序:递归之后,顶点加入队列 post()
  • 逆后序:递归之后,顶点入栈 reversePost()
//伪代码
dfs(G ,v):
	pre.enqueue(v)//函数栈调用的先后次序
	for each w in v.adj():
		dfs(G,w)
	post.enqueue(v)//完成dfs的节点次序,先完成的在队头
	reversePost.enstack(v)//完成dfs的节点次序,但,先完成的在栈底
一幅有向无环图的拓扑顺序即为所有顶点的逆后序排列

证明:对于任意边 v->w,再调用dfs(v)下面三种情况必有其一成立:

  • dfs(w) 已经调用且已经返回(w已经被标记,比如,w只是v的第一个儿子且为叶子,现在访问第二个儿子)
  • dfs(w)还没被调用(w未被标记),因此,v->w会直接或间接的调用并返回dfs(w),且dfs(w)会在dfs(v)返回前返回。(dfs中儿子会在父亲前被返回)
  • dfs(w)已经被调用但是还未返回。证明的关键是,这种情况说明w已经在函数栈中,即存在一条w到v的路径,加上当前的v->w,已经构成环。即这种情况不可能存在于有向无环图中。

综上,在有向无环图中前两种情况,儿子总能比父亲先完成dfs,因此在后序排列中儿子在前父亲在后,逆后序中保证父亲v在前(栈顶)儿子w在后(栈底)。因此任意一条边v->w都能如愿的从排名较前的顶点指向排名靠后的顶点。

注解:dfs()先完成的节点有什么特征?儿子少,或者说是个光棍。称为这类节点排名靠后联想最经典的选课次序图,排名靠后的大概像《嵌入式系统概论》,《图像识别与深度学习》,排名较前的像《计算机基础》,它的儿子就很多。

图示

在这里插入图片描述
注意到,三号栈,依次弹出<0 1 3 2>刚好就是拓扑序列。

有向图基于DFS的顶点排序

对下图记录三种排序
在这里插入图片描述
pre: 0 5 4 3 2 1 6 9 11 12 10 8 7
rePost:7 6 8 9 10 11 12 0 1 5 4 3 2

头文件和测试文件,点这里 Digraph.h

#pragma once

#include"Digraph.h"
#include<queue>
#include<stack>


class DepthFirstOrder
{
public:
	DepthFirstOrder(Digraph& G);
	
	
	queue<int>* getPre() {
		return m_pre;
	}

	queue<int>* getPost() {
		return m_post;
	}

	stack<int>* getRePost() {
		return m_rePost;
	}

private:
	vector<bool>* m_marked = nullptr;
	queue<int>* m_pre = nullptr;
	queue<int>* m_post = nullptr;
	stack<int>* m_rePost = nullptr;

	void dfs(Digraph& G, int v);
};


void testDFOrder();
#include"DepthFirstOrder.h"

DepthFirstOrder::DepthFirstOrder(Digraph& G)
{
	m_pre = new queue<int>();
	m_post = new queue<int>();
	m_rePost = new stack<int>();

	m_marked = new vector<bool>(G.V(), false);
	for (int i = 0; i < G.V(); ++i) {
		if (!m_marked->at(i)) dfs(G, i);
	}
}

inline
void DepthFirstOrder::dfs(Digraph& G, int v)
{
	m_pre->push(v);
	m_marked->at(v) = true;
	forIt(G.adj(v)) {
		int w = *it;
		if (!m_marked->at(w)) {
			dfs(G, w);
		}
	}
	m_post->push(v);
	m_rePost->push(v);
}

void testDFOrder()
{
	Digraph G("tinyDG.txt");
	DepthFirstOrder dfO(G);

	queue<int>* q = dfO.getPre();

	while (!q->empty()) {
		int x = q->front(); q->pop();
		out(x);
	}
	hh;

	stack<int>* stk = dfO.getRePost();
	while (!stk->empty()) {
		int x = stk->top(); stk->pop();
		out(x);
	}
	hh;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值