拓扑排序定义:
就是将AOV-网中所有顶点排成一个线性序列,该序列满足:若在AOV-网中顶点A到顶点B有一条路径,则在该线性序列中的顶点A必定在顶点B之前。
如图
则该图有两个拓扑有序序列:
一,A B C D E G I J K F L H
二,I J K F A L D B C E G H
一,排序大致过程(仅讲上半部分图,上下一样)
遍历过程:
-
如图(a),先遍历入度为0的顶点,即A,然后去掉以A为弧头的弧,所以对应弧尾的入度减一,当入度变成0, - 则入栈,此时因为DB原入度都为1,所以减少后入度为零,所以入栈,因为C原来入度为2,减一后为一,所以不入栈。
- 此时栈内元素为D B;
- 如图(b)出栈栈顶元素,即B,然后去掉以B为弧头的弧,所以对应弧尾的入度减一,当入度变成0,
- 则入栈此时因为C原入度都为1,所以减少后入度为零,所以入栈。
- 此时栈内元素为D C;
-
如图(c),出栈栈顶元素,即C,然后去掉以C为弧头的弧,所以对应弧尾的入度减一,当入度变成0,则入栈
- 此时因为EG原入度都为2,所以减少后入度都为一,所以都不入栈
- 此时栈内元素为D;
- 图(d),然后出栈D。。。。。。。。。(都按照上面的过程)
看懂上面的图,大致就了解了大致过程
二,实现方法
这一种求最短路径的方法除了需要AOV-网所需的结构体和函数之外,又入了栈和两个数组:
int Indegree[pointMax]; //保存每个顶点的入度数
int topo[pointMax]; //存出度的信息,最后遍历即按照该数组。即可得到结果
eg:如最上面图中,A点的Indegree[ ]为0;B点的Indegree[ ]为1;
本程序中图的储存方法类似于十字链表
即每一个顶底都有两个链表,一个该顶点的入度链表,一个该顶点的出度链表
VtNode *nextVt; //入度链表下一个结点
int peace; //入度下一顶点的值
VtNode *nextVtt; //出度链表下一个结点
int peaceE; //出度下一顶点的值
定义两个目的是:
首先需要将每一个顶点的入度数存入 Indegree数组中,所以需要用入度链表。
然后找到某一顶点后,需要遍历以该顶点为弧头的所有弧,所以此时还需要一个出度链表。
(这部分可以看我以前关于十字链表编写方法的博客)
- 先构造AOV-网,该过程类似于图的十字链表法类似。
- 先将上面新加入的数组进行赋值。
- 从入度为零的点进行遍历周围弧,入度为零则入栈。
- 然后再出栈,入栈,一直遍历完所有顶点。(上面的图已经说的特别清楚了,可以看上面图)
(eg:比如A入度为0,以A点为弧头的弧有AB AC AD,则此时B C D 的入度都减一,当减到零时,入栈)。
直到程序结束。
三,代码
(几乎每一部都有相应注释,可以带着注释理解代码)
里面有一些显示的地方,代码运行会看到
#include<iostream>
using namespace std;
#define pointMax 100
struct VtNode //权值信息
{
VtNode *nextVt; //入度链表下一个结点
int peace; //入度下一顶点的值
VtNode *nextVtt; //出度链表下一个结点
int peaceE; //出度下一顶点的值
int len;
};
struct PoNode //顶点信息
{
char data;
VtNode *firstPo; //入度
VtNode *Out; //出度
};
struct ATgroup
{
PoNode vertices[pointMax]; //每一个verticse代表一个顶点
int point, vert; //point顶点数,vert弧数
};
struct Node
{
int data;
Node *next;
};
struct SqStack //栈
{
Node *base; //栈底
Node *top; //栈顶
int data;
};
void Push(SqStack &S, int i) //入栈
{
Node *m = new Node;
m->data = i;
m->next = S.top; //入栈过程
S.top = m;
}
int Pop(SqStack &S) //出栈
{
int n = S.top->data;
S.top = S.top->next; //出栈过程
return n;
}
int ATlocate(ATgroup A, char x) //找到位置
{
for (int i = 0; i < A.point; i++) //依次遍历点的信息
{
if (A.vertices[i].data == x) //找到x的位置
{
return i;
}
}
}
void show(ATgroup &A) //显示当前所有点入度出度的顶点
{
cout << endl;
for (int i = 0; i < A.point; i++)
{
cout << i;
if (A.vertices[i].firstPo != NULL) //入度位置
{
cout << " " << A.vertices[i].firstPo->peace << " ";
}
else
cout << " -1" << " ";
if (A.vertices[i].Out != NULL) //出度位置
{
cout << A.vertices[i].Out->peaceE << endl;
}
else
cout << " -1" << endl;
}
}
void CreatAT(ATgroup &A)
{
cout << "输入邻接矩阵顶点数:";
cin >> A.point;
cout << "输入邻接矩阵边数:";
cin >> A.vert;
getchar();
char q[100];
cout << "输入顶点信息:";
gets_s(q);
for (int i = 0; i < A.point; i++)
{
A.vertices[i].data = q[i]; //输入顶点值
A.vertices[i].firstPo = NULL; //初始化头结点为空
A.vertices[i].Out = NULL;
}
char v1, v2; int m, n; int len;
for (int i = 0; i < A.vert; i++) //输入各边,构造邻接表
{
cout << "输入第" << i << "条边的依附的两个顶点:";
cin >> v1 >> v2;
m = ATlocate(A, v1); //确定位置
n = ATlocate(A, v2);
//第一个
VtNode *p1 = new VtNode;
VtNode *p2 = new VtNode;
p1->peace = m; //入度
p1->nextVt = A.vertices[n].firstPo;
A.vertices[n].firstPo = p1;
p2->peaceE = n; //出度
p2->nextVtt = A.vertices[m].Out;
A.vertices[m].Out = p2;
}
show(A);
}
void FindIn(ATgroup *A, int *in) //统计所有点的入度数并存入到in数组中
{
int n = 0;
for (int i = 0; i < A->point; i++) //遍历每一个点
{
VtNode *p = new VtNode;
p = A->vertices[i].firstPo;
while (p != NULL) //将入度链表进行遍历
{
n++;
p = p->nextVt; //下一结点
}
in[i] = n; //存入in数组
n = 0;
}
}
void SHOW(int *a, ATgroup *A) //显示当前所有顶点入度数量还有几个
{
for (int i = 0; i < A->point; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
int M[pointMax] = { 0 };
void TPsort(ATgroup *A, SqStack &S) //拓扑排序过程
{
int Indegree[pointMax];
FindIn(A, Indegree); //将入度赋值给数组
for (int i = 0; i < A->point; i++)
{
if (Indegree[i] == 0) //将所有入度等于0的入栈
{
cout << "Push=" << i << endl;
Push(S, i);
}
}
int m = 0; //统计入度的顶点数
int n, k;
int topo[pointMax]; //存出度的信息,最后遍历即按照该数组。
while (S.base != S.top) //判断是否遍历完
{
cout << endl;
n = Pop(S); //栈顶出栈
cout << "Pop=" << n << endl;
topo[m] = n; //存入topo
m++;
VtNode* p = new VtNode;
p = A->vertices[n].Out; //出度链表的结点
while (p != NULL) //遍历出度链表
{
k = p->peaceE; //某结点的位置
cout << "出度下一结点k=" << k << endl;
Indegree[k]--; //将该结点顶点位置入度减一
//SHOW(Indegree, A); //显示当前所有点的入度
if (Indegree[k] == 0) //当等于0时,入栈
{
cout << "Push=" << k << endl;
Push(S, k);
}
p = p->nextVtt; //下一个
}
}
for (int i = 0; i < m; i++) //将顶点按照拓扑排序输出
{
cout << A->vertices[topo[i]].data;
}
}
int main()
{
ATgroup *A = new ATgroup;
SqStack *S = new SqStack;
S->top = S->base;
S->data = pointMax;
CreatAT(*A);
TPsort(A, *S);
system("pause");
}