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;
}