第 6 章 图
6.1 图的顺序存储
图的顺序存储结构代码如下:
#include <stdio.h>
#define MAX_VERtEX_NUM 20 //顶点的最大个数
#define VRType int //表示顶点之间的关系的变量类型
#define InfoType char //存储弧或者边额外信息的指针变量类型
#define VertexType int //图中顶点的数据类型
typedef enum { DG, DN, UDG, UDN }GraphKind; //枚举图的 4 种类型
typedef struct {
VRType adj; //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。
InfoType* info; //弧或边额外含有的信息指针
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];
typedef struct {
VertexType vexs[MAX_VERtEX_NUM]; //存储图中顶点数据
AdjMatrix arcs; //二维数组,记录顶点之间的关系
int vexnum, arcnum; //记录图的顶点数和弧(边)数
GraphKind kind; //记录图的种类
}MGraph;
//根据顶点本身数据,判断出顶点在二维数组中的位置
int LocateVex(MGraph* G, VertexType v) {
int i = 0;
//遍历一维数组,找到变量v
for (; i < G->vexnum; i++) {
if (G->vexs[i] == v) {
break;
}
}
//如果找不到,输出提示语句,返回-1
if (i == G->vexnum) {
printf("no such vertex.\n");
return -1;
}
return i;
}
//构造有向图
void CreateDG(MGraph* G) {
int i, j;
//输入图含有的顶点数和弧的个数
scanf("%d,%d", &(G->vexnum), &(G->arcnum));
//依次输入顶点本身的数据
for (i = 0; i < G->vexnum; i++) {
scanf("%d", &(G->vexs[i]));
}
//初始化二维矩阵,全部归0,指针指向NULL
for (i = 0; i < G->vexnum; i++) {
for (j = 0; j < G->vexnum; j++) {
G->arcs[i][j].adj = 0;
G->arcs[i][j].info = NULL;
}
}
//在二维数组中添加弧的数据
for (i = 0; i < G->arcnum; i++) {
int v1, v2;
int n, m;
//输入弧头和弧尾
scanf("%d,%d", &v1, &v2);
//确定顶点位置
n = LocateVex(G, v1);
m = LocateVex(G, v2);
//排除错误数据
if (m == -1 || n == -1) {
printf("no this vertex\n");
return;
}
//将正确的弧的数据加入二维数组
G->arcs[n][m].adj = 1;
}
}
//构造无向图
void CreateDN(MGraph* G) {
int i, j;
scanf("%d,%d", &(G->vexnum), &(G->arcnum));
for (i = 0; i < G->vexnum; i++) {
scanf("%d", &(G->vexs[i]));
}
for (i = 0; i < G->vexnum; i++) {
for (j = 0; j < G->vexnum; j++) {
G->arcs[i][j].adj = 0;
G->arcs[i][j].info = NULL;
}
}
for (i = 0; i < G->arcnum; i++) {
int v1, v2;
int n, m;
scanf("%d,%d", &v1, &v2);
n = LocateVex(G, v1);
m = LocateVex(G, v2);
if (m == -1 || n == -1) {
printf("no this vertex\n");
return;
}
G->arcs[n][m].adj = 1;
G->arcs[m][n].adj = 1;//无向图的二阶矩阵沿主对角线对称
}
}
//构造有向网,和有向图不同的是二阶矩阵中存储的是权值。
void CreateUDG(MGraph* G) {
int i, j;
scanf("%d,%d", &(G->vexnum), &(G->arcnum));
for (i = 0; i < G->vexnum; i++) {
scanf("%d", &(G->vexs[i]));
}
for (i = 0; i < G->vexnum; i++) {
for (j = 0; j < G->vexnum; j++) {
G->arcs[i][j].adj = 0;
G->arcs[i][j].info = NULL;
}
}
for (i = 0; i < G->arcnum; i++) {
int v1, v2, w;
int n, m;
scanf("%d,%d,%d", &v1, &v2, &w);
n = LocateVex(G, v1);
m = LocateVex(G, v2);
if (m == -1 || n == -1) {
printf("no this vertex\n");
return;
}
G->arcs[n][m].adj = w;
}
}
//构造无向网。和无向图唯一的区别就是二阶矩阵中存储的是权值
void CreateUDN(MGraph* G) {
int i, j;
scanf("%d,%d", &(G->vexnum), &(G->arcnum));
for (i = 0; i < G->vexnum; i++) {
scanf("%d", &(G->vexs[i]));
}
for (i = 0; i < G->vexnum; i++) {
for (j = 0; j < G->vexnum; j++) {
G->arcs[i][j].adj = 0;
G->arcs[i][j].info = NULL;
}
}
for (i = 0; i < G->arcnum; i++) {
int v1, v2, w;
int m, n;
scanf("%d,%d,%d", &v1, &v2, &w);
m = LocateVex(G, v1);
n = LocateVex(G, v2);
if (m == -1 || n == -1) {
printf("no this vertex\n");
return;
}
G->arcs[n][m].adj = w;
G->arcs[m][n].adj = w;//矩阵对称
}
}
void CreateGraph(MGraph* G) {
//选择图的类型
scanf("%d", &(G->kind));
//根据所选类型,调用不同的函数实现构造图的功能
switch (G->kind) {
case DG:
return CreateDG(G);
break;
case DN:
return CreateDN(G);
break;
case UDG:
return CreateUDG(G);
break;
case UDN:
return CreateUDN(G);
break;
default:
break;
}
}
//输出函数
void PrintGrapth(MGraph G)
{
int i, j;
for (i = 0; i < G.vexnum; i++)
{
for (j = 0; j < G.vexnum; j++)
{
printf("%d ", G.arcs[i][j].adj);
}
printf("\n");
}
}
int main() {
MGraph G;//建立一个图的变量
CreateGraph(&G);//调用创建函数,传入地址参数
PrintGrapth(G);//输出图的二阶矩阵
return 0;
}
6.2 邻接表
邻接表(Adjacency List)是图的一种链式存储结构,既可以存储无向图(网),也可以存储有向图(网)。
邻接表存储图的核心思想是:将图中的所有顶点存储到顺序表中(也可以是链表),同时为各个顶点配备一个单链表,用来存储和当前顶点有直接关联的边或者弧(边的一端是该顶点或者弧的弧尾是该顶点)。
邻接表的实现代码如下:
#define MAX_VERTEX_NUM 20//图中顶点的最大数量
#define VertexType int//图中顶点的类型
#define InfoType int*//图中弧或者边包含的信息的类型
typedef struct ArcNode{
int adjvex;//存储边或弧,即另一端顶点在数组中的下标
struct ArcNode * nextarc;//指向下一个结点
InfoType info;//记录边或弧的其它信息
}ArcNode;
typedef struct VNode{
VertexType data;//顶点的数据域
ArcNode * firstarc;//指向下一个结点
}VNode,AdjList[MAX_VERTEX_NUM];//存储各链表首元结点的数组
typedef struct {
AdjList vertices;//存储图的邻接表
int vexnum,arcnum;//记录图中顶点数以及边或弧数
int kind;//记录图的种类
}ALGraph;
使用邻接表存储有向图的代码如下:
#include<stdio.h>
#include<stdlib.h>
#define MAX_VERTEX_NUM 20//最大顶点个数
#define VertexType char//图中顶点的类型
typedef struct ArcNode {
int adjvex;//存储弧,即另一端顶点在数组中的下标
struct ArcNode* nextarc;//指向下一个结点
}ArcNode;
typedef struct VNode {
VertexType data;//顶点的数据域
ArcNode* firstarc;//指向下一个结点
}VNode, AdjList[MAX_VERTEX_NUM];//存储各链表首元结点的数组
typedef struct {
AdjList vertices; //存储图的邻接表
int vexnum, arcnum;//图中顶点数以及弧数
}ALGraph;
void CreateGraph(ALGraph * graph) {
int i, j;
char VA, VB;
ArcNode* node = NULL;
printf("输入顶点的数目:\n");
scanf("%d", &(graph->vexnum));
printf("输入弧的数目:\n");
scanf("%d", &(graph->arcnum));
scanf("%*[^\n]"); scanf("%*c");
printf("输入各个顶点的值:\n");
for (i = 0; i < graph->vexnum; i++) {
scanf("%c", &(graph->vertices[i].data));
getchar();
graph->vertices[i].firstarc = NULL;
}
//输入弧的信息,并为弧建立结点,链接到对应的链表上
for (i = 0; i < graph->arcnum; i++) {
printf("输入弧(a b 表示弧 a->b):\n");
scanf("%c %c", &VA, &VB);
getchar();
node = (ArcNode*)malloc(sizeof(ArcNode));
node->adjvex = '#';
node->nextarc = NULL;
//存储弧另一端顶点所在顺序表中的下标
for (j = 0; j < graph->vexnum; j++) {
if (VB == graph->vertices[j].data) {
node->adjvex = j;
break;
}
}
//如果未在顺序表中找到另一端顶点,则构建图失败
if (node->adjvex == '#') {
printf("弧信息输入有误\n");
exit(0);
}
//将结点添加到对应的链表中
for (j = 0; j < graph->vexnum; j++) {
if (VA == graph->vertices[j].data) {
//将 node 结点以头插法的方式添加到相应链表中
node->nextarc = graph->vertices[j].firstarc;
graph->vertices[j].firstarc = node;
break;
}
}
if (j == graph->vexnum) {
printf("弧信息输入有误\n");
exit(0);
}
}
}
//计算某个顶点的入度
int InDegree(ALGraph graph, char V) {
int i, j, index = -1;
int count = 0;
//找到 V 在顺序表中的下标
for (j = 0; j < graph.vexnum; j++) {
if (V == graph.vertices[j].data) {
index = j;
break;
}
}
if (index == -1) {
return -1;
}
//遍历每个单链表,找到存储 V 下标的结点,并计数
for (j = 0; j < graph.vexnum; j++) {
ArcNode* p = graph.vertices[j].firstarc;
while (p) {
if (p->adjvex == index) {
count++;
}
p = p->nextarc;
}
}
return count;
}
//计算某个顶点的出度
int OutDegree(ALGraph graph, char V) {
int j;
int count = 0;
for (j = 0; j < graph.vexnum; j++) {
if (V == graph.vertices[j].data) {
ArcNode* p = graph.vertices[j].firstarc;
while (p) {
count++;
p = p->nextarc;
}
break;
}
}
//如果查找失败,返回 -1 表示计算失败
if (j == graph.vexnum) {
return -1;
}
return count;
}
int main(void) {
ALGraph graph;
CreateGraph(&graph);
if (OutDegree(graph, 'A') != -1) {
printf("%c 顶点的出度为 %d\n", 'A', OutDegree(graph, 'A'));
}
if (InDegree(graph, 'A') != -1) {
printf("%c 顶点的入度为 %d", 'A', InDegree(graph, 'A'));
}
return 0;
}
6.3 十字链表
十字链表(Orthogonal List)是一种专门存储有向图(网)的结构,它的核心思想是:将图中的所有顶点存储到顺序表(也可以是链表)中,同时为每个顶点配备两个链表,一个链表记录以当前顶点为弧头的弧,另一个链表记录以当前顶点为弧尾的弧。
构建图的十字链表结构的代码如下:
#define MAX_VERTEX_NUM 20 //图中顶点的最大数量
#define InfoType int* //表示弧额外信息的数据类型
#define VertexType char //图中顶点的数据类型
//表示链表中存储弧的结点
typedef struct ArcBox {
int tailvex, headvex; //弧尾、弧头对应顶点在顺序表中的位置下标
struct ArcBox* hlik, * tlink; //hlik指向下一个以当前顶点为弧头的弧结点;
//tlink 指向下一个以当前顶点为弧尾的弧结点;
//InfoType info; //存储弧相关信息的指针
}ArcBox;
//表示顺序表中的各个顶点
typedef struct VexNode {
VertexType data; //顶点的数据域
ArcBox* firstin, * firstout; //指向以该顶点为弧头和弧尾的链表首个结点
}VexNode;
//表示十字链表存储结构
typedef struct {
VexNode xlist[MAX_VERTEX_NUM]; //存储顶点的顺序表
int vexnum, arcnum; //记录图的顶点数和弧数
}OLGraph;
使用十字链表结构存储此图的代码如下:
#include<stdio.h>
#define MAX_VERTEX_NUM 20 //图中顶点的最大数量
#define InfoType int* //表示弧额外信息的数据类型
#define VertexType char //图中顶点的数据类型
//表示链表中存储弧的结点
typedef struct ArcBox {
int tailvex, headvex; //弧尾、弧头对应顶点在顺序表中的位置下标
struct ArcBox* hlik, * tlink; //hlik指向下一个以当前顶点为弧头的弧结点;
//tlink 指向下一个以当前顶点为弧尾的弧结点;
//InfoType info; //存储弧相关信息的指针
}ArcBox;
//表示顺序表中的各个顶点
typedef struct VexNode {
VertexType data; //顶点的数据域
ArcBox* firstin, * firstout; //指向以该顶点为弧头和弧尾的链表首个结点
}VexNode;
//表示十字链表存储结构
typedef struct {
VexNode xlist[MAX_VERTEX_NUM]; //存储顶点的顺序表
int vexnum, arcnum; //记录图的顶点数和弧数
}OLGraph;
int LocateVex(OLGraph* G, VertexType v) {
int i;
//遍历一维数组,找到变量v
for (i = 0; i < G->vexnum; i++) {
if (G->xlist[i].data == v) {
break;
}
}
//如果找不到,输出提示语句,返回 -1
if (i > G->vexnum) {
printf("no such vertex.\n");
return -1;
}
return i;
}
//构建十字链表存储结构
void CreateDG(OLGraph* G) {
int i, j, k;
VertexType v1, v2;
ArcBox* p = NULL;
//输入有向图的顶点数和弧数
scanf("%d %d", &(G->vexnum), &(G->arcnum));
getchar();
//使用一维数组存储顶点数据,初始化指针域为NULL
for (i = 0; i < G->vexnum; i++) {
scanf("%c", &(G->xlist[i].data));
getchar();
G->xlist[i].firstin = NULL;
G->xlist[i].firstout = NULL;
}
//存储图中的所有弧
for (k = 0; k < G->arcnum; k++) {
scanf("%c %c", &v1, &v2);
getchar();
//确定v1、v2在数组中的位置下标
i = LocateVex(G, v1);
j = LocateVex(G, v2);
//建立弧的结点
p = (ArcBox*)malloc(sizeof(ArcBox));
p->tailvex = i;
p->headvex = j;
//采用头插法插入新的p结点
p->hlik = G->xlist[j].firstin;
p->tlink = G->xlist[i].firstout;
G->xlist[j].firstin = G->xlist[i].firstout = p;
}
}
//计算某顶点的入度
int indegree(OLGraph* G, VertexType x) {
int i;
int num = 0;
//遍历整个顺序表
for (i = 0; i < G->vexnum; i++) {
//找到目标顶点
if (x == G->xlist[i].data) {
//从该顶点的 firstin 指针所指的结点开始遍历
ArcBox* p = G->xlist[i].firstin;
while (p)
{
num++;
//遍历 hlink 指针指向的下一个结点
p = p->hlik;
}
break;
}
}
if (i == G->vexnum) {
printf("图中没有指定顶点\n");
return -1;
}
return num;
}
//计算某顶点的出度
int outdegree(OLGraph* G, VertexType x) {
int i;
int num = 0;
//遍历整个顺序表
for (i = 0; i < G->vexnum; i++) {
//找到目标顶点
if (x == G->xlist[i].data) {
//从该顶点的 firstout 指针所指的结点开始遍历
ArcBox* p = G->xlist[i].firstout;
while (p)
{
num++;
//遍历 tlink 指针指向的下一个结点
p = p->tlink;
}
break;
}
}
if (i == G->vexnum) {
printf("图中没有指定顶点\n");
return -1;
}
return num;
}
//删除十字链表结构
//每个顶点配备两个链表,选定一个链表(比如 firstout 所指链表),删除每个顶点中 firstout 所指链表上的结点
void DeleteDG(OLGraph* G) {
int i;
ArcBox* p = NULL, * del = NULL;
for (i = 0; i < G->vexnum; i++) {
p = G->xlist[i].firstout;
while (p) {
del = p;
p = p->tlink;
free(del);
}
//将第 i 个位置的两个指针全部置为 NULL,能有效避免出现野指针
G->xlist[i].firstout = NULL;
G->xlist[i].firstin = NULL;
}
}
int main() {
OLGraph G;
CreateDG(&G);
printf("A 顶点的入度为 %d\n", indegree(&G, 'A'));
printf("A 顶点的出度为 %d\n", outdegree(&G, 'A'));
DeleteDG(&G);
return 0;
}
6.4 深度优先搜索
深度优先搜索(Depth First Search)简称深搜或者 DFS,是遍历图存储结构的一种算法,既适用于无向图(网),也适用于有向图(网)。
深度优先搜索算法的具体实现,所谓深度优先搜索,就是从图中的某个顶点出发,不停的寻找相邻的、尚未访问的顶点:
(1)如果找到多个,则任选一个顶点,然后继续从该顶点出发。
(2)如果一个都没有找到,则回退到之前访问过的顶点,看看是否有漏掉的。
实现图的顺序存储结构的深度优先搜索算法的代码如下 :
#include <stdio.h>
#define MAX_VERtEX_NUM 20 //顶点的最大个数
#define VRType int //表示顶点之间关系的类型, 0 表示不相邻,1 表示相邻
#define VertexType int //图中顶点的数据类型
#define States int
typedef enum { false, true }bool; //定义bool型常量
bool visited[MAX_VERtEX_NUM]; //设置全局数组,标记图中的各个顶点是否被访问过
typedef struct {
VRType adj; //用 1 或 0 表示是否相邻;
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];
typedef struct {
VertexType vexs[MAX_VERtEX_NUM]; //存储图中的顶点
AdjMatrix arcs; //二维数组,记录顶点之间的关系
int vexnum, arcnum; //记录图的顶点数和弧(边)数
}MGraph;
//根据顶点数据,返回顶点在二维数组中的位置下标
int LocateVex(MGraph* G, VertexType v) {
int i = 0;
//遍历一维数组,找到变量v
for (; i < G->vexnum; i++) {
if (G->vexs[i] == v) {
break;
}
}
//如果找不到,输出提示语句,返回-1
if (i > G->vexnum) {
printf("no such vertex.\n");
return -1;
}
return i;
}
//构造无向图
States CreateDN(MGraph* G) {
int i, j, n, m;
int v1, v2;
scanf("%d,%d", &(G->vexnum), &(G->arcnum));
for (i = 0; i < G->vexnum; i++) {
scanf("%d", &(G->vexs[i]));
}
for (i = 0; i < G->vexnum; i++) {
for (j = 0; j < G->vexnum; j++) {
G->arcs[i][j].adj = 0;
}
}
for (i = 0; i < G->arcnum; i++) {
scanf("%d,%d", &v1, &v2);
n = LocateVex(G, v1);
m = LocateVex(G, v2);
if (m == -1 || n == -1) {
printf("no this vertex\n");
return -1;
}
G->arcs[n][m].adj = 1;
G->arcs[m][n].adj = 1;//无向图的二阶矩阵沿主对角线对称
}
return 1;
}
int FirstAdjVex(MGraph G, int v)
{
int i;
//对于数组下标 v 处的顶点,找到第一个和它相邻的顶点,并返回该顶点的数组下标
for (i = 0; i < G.vexnum; i++) {
if (G.arcs[v][i].adj) {
return i;
}
}
return -1;
}
int NextAdjVex(MGraph G, int v, int w)
{
int i;
//对于数组下标 v 处的顶点,从 w 位置开始继续查找和它相邻的顶点,并返回该顶点的数组下标
for (i = w + 1; i < G.vexnum; i++) {
if (G.arcs[v][i].adj) {
return i;
}
}
return -1;
}
void DFS(MGraph G, int v) {
int w;
printf("%d ", G.vexs[v]); //访问第 v 个顶点
visited[v] = true; //将第 v 个顶点的标记设置为true
//对于与第 v 个顶点相邻的其它顶点,逐个调用深度优先搜索算法
for (w = FirstAdjVex(G, v); w >= 0; w = NextAdjVex(G, v, w)) {
//如果该顶点的标记为false,证明尚未被访问,就调用深度优先搜索算法
if (!visited[w]) {
DFS(G, w);
}
}
}
//深度优先搜索
void DFSTraverse(MGraph G) {
int v;
//visit数组记录各个顶点是否已经访问过,全部初始化为 false
for (v = 0; v < G.vexnum; ++v) {
visited[v] = false;
}
//对于每个标记为false的顶点,都调用一次深度优先搜索算法
for (v = 0; v < G.vexnum; v++) {
//如果该顶点的标记位为false,就调用深度优先搜索算法
if (!visited[v]) {
DFS(G, v);
}
}
}
int main() {
MGraph G; //建立一个图
CreateDN(&G); //初始化图
DFSTraverse(G);//深度优先搜索图
return 0;
}
6.5 广度优先搜索
广度优先搜索(Breadth First Search)简称广搜或者 BFS,是遍历图存储结构的一种算法,既适用于无向图(网),也适用于有向图(网)。
广度优先搜索算法的具体实现如下:
(1)所谓广度优先搜索,就是从图中的某个顶点出发,寻找紧邻的、尚未访问的顶点,找到多少就访问多少,然后分别从找到的这些顶点出发,继续寻找紧邻的、尚未访问的顶点。
(2)当从某个顶点出发,所有和它连通的顶点都访问完之后,广度优先搜索算法会重新选择一个尚未访问的顶点(非连通图中就存在这样的顶点),继续以同样的思路寻找未访问的其它顶点。直到图中所有顶点都被访问,广度优先搜索算法才会结束执行。
实现图的顺序存储结构的广度优先搜索算法的代码如下 :
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERtEX_NUM 20 //顶点的最大数量
#define VRType int //表示顶点之间关系的数据类型
#define VertexType int //顶点的数据类型
typedef enum { false, true }bool; //定义bool型常量
bool visited[MAX_VERtEX_NUM]; //设置全局数组,记录每个顶点是否被访问过
//队列链表中的结点类型
typedef struct Queue {
VertexType data;
struct Queue* next;
}Queue;
typedef struct {
VRType adj; //用 0 表示不相邻,用 1 表示相邻
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];
typedef struct {
VertexType vexs[MAX_VERtEX_NUM]; //存储图中的顶点
AdjMatrix arcs; //二维数组,记录顶点之间的关系
int vexnum, arcnum; //记录图的顶点数和弧(边)数
}MGraph;
//判断 v 顶点在二维数组中的位置
int LocateVex(MGraph* G, VertexType v) {
int i;
//遍历一维数组,找到变量v
for (i = 0; i < G->vexnum; i++) {
if (G->vexs[i] == v) {
break;
}
}
//如果找不到,输出提示语句,返回-1
if (i > G->vexnum) {
printf("no this vertex\n");
return -1;
}
return i;
}
//构造无向图
void CreateDN(MGraph* G) {
int i, j, n, m;
int v1, v2;
scanf("%d,%d", &(G->vexnum), &(G->arcnum));
for (i = 0; i < G->vexnum; i++) {
scanf("%d", &(G->vexs[i]));
}
for (i = 0; i < G->vexnum; i++) {
for (j = 0; j < G->vexnum; j++) {
G->arcs[i][j].adj = 0;
}
}
for (i = 0; i < G->arcnum; i++) {
scanf("%d,%d", &v1, &v2);
n = LocateVex(G, v1);
m = LocateVex(G, v2);
if (m == -1 || n == -1) {
printf("no this vertex\n");
return;
}
G->arcs[n][m].adj = 1;
G->arcs[m][n].adj = 1;
}
}
int FirstAdjVex(MGraph G, int v)
{
int i;
//对于数组下标 v 处的顶点,找到第一个和它相邻的顶点,并返回该顶点的数组下标
for (i = 0; i < G.vexnum; i++) {
if (G.arcs[v][i].adj) {
return i;
}
}
return -1;
}
int NextAdjVex(MGraph G, int v, int w)
{
int i;
//对于数组下标 v 处的顶点,从 w 位置开始继续查找和它相邻的顶点,并返回该顶点的数组下标
for (i = w + 1; i < G.vexnum; i++) {
if (G.arcs[v][i].adj) {
return i;
}
}
return -1;
}
//初始化队列,这是一个有头结点的队列链表
void InitQueue(Queue** Q) {
(*Q) = (Queue*)malloc(sizeof(Queue));
(*Q)->next = NULL;
}
//顶点元素v进队列
void EnQueue(Queue** Q, VertexType v) {
Queue* temp = (*Q);
//创建一个存储 v 的结点
Queue* element = (Queue*)malloc(sizeof(Queue));
element->data = v;
element->next = NULL;
//将 v 添加到队列链表的尾部
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = element;
}
//队头元素出队列
void DeQueue(Queue** Q, int* u) {
Queue* del = (*Q)->next;
(*u) = (*Q)->next->data;
(*Q)->next = (*Q)->next->next;
free(del);
}
//判断队列是否为空
bool QueueEmpty(Queue* Q) {
if (Q->next == NULL) {
return true;
}
return false;
}
//释放队列占用的堆空间
void DelQueue(Queue* Q) {
Queue* del = NULL;
while (Q->next) {
del = Q->next;
Q->next = Q->next->next;
free(del);
}
free(Q);
}
//广度优先搜索
void BFSTraverse(MGraph G) {
int v, u, w;
Queue* Q = NULL;
InitQueue(&Q);
//将用做标记的visit数组初始化为false
for (v = 0; v < G.vexnum; ++v) {
visited[v] = false;
}
//遍历图中的各个顶点
for (v = 0; v < G.vexnum; v++) {
//若当前顶点尚未访问,从此顶点出发,找到并访问和它连通的所有顶点
if (!visited[v]) {
//访问顶点,并更新它的访问状态
printf("%d ", G.vexs[v]);
visited[v] = true;
//将顶点入队
EnQueue(&Q, G.vexs[v]);
//遍历队列中的所有顶点
while (!QueueEmpty(Q)) {
//从队列中的一个顶点出发
DeQueue(&Q, &u);
//找到顶点对应的数组下标
u = LocateVex(&G, u);
//遍历紧邻 u 的所有顶点
for (w = FirstAdjVex(G, u); w >= 0; w = NextAdjVex(G, u, w)) {
//将紧邻 u 且尚未访问的顶点,访问后入队
if (!visited[w]) {
printf("%d ", G.vexs[w]);
visited[w] = true;
EnQueue(&Q, G.vexs[w]);
}
}
}
}
}
DelQueue(Q);
}
int main() {
MGraph G;
//构建图
CreateDN(&G);
//对图进行广度优先搜索
BFSTraverse(G);
return 0;
}
6.6 Dijkstra 算法
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
#define V 6 //图的顶点数
int minDistance(int dist[], bool sptSet[]) {
int min = INT_MAX, min_index;
for (int v = 0; v < V; v++) {
if (sptSet[v] == false && dist[v] <= min) {
min = dist[v];
min_index = v;
}
}
return min_index;
}
void dijkstra(int graph[V][V], int src) {
int dist[V]; //存储源节点到各个节点的最短距离
bool sptSet[V]; //标记是否包含在最短路径树中
for (int i = 0; i < V; i++) {
dist[i] = INT_MAX;
sptSet[i] = false;
}
dist[src] = 0;
for (int count = 0; count < V-1; count++) {
int u = minDistance(dist, sptSet);
sptSet[u] = true;
for (int v = 0; v < V; v++) {
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX
&& dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
//打印最短路径
printf("节点\t\t距离\n");
for (int i = 0; i < V; i++) {
printf("%d\t\t%d\n", i, dist[i]);
}
}
int main() {
int graph[V][V] = {
{0, 2, 0, 3, 0, 0},
{2, 0, 4, 4, 5, 0},
{0, 4, 0, 1, 0, 0},
{3, 4, 1, 0, 7, 4},
{0, 5, 0, 7, 0, 3},
{0, 0, 0, 4, 3, 0}
};
dijkstra(graph, 0);
return 0;
}
6.7 Bellman-Ford 算法
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
#define V 5 //图的顶点数
#define E 8 //图的边数
struct Edge {
int src, dest, weight;
};
void bellmanFord(struct Edge edges[], int dist[], int src) {
int i, j;
//初始化距离数组
for (i = 0; i < V; i++) {
dist[i] = INT_MAX;
}
dist[src] = 0;
//执行 V-1 次迭代
for (i = 0; i < V-1; i++) {
for (j = 0; j < E; j++) {
int u = edges[j].src;
int v = edges[j].dest;
int weight = edges[j].weight;
if (dist[u] != INT_MAX && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}
}
//检测负环
for (j = 0; j < E; j++) {
int u = edges[j].src;
int v = edges[j].dest;
int weight = edges[j].weight;
if (dist[u] != INT_MAX && dist[u] + weight < dist[v]) {
printf("图中存在负环\n");
return;
}
}
//打印最短路径
printf("节点\t\t距离\n");
for (i = 0; i < V; i++) {
printf("%d\t\t%d\n", i, dist[i]);
}
}
int main() {
struct Edge edges[E] = {
{0, 1, -1}, {0, 2, 4}, {1, 2, 3},
{1, 3, 2}, {1, 4, 2}, {3, 2, 5},
{3, 1, 1}, {4, 3, -3}
};
int dist[V];
int src = 0;
bellmanFord(edges, dist, src);
return 0;
}