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