拓撲排序代碼實現詳解

拓撲排序定義:

就是將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數組中,所以需要用入度鏈表。

然後找到某一頂點後,需要遍歷以該頂點爲弧頭的所有弧,所以此時還需要一個出度鏈表。

(這部分可以看我以前關於十字鏈表編寫方法的博客)

 

 

  1. 先構造AOV-網,該過程類似於圖的十字鏈表法類似。
  2. 先將上面新加入的數組進行賦值。
  3. 從入度爲零的點進行遍歷周圍弧,入度爲零則入棧。
  4.  然後再出棧,入棧,一直遍歷完所有頂點。(上面的圖已經說的特別清楚了,可以看上面圖)

     (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");
}

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章