拓撲排序定義:
就是將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");
}