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