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


           对于比较稠密的图,通常采用邻接矩阵来表示,如下左图所示,无权的路径通常用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 << '	';
    			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	Know	Dist	Path	" << endl;
    	for (Vertex ver = 1; ver <= numVertex; ver++)
    	{
    		//按行打印无权最短路径的表
    		cout << Route[ver].ver << '	' << Route[ver].Know << '	' << Route[ver].Dist << '	' << Route[ver].Path << endl;
    	}
    	cout << endl;
    }
    
    //打印从start到某点end的路线/////////////////////////////////////////////
    void printRoute(route Route, Vertex start, Vertex end)
    {
    	if (end == start)
    		cout << start << '	';
    	else
    	{
    		printRoute(Route, start, Route[end].Path);
    		cout << Route[end].ver<<'	';
    	}
    }
    //加权最短路径算法表///////////////////////////////////////////////////////////
    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	Know	Dist	Path	" << endl;
    	for (Vertex ver = 1; ver <= adja->numVertex; ver++)
    	{
    		//按行打印加权最短路径的表
    		cout << ver << '	' << weightTable[ver].Know << '	' << weightTable[ver].Dist << '	' << 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<<"		";
    	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] << '	';
    
    		//找图的起点
    		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;
    }
    

      

      

  • 相关阅读:
    PHP抓取网络数据的6种常见方法
    Linux scp 使用详解
    php.ini的配置
    VS2013中,将Qt的GUI程序改为控制台程序
    Matlab 摄像机标定+畸变校正
    Camera 3D概念
    旋转矩阵
    #pragma pack()用法详解
    【Boost】boost库获取格式化时间
    C/C++读写csv文件
  • 原文地址:https://www.cnblogs.com/linear/p/6679135.html
Copyright © 2020-2023  润新知