拓扑排序如何用DFS实现??
首先了解DFS生成树,
根据拓扑排序的定义可以知道,只有图没有环路时才有拓扑排序,由上述DFS生成树可知,当图的DFS生成树没有反祖边时没有环路,这时就可以找出图的拓扑排序(建议画一张图,然后用DFS遍历一遍,就能理解为什么得出拓扑排序)
该算法采用 DFS+栈
首先结构定义
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MaxV = 100;
/*定义栈, 注意栈的数据类型为string*/
typedef struct SNode{
string *Data;
int Top;
int MaxSize;
} * Stack;
typedef struct ENode{
int v1, v2;
string va, vb;
} * Edge;
struct AdjVNode{
int AdjV;
AdjVNode *Next;
};
typedef struct VNode{
string Data;
bool tag;
AdjVNode *EdgeFirst;
} AdjList[MaxV];
typedef struct GNode{
AdjList L;
int Nv, Ne;
} * LGraph;
栈的需要的操作
/*初始化栈*/
Stack CreateStack(int MaxSize)
{
Stack S = new SNode;
S->Data = new string[MaxSize];
S->MaxSize = MaxSize;
S->Top = -1;
return S;
}
/*入栈*/
void Push(Stack S, string x)
{
S->Data[++S->Top] = x;
}
/*出栈*/
string Pop(Stack S)
{
return S->Data[S->Top--];
}
/*查找栈中有没有元素x, 并返回在栈中的位置, 如果不在栈中返回-1*/
int FindS(Stack S, string x)
{
for (int i = S->Top; i >= 0; i--)
if (S->Data[i] == x)
return i;
return -1;
}
/*将边由string型加入对应的int型*/
void Edge_Code(Edge E, Stack S) //因为这里输入的顶点不再是固定的从0~Nv - 1的编号
{
int k = FindS(S, E->va);
if (k < 0) //如果顶点不在栈中, 压入栈
{
Push(S, E->va);
E->v1 = S->Top; //得到新的v1的编号
}
else
E->v1 = k; //得到已有v1的编号
k = FindS(S, E->vb);
if (k < 0) //如果顶点不在栈中, 压入栈
{
Push(S, E->vb);
E->v2 = S->Top; //得到新的v2的编号
}
else
E->v2 = k; //得到已有v2的编号
}
图的相关操作
/*初始化邻接表*/
LGraph CreateGraph(int Nv)
{
LGraph G = new GNode;
G->Nv = Nv; G->Ne = 0;
for (int i = 0; i < G->Nv; i++)
{
G->L[i].EdgeFirst = NULL;
G->L[i].tag = false;
}
return G;
}
/*插入边操作*/
void InsertEdge(LGraph G, Edge E)
{
AdjVNode *V = new AdjVNode;
V->AdjV = E->v2;
V->Next = G->L[E->v1].EdgeFirst;
G->L[E->v1].EdgeFirst = V;
}
/*建立新的图, 键盘输入*/
LGraph BuildGraph()
{
int Nv;
cin >> Nv;
LGraph G = CreateGraph(Nv);
cin >> G->Ne;
Edge E = new ENode;
Stack S = CreateStack(G->Nv);
for (int i = 0; i < G->Ne; i++)
{
cin >> E->va >> E->vb;
Edge_Code(E, S);
InsertEdge(G, E);
G->L[E->v1].Data = E->va;
G->L[E->v2].Data = E->vb;
}
delete E;
delete[] S->Data;
delete S;
return G;
}
/*删除图*/
void DeleteGraph(LGraph G)
{
AdjVNode *V;
for (int i = 0; i < G->Nv; i++)
while (G->L[i].EdgeFirst)
{
V = G->L[i].EdgeFirst;
G->L[i].EdgeFirst = V->Next;
delete V;
}
delete G;
}
关键代码,拓扑排序算法
/*拓扑排序DFS算法*/
/*tag: 0表示未访问, 1表示已访问且正常, -1表示已访问但有环*/
bool DFS(LGraph G, int u, Stack S) //当前DFS函数退出时,下一访问顶点是兄弟或父亲
{ //给予DFS()返回值,false表示有环,true表示无环
G->L[u].tag = -1; //if:DFS(i - 1) == false && Tree(i - 1)中没有指向顶点i的边,就有 DFS(i) = false
AdjVNode *V = G->L[u].EdgeFirst;// Tree树是DFS生成树
while (V)
{
if(G->L[V->AdjV].tag == -1) //此时表明在当前函数下,又回到了u点, 即有回路
return false;
if(G->L[V->AdjV].tag == 0 && !DFS(G, V->AdjV, S)) //顶点v没有访问过, 但是v的子节点出现有环,返回错误
return false; //有DFS()进入下一递归函数, 作为后判断
/*函数运行到此处,表示以u为顶点的其中一条边访问完毕,下面进行u的另一条边的访问*/
V = V->Next;
}
/*退出循坏后,表明,而上面没有return,则以u为顶点的*/
G->L[u].tag = 1; //对u所有的边访问完后,tag = 1表明正常
Push(S, G->L[u].Data); //注意,入栈是后序(通过前面自己模拟DFS可以明白)
return true;
}
bool TopSort(LGraph G, Stack S) //返回值表示是否有拓扑排序, true表示有拓扑排序
{
for (int i = 0; i < G->Nv; i++)
if(G->L[i].tag == 0) //没访问过才访问
if(!DFS(G, i, S)) //一旦发现有环, 直接退出
return false;
return true;
}
实例一
输入
14 21
w z
v w
p z
p s
o s
p o
n o
o r
s r
o v
v x
y v
r y
r u
n u
u t
q t
n q
m r
m q
m x
若插入一条有向边<t, m>使之成为一个有环图
实例二
输入1
13 15
0 1
0 5
0 6
2 0
2 3
3 5
5 4
6 4
7 6
8 7
6 9
9 10
9 12
9 11
11 12
输入2
13 22
0 1
0 5
2 0
2 3
3 2
3 5
4 2
4 3
5 4
6 0
6 4
6 9
7 6
7 8
8 7
8 9
9 10
9 11
10 12
11 4
11 12
12 9
参考
https://zhuanlan.zhihu.com/p/45051460
完整代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MaxV = 100;
/*定义栈, 注意栈的数据类型为string*/
typedef struct SNode{
string *Data;
int Top;
int MaxSize;
} * Stack;
typedef struct ENode{
int v1, v2;
string va, vb;
} * Edge;
struct AdjVNode{
int AdjV;
AdjVNode *Next;
};
typedef struct VNode{
string Data;
int tag;
AdjVNode *EdgeFirst;
} AdjList[MaxV];
typedef struct GNode{
AdjList L;
int Nv, Ne;
} * LGraph;
/*初始化栈*/
Stack CreateStack(int MaxSize)
{
Stack S = new SNode;
S->Data = new string[MaxSize];
S->MaxSize = MaxSize;
S->Top = -1;
return S;
}
/*入栈*/
void Push(Stack S, string x)
{
S->Data[++S->Top] = x;
}
/*出栈*/
string Pop(Stack S)
{
return S->Data[S->Top--];
}
/*查找栈中有没有元素x, 并返回在栈中的位置, 如果不在栈中返回-1*/
int FindS(Stack S, string x)
{
for (int i = S->Top; i >= 0; i--)
if (S->Data[i] == x)
return i;
return -1;
}
/*将边由string型加入对应的int型*/
void Edge_Code(Edge E, Stack S)
{
int k = FindS(S, E->va);
if (k < 0) //如果顶点不在栈中, 压入栈
{
Push(S, E->va);
E->v1 = S->Top; //得到新的v1的编号
}
else
E->v1 = k; //得到已有v1的编号
k = FindS(S, E->vb);
if (k < 0) //如果顶点不在栈中, 压入栈
{
Push(S, E->vb);
E->v2 = S->Top; //得到新的v2的编号
}
else
E->v2 = k; //得到已有v2的编号
}
/*初始化邻接表*/
LGraph CreateGraph(int Nv)
{
LGraph G = new GNode;
G->Nv = Nv; G->Ne = 0;
for (int i = 0; i < G->Nv; i++)
{
G->L[i].EdgeFirst = NULL;
G->L[i].tag = 0;
}
return G;
}
/*插入边操作*/
void InsertEdge(LGraph G, Edge E)
{
AdjVNode *V = new AdjVNode;
V->AdjV = E->v2;
V->Next = G->L[E->v1].EdgeFirst;
G->L[E->v1].EdgeFirst = V;
}
/*建立新的图, 键盘输入*/
LGraph BuildGraph()
{
int Nv;
cin >> Nv;
LGraph G = CreateGraph(Nv);
cin >> G->Ne;
Edge E = new ENode;
Stack S = CreateStack(G->Nv);
for (int i = 0; i < G->Ne; i++)
{
cin >> E->va >> E->vb;
Edge_Code(E, S);
InsertEdge(G, E);
G->L[E->v1].Data = E->va;
G->L[E->v2].Data = E->vb;
}
delete E;
delete[] S->Data;
delete S;
return G;
}
/*删除图*/
void DeleteGraph(LGraph G)
{
AdjVNode *V;
for (int i = 0; i < G->Nv; i++)
while (G->L[i].EdgeFirst)
{
V = G->L[i].EdgeFirst;
G->L[i].EdgeFirst = V->Next;
delete V;
}
delete G;
}
/*拓扑排序DFS算法*/
/*tag: 0表示未访问, 1表示已访问且正常, -1表示已访问但有环*/
bool DFS(LGraph G, int u, Stack S) //当前DFS函数退出时,下一访问顶点是兄弟或父亲
{ //给予DFS()返回值,false表示有环,true表示无环
G->L[u].tag = -1; //if:DFS(i - 1) == false && Tree(i - 1)中没有指向顶点i的边,就有 DFS(i) = false
AdjVNode *V = G->L[u].EdgeFirst;// Tree树是DFS生成树
while (V)
{
if(G->L[V->AdjV].tag == -1) //此时表明在当前函数下,又回到了u点, 即有回路
return false;
if(G->L[V->AdjV].tag == 0 && !DFS(G, V->AdjV, S)) //顶点v没有访问过, 但是v的子节点出现有环,返回错误
return false; //有DFS()进入下一递归函数, 作为后判断
/*函数运行到此处,表示以u为顶点的其中一条边访问完毕,下面进行u的另一条边的访问*/
V = V->Next;
}
/*退出循坏后,表明,而上面没有return,则以u为顶点的*/
G->L[u].tag = 1; //对u所有的边访问完后,tag = 1表明正常
Push(S, G->L[u].Data); //注意,入栈是后序(通过前面自己模拟DFS可以明白)
return true;
}
bool TopSort(LGraph G, Stack S) //返回值表示是否有拓扑排序, true表示有拓扑排序
{
for (int i = 0; i < G->Nv; i++)
if(G->L[i].tag == 0) //没访问过才访问
if(!DFS(G, i, S)) //一旦发现有环, 直接退出
return false;
return true;
}
int main()
{
LGraph G = BuildGraph();
Stack S = CreateStack(G->Nv);
if(!TopSort(G, S)) //如果没有拓扑排序
cout << "无拓扑排序,存在有向环!" << endl;
else
{
cout << "拓扑排序为:" << endl;
while (S->Top >= 0)
cout << Pop(S) << " ";
cout << endl;
}
DeleteGraph(G);
delete[] S->Data;
delete S;
system("pause");
return 0;
}