圖的鄰接表、拓撲排序、無權最短路徑和加權最短路徑


       對於比較稠密的圖,通常采用鄰接矩陣來表示,如下左圖所示,無權的路徑通常用1表示兩點有連接,0表示沒有連接,若是加權圖,則把1改成權重就好,如下右圖。

                                      

     鄰接表結構用來表示稀疏的圖,圖的拓撲排序是指按每一個頂點的入度來對頂點進行排序,無權最短路徑指的是所有路徑的權重都是1,求某一點到另外一點的最短路徑

鄰接表(有向圖)

 

       下述程序用的圖及對應的鄰接表如下所示,其中加權圖9-20每條邊的方向還是按圖9-1的方向。

// Graph.cpp : 定義控制台應用程序的入口點。
//

#include "stdafx.h"
#include<iostream>
#include<queue>
using namespace std;
typedef int Vertex;
#define NotAVertex 0
#define INF 65536
//定義鏈表節點////////////////////////////////////
typedef struct TreeNode *Position;
struct TreeNode {
	int vertex;
	int weight;
	Position Next;
};

//定義鄰接表結構/////////////////////////////////////
typedef struct adjaceency_list *adjaceency;
struct adjaceency_list {
	int numVertex;      //大小
	Position* table;   //表地址
};

//鄰接表初始化函數////////////////////////////////////
adjaceency initAdjaceency_list(int numVertex)
{
	//申請一個鄰接表地址,給鄰接表賦初值
	adjaceency adja = (adjaceency)malloc(sizeof(adjaceency_list)); 
	adja->numVertex = numVertex;
	if (adja == NULL)
		cout << "Error";

	//申請一個table地址
	adja->table = (Position*)malloc(sizeof(Position)*(adja->numVertex+1));  
	if (adja->table == NULL)
		cout << "Error";

	//給鄰接表每一個表項添加一個鏈表表頭
	for (int i = 1; i <= adja->numVertex; i++) {
		adja->table[i] = (Position)malloc(sizeof(TreeNode));
		if (adja->table[i] == NULL)
			cout << "Error";
		else {
			adja->table[i]->vertex = i;
			adja->table[i]->weight = 0;       //給每個鄰接表項的鏈表頭的權重設為0
			adja->table[i]->Next = NULL;
		}
	}
	return adja;
}

//鄰接表的插入函數,制定一個頂點per_ver,把鄰接的頂點aft_ver插入其后//////////////////////////////////
void Insert(adjaceency adja, Vertex per_ver, Vertex aft_ver, int weight)
{
	//申請一個鏈表節點地址
	Position inser = (Position)malloc(sizeof(TreeNode));
	if (inser == NULL)
		cout << "Error";

	//從頭插入,修改指針
	inser->vertex = aft_ver;
	inser->weight = weight;                   //從per_ver指向aft_ver的權重
	inser->Next = adja->table[per_ver]->Next;
	adja->table[per_ver]->Next = inser;
}

//計算每個頂點入度的函數//////////////////////////////////////
void findIndegree(adjaceency adja)  
{
	//用表頭來存儲入度,先給每個表頭賦初值0
	for (int i = 1; i <= adja->numVertex; i++) adja->table[i]->vertex = 0;

	//從鄰接表表項1-N遍歷,每一項又由鏈表遍歷,鏈表遍歷時遇到某頂點就把某頂點對應的表頭加1
	for (int i = 1; i <= adja->numVertex; i++)
	{
		Position p = adja->table[i]->Next;
		while (p != NULL) {
			adja->table[p->vertex]->vertex++;
			p = p->Next;
		}
	}
}

//圖的拓撲排序///////////////////////////////////
int* Topsort(adjaceency adja)
{
	//用隊列來存放入度為0的頂點
	queue<Vertex> que;
	int counter = 0;

	//申請一個數組來存放頂點的次序,如TopNum[1]=6代表1號頂點排在第6位
	int *TopNum = (int*)malloc(sizeof(int)*(adja->numVertex + 1));
	for (int i = 1; i <= adja->numVertex; i++) TopNum[i] = 0;

	//先檢查初始入度為0的頂點並入隊
	for (Vertex ver = 1; ver <= adja->numVertex; ver++)
		if (adja->table[ver]->vertex == 0)
			que.push(ver);

	while (!que.empty())
	{
		//按出隊順序來決定頂點的拓撲排序
		Vertex v = que.front();
		que.pop();
		TopNum[v] = ++counter;
		
		//去掉該頂點后與該頂點鄰接的點的入度減一
		Position p = adja->table[v]->Next;
		while (p != NULL)
		{
			if (--adja->table[p->vertex]->vertex == 0)  //檢查p->vertex的入度是否為0,為0入隊
				que.push(p->vertex);
			p = p->Next;
		}
	}

	//檢查有沒有圈
	if (counter != adja->numVertex)
		cout << "Graph has a cycle";

	return TopNum;
}

//打印鄰接表//////////////////////////////////////////
void print(adjaceency adja)
{
	cout << "Vertex"<<endl;
	for (int i = 1; i <= adja->numVertex; i++)
	{
		Position p = adja->table[i];
		while (p != NULL) {
			cout << p->vertex << '\t';
			p = p->Next;
		}
		cout << endl;
	}
	cout << endl;
}

//定義用於無權最短路徑的表的表項結構//////////////////////////////////////
typedef struct routeTable *route;
struct routeTable
{
	Vertex ver;
	bool Know;
	int Dist;
	int Path;
};

//初始化用於無權最短路徑的表,並申請一片表空間///////////////////////////
route initRouteTable(int numVertex,Vertex start)
{
	route Route = (route)malloc(sizeof(routeTable)*(numVertex + 1));
	for (Vertex ver = 1; ver <= numVertex; ver++)
	{
		Route[ver].ver = ver;
		Route[ver].Know = false;
		Route[ver].Dist = INF;
		Route[ver].Path = 0;
	}
	//起始點的距離設為0;
	Route[start].Dist = 0;

	return Route;
}

//無權最短路徑算法1,時間復雜度較高////////////////////////////
void Unweighted(route Route, adjaceency adja)
{
	int CurrDist;

	for(CurrDist=0;CurrDist<adja->numVertex;CurrDist++)
		for(Vertex ver=1;ver<= adja->numVertex;ver++)
			if (!Route[ver].Know&&Route[ver].Dist == CurrDist)
			{
				//Route[ver].Dist == CurrDist,則找到指定距離的點
				Route[ver].Know = true;

				//把與該點鄰接的所有點在該點上的距離加1,以便於下次循環能找到這些點
				Position p = adja->table[ver]->Next;
				while (p)
				{
					if (Route[p->vertex].Dist == INF)
					{
						Route[p->vertex].Dist = CurrDist + 1;
						Route[p->vertex].Path = ver;
					}
					p = p->Next;
				}
			}
}

//無權最短路徑算法2,時間復雜度較低,用隊列實現////////////////////////////
void Unweighted2(route Route, adjaceency adja, Vertex start)
{
	//先把第一個點入隊
	queue<Vertex> que;
	que.push(start);

	while (!que.empty())
	{
		//按距離長短出隊,距離越晚出隊越晚
		Vertex ver = que.front();
		que.pop();
		Route[ver].Know = true;

		//從起點的臨接點找起,然后把鄰接點入隊,再找鄰接點的鄰接點,如此循環
		Position p = adja->table[ver]->Next;
		while (p)
		{
			if (Route[p->vertex].Dist == INF)
			{
				//設置鄰接點的表項,鄰接點的距離等於該點的距離加1。
				Route[p->vertex].Dist = Route[ver].Dist + 1;
				Route[p->vertex].Path = ver;

				//鄰接點入隊
				que.push(p->vertex);
			}
			//指向下一個鄰接點
			p = p->Next;
		}
	}
}

//打印無權最短路徑的表////////////////////////////////////////////
void printRouteTable(route Route, int numVertex)
{
	cout << "Vertex\tKnow\tDist\tPath\t" << endl;
	for (Vertex ver = 1; ver <= numVertex; ver++)
	{
		//按行打印無權最短路徑的表
		cout << Route[ver].ver << '\t' << Route[ver].Know << '\t' << Route[ver].Dist << '\t' << Route[ver].Path << endl;
	}
	cout << endl;
}

//打印從start到某點end的路線/////////////////////////////////////////////
void printRoute(route Route, Vertex start, Vertex end)
{
	if (end == start)
		cout << start << '\t';
	else
	{
		printRoute(Route, start, Route[end].Path);
		cout << Route[end].ver<<'\t';
	}
}
//加權最短路徑算法表///////////////////////////////////////////////////////////
typedef struct TableEntry *WeightTable;
struct TableEntry
{
	adjaceency adja;
	bool Know;
	int Dist;
	Vertex Path;
};

//加權最短路徑算法表初始化函數////////////////////////////////////////////////
WeightTable InitTable(Vertex start, adjaceency adja)
{
	WeightTable weightTable = (WeightTable)malloc(sizeof(TableEntry)*(adja->numVertex + 1));

	for (int i = 1; i <= adja->numVertex; i++)
	{
		weightTable[i].Know = false;
		weightTable[i].Dist = INF;
		weightTable[i].Path = NotAVertex;
	}
	weightTable[start].Dist = 0;       //只有起點的距離設為0,其他設為無窮

	return weightTable;
}

//尋找距離最近的未知節點的函數,這里用的方法是掃描整個表,看哪個距離最近,
//但時間復雜度較高,可以用有限隊列的deleteMin()來實現,時間復雜度較小。
Vertex smallest_dist_vertec(WeightTable weightTable, adjaceency adja)
{
	int temp=INF;
	Vertex minver=NotAVertex;
	for(int ver=1;ver<=adja->numVertex;ver++)
		if (!weightTable[ver].Know&&temp > weightTable[ver].Dist)
		{
			temp = weightTable[ver].Dist;
			minver = ver;
		}
	return minver;
}

//計算最短加權的函數////////////////////////////////////////////////////
void Dijkstra(WeightTable weightTable, adjaceency adja)
{
	for (;;)
	{
		//循環結束的條件沒有未知的點
		Vertex smallest_ver = smallest_dist_vertec(weightTable, adja);
		if (smallest_ver == NotAVertex)
			break;
		weightTable[smallest_ver].Know = true;

		//得到未知的最近的點V后,令p逐一指向V的所有鄰接點W,更新所有鄰接點的距離。
		//這里要注意的是由於是從最近節點一層一層往下找的,所以每一個w的距離最多只更新一次
		Position p = adja->table[smallest_ver]->Next;
		while (p)
		{
			if (!weightTable[p->vertex].Know)
				if (weightTable[smallest_ver].Dist + p->weight < weightTable[p->vertex].Dist)
				{
					weightTable[p->vertex].Dist = weightTable[smallest_ver].Dist + p->weight;
					weightTable[p->vertex].Path = smallest_ver;
				}
			p = p->Next;
		}
	}
}

//打印最短加權路徑表的函數//////////////////////////////////////////
void printWeightTable(WeightTable weightTable, adjaceency adja)
{
	cout << "Vertex\tKnow\tDist\tPath\t" << endl;
	for (Vertex ver = 1; ver <= adja->numVertex; ver++)
	{
		//按行打印加權最短路徑的表
		cout << ver << '\t' << weightTable[ver].Know << '\t' << weightTable[ver].Dist << '\t' << weightTable[ver].Path << endl;
	}
	cout << endl;
}
int main()
{
	//初始化鄰接表////////////////////////////////////////
	adjaceency adja = initAdjaceency_list(7);
	Insert(adja, 1, 3, 4); Insert(adja, 1, 4, 1); Insert(adja, 1, 2, 2);
	Insert(adja, 2, 5, 10); Insert(adja, 2, 4, 3);
	Insert(adja, 3, 6, 5);
	Insert(adja, 4, 3, 2); Insert(adja, 4, 7, 4); Insert(adja, 4, 6, 8);
	Insert(adja, 5, 7, 6); Insert(adja, 5, 4, 2);
	Insert(adja, 7, 6, 1);
	print(adja);

	//檢查每一個頂點的入度////////////////////////////////////
	findIndegree(adja);
	for (int i = 1; i <= adja->numVertex; i++)
	   cout << i<<"入度:"<< adja->table[i]->vertex<<"\t\t";
	cout << endl;

	//按拓撲排序打印輸出//////////////////////////////////////
	int* TopNum = Topsort(adja);
	for (Vertex ver = 1; ver <= adja->numVertex; ver++)
		adja->table[ver]->vertex = ver;
	int start;
	for (Vertex ver = 1; ver <= adja->numVertex; ver++)
	{
		cout << TopNum[ver] << '\t';

		//找圖的起點
		if (TopNum[ver] == 1)
			start = ver;
	}
	cout << endl;

	//求無權最短路徑表/////////////////////////////////////
	route Route = initRouteTable(adja->numVertex, start);
	printRouteTable(Route, adja->numVertex);
	//Unweighted(Route, adja);
	Unweighted2(Route, adja, start);
	printRouteTable(Route, adja->numVertex);
	printRoute(Route, start, adja->numVertex);
	cout << endl;
	
	//求加權最短路徑表/////////////////////////////////////
	WeightTable weightTable = InitTable(start, adja);
	Dijkstra(weightTable, adja);
	printWeightTable(weightTable, adja);
	while (1);
    return 0;
}

  

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM