图的邻接表、拓扑排序、无权最短路径和加权最短路径

       对于比较稠密的图,通常采用邻接矩阵来表示,如下左图所示,无权的路径通常用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;
}

  

 

  

posted @ 2017-04-07 17:12  lineaar  阅读(2293)  评论(0编辑  收藏  举报