拓扑排序-邻接矩阵
获得一个图的拓扑排序可以有深搜和广搜两种途径。深搜是逆向思维,遍历到递归最深处,层层返回的时候把结点压栈,得到的是一个逆序拓扑序列。广搜是正向思维,从度为 0 的结点入手,每次将度为 0 的结点入队,直到全部遍历。
深搜:
int visited[5] = {0};
//visited有三种状态,未搜索-0/搜索中-1/搜索完成-2
//如果一个结点dfs到一个visited为1的结点那就说明能够通过这个结点再到达自己,说明有环
bool valid = true;//valid是标志位,初始为true,一旦出现环,置为false
stack<char> s;//栈保存结果
void dfs(MGraph& G,int u){
visited[u] = 1; //搜索中
for(int i = 0;i < G.numVertices;i ++){
if(G.edge[u][i] > 0){ //有出边
if(!visited[i]) {
dfs(G,i);
if(!valid) return; //有环退出
}
else if(visited[i] == 1){ //有环
valid = false;
return;
}
}
}
visited[u] = 2;
s.push(G.vertice[u]); //dfs后入栈
}
void findOrder_dfs(MGraph& G){
for(int i = 0;i < G.numVertices && valid;i ++)
if(!visited[i]) dfs(G,i);
if(!valid) return; //有环返回
while(!s.empty()){
cout<<s.top()<<" ";
s.pop();
}
}
广搜:
queue<char> res; //队列存放结果
int v[5] = {0}; //visited[]
int indeg[5] = {0}; //入度表
int visit = 0; //已访问的结点数
void bfs(MGraph& G,int u){
queue<int> q;
q.push(u);
v[u] = true;
while(!q.empty()){
visit ++; //访问结点数+1
int w = q.front();
res.push(G.vertice[w]); //放入结果队列
q.pop();
for(int i = 0;i < G.numVertices;i ++){
if(G.edge[w][i] > 0){
indeg[i] --; //入度-1
if(indeg[i] == 0 && !v[i]){ //度为0入队
v[i] = true;
q.push(i);
}
}
}
}
}
void findOrder_bfs(MGraph& G){
//初始化入边表
for(int i = 0;i < G.numVertices;i ++)
for(int j = 0;j < G.numVertices;j ++)
if(G.edge[i][j] > 0)
indeg[j] ++;
for(int i = 0;i < G.numVertices;i ++)
if(indeg[i] == 0 && !v[i]) bfs(G,i);
if(visit != G.numVertices) return; //有环返回
while(!res.empty()){
cout<<res.front()<<" ";
res.pop();
}
}
全部代码:
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
typedef struct{
int numVertices;
char vertice[10];
int edge[10][10];
}MGraph;
//深度优先遍历
/*
逆向思维:
对于一个节点 u,如果它的所有相邻节点都已经搜索完成,那么在搜索回溯到u的时候,
u本身也会变成一个已经搜索完成的节点。我们对图进行一遍深度优先搜索,当每个节点
进行回溯的时候,把该节点放入栈中。最终从栈顶到栈底的序列就是一种拓扑排序。
*/
int visited[5] = {0};
//visited有三种状态,未搜索-0/搜索中-1/搜索完成-2
//如果一个结点dfs到一个visited为1的结点那就说明能够通过这个结点再到达自己,说明有环
bool valid = true;//valid是标志位,初始为true,一旦出现环,置为false
stack<char> s;//栈保存结果
void dfs(MGraph& G,int u){
visited[u] = 1; //搜索中
for(int i = 0;i < G.numVertices;i ++){
if(G.edge[u][i] > 0){ //有出边
if(!visited[i]) {
dfs(G,i);
if(!valid) return; //有环退出
}
else if(visited[i] == 1){ //有环
valid = false;
return;
}
}
}
visited[u] = 2;
s.push(G.vertice[u]); //dfs后入栈
}
void findOrder_dfs(MGraph& G){
for(int i = 0;i < G.numVertices && valid;i ++)
if(!visited[i]) dfs(G,i);
if(!valid) return; //有环返回
while(!s.empty()){
cout<<s.top()<<" ";
s.pop();
}
}
//广度优先遍历
/*
正向思维:
我们使用一个队列来进行广度优先搜索。所有入度为0的节点都被放入队列中,
它们就是可以作为拓扑排序最前面的节点,并且它们之间的相对顺序是无关紧要的。
*/
queue<char> res; //队列存放结果
int v[5] = {0}; //visited[]
int indeg[5] = {0}; //入度表
int visit = 0; //已访问的结点数
void bfs(MGraph& G,int u){
queue<int> q;
q.push(u);
v[u] = true;
while(!q.empty()){
visit ++; //访问结点数+1
int w = q.front();
res.push(G.vertice[w]); //放入结果队列
q.pop();
for(int i = 0;i < G.numVertices;i ++){
if(G.edge[w][i] > 0){
indeg[i] --; //入度-1
if(indeg[i] == 0 && !v[i]){ //度为0入队
v[i] = true;
q.push(i);
}
}
}
}
}
void findOrder_bfs(MGraph& G){
//初始化入边表
for(int i = 0;i < G.numVertices;i ++)
for(int j = 0;j < G.numVertices;j ++)
if(G.edge[i][j] > 0)
indeg[j] ++;
for(int i = 0;i < G.numVertices;i ++)
if(indeg[i] == 0 && !v[i]) bfs(G,i);
if(visit != G.numVertices) return; //有环返回
while(!res.empty()){
cout<<res.front()<<" ";
res.pop();
}
}
int main(){
MGraph G;
G.numVertices = 5;
G.vertice[0]= 'a';
G.vertice[1]= 'b';
G.vertice[2]= 'c';
G.vertice[3]= 'd';
G.vertice[4]= 'e';
//a
G.edge[0][0] = 0;
G.edge[0][1] = 1;
G.edge[0][2] = 0;
G.edge[0][3] = 0;
G.edge[0][4] = 1;
//b
G.edge[1][0] = 0;
G.edge[1][1] = 0;
G.edge[1][2] = 1;
G.edge[1][3] = 0;
G.edge[1][4] = 0;
//c
G.edge[2][0] = 0;
G.edge[2][1] = 0;
G.edge[2][2] = 0;
G.edge[2][3] = 1;
G.edge[2][4] = 0;
//d
G.edge[3][0] = 0; //改为1,构成了环,无拓扑序列
G.edge[3][1] = 0;
G.edge[3][2] = 0;
G.edge[3][3] = 0;
G.edge[3][4] = 0;
//e
G.edge[4][0] = 0;
G.edge[4][1] = 0;
G.edge[4][2] = 1;
G.edge[4][3] = 0;
G.edge[4][4] = 0;
cout<<"dfs:"<<endl;
findOrder_dfs(G);
cout<<endl;
cout<<"bfs:"<<endl;
findOrder_bfs(G);
}
运行结果: