提示:古人学问无遗力 少壮功夫老始成,纸上得来终觉浅 觉知此事须躬行
文章目录
- 一、AOV网的特点
- 二、问题
- 三、拓扑排序
- 四、拓扑排序的方法
- 五、检查AOV网中是否存在一个环
- 六、两种思路
- 6.1、思路一
- 6.1.1、思路一代码实现
- 6.1.2、思路一运行截图
- 6.2、思路二
-
- 七、可执行代码汇总
- 总结
一、AOV网的特点
1、若是从i到j有有一条有向路径,则i是j的前驱,j是i的后继
2、若<i,j> 是网中有向边,则i是j的直接前驱,j是i的直接后继
3、AOV网中不允许有回路,因为如果有回路存在,则表明某项活动以自己为先决条件,显然不可能
二、问题
我们要解决如下两个问题
1、如何进行拓扑排序
2、如何判别AOV网中是否存在回路
三、拓扑排序
在AOV网没有回路的前提下,我们将全部活动排列成一个线性序列,使得AOV网中有弧<i,j>存在 则在这个序列中,i 一定排在j的前面 具有这种线性序列称为拓扑有序序列,相应的拓扑有序排序的算法称为拓扑排序
四、拓扑排序的方法
1、在有向图中选一个没有前驱的顶点且输出
2、从图中删除该顶点和所有以他为尾的弧
3、重复上述步骤,直到所有的顶点均已输出,或者当图中不存在无前驱的顶点为止
比如我们要对上述图进行拓扑排序 第一次寻找的是A这一个结点 因为A没有前驱 接下来就是删除A以及以A为弧尾的弧,此时就是如下图
在从中选择一个没有前驱的结点,这里BCD 都可以,这里我们就选择B 同样的道理 删除B以及以B为弧尾的弧此时就是如下图
同样的道理 再选一个C删除以C为弧尾的弧
再删除 D
再删除E
所有总的拓扑排序是 当然排序可能不止一种 因为可能不止一个结点在某一步骤中是无前驱的
ABCDE 有人可能会说了这个图太简单了,能不能难一点的图来拓扑
如下
拓扑排序可以如下
AEBDC
EABDC
AEDBC
EADBC
五、检查AOV网中是否存在一个环
对有向图构造其顶点的拓扑有序序列,若是网中所有顶点都在它的拓扑有序序列中,则该AOV 网必定不存在环 若是最后还剩下几个顶点必然是存在环,如下图
第一次我们删除的是A 第二次我们就找不到没有前驱的节点了 所以就可以判断是存在回路的
六、两种思路
6.1、思路一
1、需要知道那些结点没有前驱 也就是入度为零 怎么找到呢?
本来想着可以使用逆邻接表来实现 不过也确实可以 但是这里依然使用邻接矩阵的方式
我们知道存储有向图的时候 邻接矩阵中行表示的是每一个结点的出度,那么列其实自然就是每一个结点的入度了 这样我们使用一个for就可以知道哪一个结点的入度为零了 但是这样其实你也可以感觉到没有逆邻接表高效, 所以使用另外一种方式就是使用一个count数组来记录每一个结点当前的入度 这样在遍历的过程中一直修改count[]数组 并查看count数组中为零的可以输出 并将其中的值设置为一个标记 比如说-1 表示结点已经被删除 当count数组中的所有的count都为-1时也就可以跳出循环
2、如何删除顶点对应的出度
通过查看邻接矩阵,可以知道与待删除结点关联的结点,此时将对应的结点的count[]中的值减一即可
6.1.1、思路一代码实现
这里我使用了许多的日志 方便进行代码的调试 当时同样的很影响运行的时间
void TopologicalSort(AMGraph &G){
int count[G.vexnum]={0};
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++){
if(G.arcs[j][i])count[i]++;
}
}
int i=0; vector<char> V;
for(int x=0;x<G.vexnum;x++){
int i=0;
while(count[i++]!=0);
if(i>G.vexnum){
cout<<"存在环"<<endl;
return;
}
else{
count[i-1]=-1;
cout<<"将"<<G.vexs[i-1]<<"加入到容器中"<<endl;
V.push_back(G.vexs[i-1]);
for(int j=0;j<G.vexnum;j++){
if(G.arcs[i-1][j]){
count[j]-=1;
}
}
}
cout<<"此时的count数组是"<<endl;
for(int H=0;H<G.vexnum;H++){
cout<<count[H]<<" ";
}
cout<<endl;
}
for(int i=0;i<G.vexnum;i++){
cout<<V[i]<<" ";
}
cout<<endl;
}
6.1.2、思路一运行截图
使用用图就是上述的两个图
输入方式
第一个图的运行结果
第二个有环图的运行结果
6.2、思路二
使用栈(或者队列)的结构:首先计算所有顶点的初始入度,然后将count初值为零的顶点全部入栈,再将栈中的顶点依次出栈(出栈的同时修改其所有邻接顶点的入度,若为零则邻接顶点也入栈)。由于有n个顶点,因此出栈的操作应该循环n次;若在少于n次时栈已经空了,说明图中存在回路。这种方式较上种优化的地方是 不需要每一次都查找哪一个count==0 了
6.2.1、思路二代码
void TopologicalSort(AMGraph &G){
int count[G.vexnum]={0};
vector<char> result;
stack<int> S;
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++){
if(G.arcs[j][i])count[i]++;
}
}
for(int i=0;i<G.vexnum;i++){
if(0==count[i]){
cout<<"将"<<G.vexs[i]<<"放入栈中"<<endl;
S.push(i);
}
}
for(int h=0;h<G.vexnum;h++){
if(S.empty()){
break;
}
int cur=S.top();
S.pop();
cout<<"将"<<G.vexs[cur]<<"放入结果集中"<<endl;
result.push_back(G.vexs[cur]);
count[cur]=-1;
for(int j=0;j<G.vexnum;j++){
if(G.arcs[cur][j]){
count[j]-=1;
G.arcs[cur][j]=0;
}
if(count[j]==0){
cout<<"将"<<G.vexs[j]<<"放入栈集中"<<endl;
S.push(j);
}
}
}
if(result.size()==G.vexnum){
for(int i=0;i<G.vexnum;i++){
cout<<result[i]<<" ";
}
cout<<endl;
}
else cout<<"此时存在环"<<endl;
}
6.2.2、运行结果
用的测试图是上文中的三个测试图
第一个的输入方式
第二个的输入的方式与第一个一样 这里太长 就直接放结果吧
第三个测试图
七、可执行代码汇总
#include<bits/stdc++.h>
using namespace std;
#define MaxInt 32767
#define MVNum 100
typedef char VerTexType;
typedef int ArcType;
typedef struct{
VerTexType vexs[MVNum];
ArcType arcs[MVNum][MVNum];
int vexnum;
int arcnum;
}AMGraph;
void CreateAdjoin(AMGraph &G){
cout<<"请输入顶点数和边数"<<endl;
cin>>G.vexnum>>G.arcnum;
cout<<"请输入各个顶点名称"<<endl;
for(int i=0;i<G.vexnum;i++){
cin>>G.vexs[i];
}
for(int h=0;h<G.arcnum;h++){
char vex1;char vex2;
cout<<"请输入弧的两个顶点(类如从A指向B则输入AB)"<<endl;
cin>>vex1>>vex2;
int i=0;while(G.vexs[i++]!=vex1);
int j=0;while(G.vexs[j++]!=vex2);
if(i>G.vexnum||j>G.vexnum){
cout<<"你输入的结点值不正确"<<endl;
}
else{
cout<<"请输入此边对应的权重"<<endl;
cin>>G.arcs[i-1][j-1];
}
}
}
void TopologicalSort1(AMGraph &G){
int count[G.vexnum]={0};
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++){
if(G.arcs[j][i])count[i]++;
}
}
int i=0; vector<char> V;
for(int x=0;x<G.vexnum;x++){
int i=0;
while(count[i++]!=0);
if(i>G.vexnum){
cout<<"存在环"<<endl;
return;
}
else{
count[i-1]=-1;
cout<<"将"<<G.vexs[i-1]<<"加入到容器中"<<endl;
V.push_back(G.vexs[i-1]);
for(int j=0;j<G.vexnum;j++){
if(G.arcs[i-1][j]){
count[j]-=1;
}
}
}
cout<<"此时的count数组是"<<endl;
for(int H=0;H<G.vexnum;H++){
cout<<count[H]<<" ";
}
cout<<endl;
}
for(int i=0;i<G.vexnum;i++){
cout<<V[i]<<" ";
}
cout<<endl;
}
void TopologicalSort2(AMGraph &G){
int count[G.vexnum]={0};
vector<char> result;
stack<int> S;
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++){
if(G.arcs[j][i])count[i]++;
}
}
for(int i=0;i<G.vexnum;i++){
if(0==count[i]){
cout<<"将"<<G.vexs[i]<<"放入栈中"<<endl;
S.push(i);
}
}
for(int h=0;h<G.vexnum;h++){
if(S.empty()){
break;
}
int cur=S.top();
S.pop();
cout<<"将"<<G.vexs[cur]<<"放入结果集中"<<endl;
result.push_back(G.vexs[cur]);
count[cur]=-1;
for(int j=0;j<G.vexnum;j++){
if(G.arcs[cur][j]){
count[j]-=1;
G.arcs[cur][j]=0;
}
if(count[j]==0){
cout<<"将"<<G.vexs[j]<<"放入栈集中"<<endl;
S.push(j);
}
}
}
if(result.size()==G.vexnum){
for(int i=0;i<G.vexnum;i++){
cout<<result[i]<<" ";
}
cout<<endl;
}
else cout<<"此时存在环"<<endl;
}
int main(){
int choice;
AMGraph G;
CreateAdjoin(G);
cout<<"请选择方式1还是方式2"<<endl;
cin>>choice;
switch(choice){
case 1:{
TopologicalSort1(G);
break;
}
case 2:{
TopologicalSort2(G);
break;
}
default:{
cout<<"你输入的不正确"<<endl;
break;
}
}
}
总结
其实代码也还好,不难有兴趣的可以尝试一下 反正我确实是一遍打出来了 感觉不错点个赞呗,你都点赞是对作者莫大的鼓舞
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)