参考王道《2023年数据结构考研复习指导》
一、单源最短路径-无权图-BFS最短路径
#include <iostream>
#define MaxVerTexNum 20
typedef char VertexType; // 顶点的数据类型
typedef int EdgeType; // 带权图中边上权值的数据类型
// 邻接矩阵存储图
typedef struct {
VertexType Vex[MaxVerTexNum]; // 顶点表
EdgeType Edge[MaxVerTexNum][MaxVerTexNum]; // 邻接矩阵,边表
int vexnum, arcnum; // 图的当前顶点数和边数/弧数
} MGraph;
// 链式队列结点
typedef struct LinkNode {
int data; // 存储图的顶点的下标
struct LinkNode* next;
} LinkNode;
// 链式队列
typedef struct LinkQueue {
LinkNode* front, * rear;
} LinkQueue;
bool visited[MaxVerTexNum]; // 已访问标志数组
int d[MaxVerTexNum]; // 记录每个顶点到u的最短路径长度
int path[MaxVerTexNum]; // 记录每个顶点在广度优先生成树中的父节点
bool InitQueue(LinkQueue& Q);
bool IsEmpty(LinkQueue Q);
bool EnQueue(LinkQueue& Q, int x);
bool DeQueue(LinkQueue& Q, int& x);
void DestroyQueue(LinkQueue& Q);
int FirstNeighbor(MGraph G, int x);
int NextNeighbor(MGraph G, int x, int y);
void BFS_MIN_Distance(MGraph G, int u);
int main() {
MGraph G = {
{'1', '2', '3', '4', '5', '6', '7', '8'},
{
{0,1,0,0,1,0,0,0},
{1,0,0,0,0,1,0,0},
{0,0,0,1,0,1,1,0},
{0,0,1,0,0,0,1,1},
{1,0,0,0,0,0,0,0},
{0,1,1,0,0,0,1,0},
{0,0,1,1,0,1,0,1},
{0,0,0,1,0,0,1,0},
},
8, 10
};
BFS_MIN_Distance(G, 1);
for (int i = 0; i < G.vexnum; ++i) {
std::cout << d[i] << " ";
}
std::cout << std::endl;
for (int i = 0; i < G.vexnum; ++i) {
std::cout << path[i] << " ";
}
std::cout << std::endl;
system("pause");
return 0;
}
/*==================================================================*/
// 初始化队列(带头结点)
bool InitQueue(LinkQueue& Q) {
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
if (Q.front == NULL)
return false;
Q.front->next = NULL;
return true;
}
// 判断队列是否为空
bool IsEmpty(LinkQueue Q) {
if (Q.front == Q.rear)
return true;
else
return false;
}
// 入队(带头结点)
bool EnQueue(LinkQueue& Q, int x) {
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
if (s == NULL)
return false;
s->data = x;
s->next = NULL;
Q.rear->next = s;
Q.rear = s;
return true;
}
// 出队(带头结点)
bool DeQueue(LinkQueue& Q, int& x) {
if (IsEmpty(Q))
return false;
LinkNode* p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if (p == Q.rear) // 若此时是最后一个结点出队
Q.rear = Q.front;
free(p);
return true;
}
void DestroyQueue(LinkQueue& Q) {
LinkNode* p = Q.front->next;
LinkNode* q;
while (p != NULL) {
q = p;
p = p->next;
free(q);
}
free(Q.front);
Q.front = Q.rear = NULL;
}
/*==================================================================*/
// 求图G中顶点x(下标)的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-1
int FirstNeighbor(MGraph G, int x) {
if (x >= G.vexnum)
return -1;
int i = 0;
while (i < G.vexnum) {
if (G.Edge[x][i] != 0)
return i;
i++;
}
return -1;
}
// 假设图G中顶点y是顶点x的一个邻接点,返回除y之外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,则返回-1
int NextNeighbor(MGraph G, int x, int y) {
if (x >= G.vexnum || y >= G.vexnum)
return -1;
int i = y + 1;
while (i < G.vexnum) {
if (G.Edge[x][i] != 0)
return i;
i++;
}
return -1;
}
/*==================================================================*/
// 求顶点u到其他顶点的最短路径
void BFS_MIN_Distance(MGraph G, int u) { // 从顶点u出发,广度优先遍历图G
for (int i = 0; i < G.vexnum; ++i) {
d[i] = -1; // -1代表无穷大
path[i] = -1; // 最短路径从哪个定点过来
}
d[u] = 0;
visited[u] = true;
LinkQueue Q;
InitQueue(Q);
EnQueue(Q, u); // 顶点u入队列Q
while (!IsEmpty(Q)) {
DeQueue(Q, u); // 顶点u出队列
for (int w = FirstNeighbor(G, u); w >= 0; w = NextNeighbor(G, u, w)) { //检测v所有邻接点
if (!visited[w]) { // w为v的尚未访问的邻接点
d[w] = d[u] + 1; // 路径长度加1
path[w] = u; // 最短路径应从u到w
visited[w] = true; // 对w做已访问标记
EnQueue(Q, w); // 顶点w入队
}
}
}
DestroyQueue(Q);
}
二、Dijkstra算法
#include <iostream>
#define MaxVerTexNum 20
typedef char VertexType; // 顶点的数据类型
typedef int EdgeType; // 带权图中边上权值的数据类型
// 邻接矩阵存储图
typedef struct {
VertexType Vex[MaxVerTexNum]; // 顶点表
EdgeType Edge[MaxVerTexNum][MaxVerTexNum]; // 邻接矩阵,边表
int vexnum, arcnum; // 图的当前顶点数和边数/弧数
} MGraph;
bool final[MaxVerTexNum]; // 标记各顶点是否已找到最短路径
EdgeType dist[MaxVerTexNum]; // 最短路径长度
int path[MaxVerTexNum]; // 路径上的前驱(记录前驱节点的下标)
void ShortestPath_Dijkstra(MGraph G, int u);
void get_Path(int u, int v);
void find_Path(int v);
int main() {
MGraph G = {
{'0', '1', '2', '3', '4'},
{
{-1, 10, -1, -1, 5},
{-1, -1, 1, -1, 2},
{-1, -1, -1, 4, -1},
{7, -1, 6, -1, -1},
{-1, 3, 9, 2, -1}
},
5, 10
};
ShortestPath_Dijkstra(G, 0);
get_Path(0, 2);
system("pause");
return 0;
}
// 从顶点u出发,到其他结点的最短路径
void ShortestPath_Dijkstra(MGraph G, int u) {
for (int i = 0; i < G.vexnum; ++i) { // 初始化辅助数组
final[i] = false;
dist[i] = G.Edge[u][i];
path[i] = G.Edge[u][i] == -1 ? -1 : 0;
}
final[u] = true;
dist[u] = 0; // 顶点u到自身的最短距离为0
for (int i = 0; i < G.vexnum; ++i) {
if (i == u)
continue;
int min = -1; // -1代表无穷大
int v; // 下一个已找到最短路径的结点
for (int j = 0; j < G.vexnum; ++j) {
if (final[j] == false) {
if (min == -1 && dist[j] != -1) {
v = j;
min = dist[j];
}
else if (min != -1 && dist[j] != -1 && dist[j] < min) {
v = j;
min = dist[j];
}
}
}
final[v] = true;
for (int j = 0; j < G.vexnum; ++j) { // 更新dist和path信息
if (final[j] == false) {
if (dist[j] == -1 && G.Edge[v][j] != -1) {
dist[j] = dist[v] + G.Edge[v][j];
path[j] = v;
}
else if (dist[j] != -1 && G.Edge[v][j] != -1 && dist[j] > dist[v] + G.Edge[v][j]) {
dist[j] = dist[v] + G.Edge[v][j];
path[j] = v;
}
}
}
}
}
void get_Path(int u, int v) {
std::cout << "从" << u << "到" << v << "的最短距离为:" << dist[v] << std::endl
<< "路径为:";
find_Path(v);
std::cout << std::endl;
}
void find_Path(int v) {
if (path[v] == -1) {
std::cout << v;
}
else
{
find_Path(path[v]);
std::cout << " -> " << v;
}
}
三、Floyd算法
#include <iostream>
#define MaxVerTexNum 20
typedef char VertexType; // 顶点的数据类型
typedef int EdgeType; // 带权图中边上权值的数据类型
// 邻接矩阵存储图
typedef struct {
VertexType Vex[MaxVerTexNum]; // 顶点表
EdgeType Edge[MaxVerTexNum][MaxVerTexNum]; // 邻接矩阵,边表
int vexnum, arcnum; // 图的当前顶点数和边数/弧数
} MGraph;
int A[MaxVerTexNum][MaxVerTexNum]; // 各顶点间的最短路径长度
int path[MaxVerTexNum][MaxVerTexNum]; // 两顶点之间的中转点
void ShortestPath_Floyd(MGraph G);
void get_Path(int u, int v);
void find_Path(int u, int v);
int main() {
MGraph G = {
{'0', '1', '2', '3', '4'},
{
{-1, -1, 1, -1, 10},
{-1, -1, -1, 1, 5},
{-1, 1, -1, -1, 7},
{-1, -1, -1, -1, 1},
{-1, -1, -1, -1, -1},
},
5, 7
};
ShortestPath_Floyd(G);
get_Path(0, 4);
system("pause");
return 0;
}
void ShortestPath_Floyd(MGraph G) {
for (int i = 0; i < G.vexnum; ++i) {
for (int j = 0; j < G.vexnum; ++j) {
A[i][j] = G.Edge[i][j];
path[i][j] = -1;
}
}
for (int k = 0; k < G.vexnum; ++k) { // 以Vk作为中转点
for (int i = 0; i < G.vexnum; ++i) {
for (int j = 0; j < G.vexnum; ++j) {
if (A[i][j] == -1 && A[i][k] != -1 && A[k][j] != -1) {
A[i][j] = A[i][k] + A[k][j];
path[i][j] = k;
}
else if (A[i][j] != -1 && A[i][k] != -1 && A[k][j] != -1 && A[i][j] > A[i][k] + A[k][j]) {
A[i][j] = A[i][k] + A[k][j];
path[i][j] = k;
}
}
}
}
}
void get_Path(int u, int v) {
std::cout << "从" << u << "到" << v << "的最短距离为:" << A[u][v] << std::endl
<< "路径为:";
std::cout << u << " -> ";
find_Path(u, v);
std::cout << v << std::endl;
}
void find_Path(int u, int v) {
if (path[u][v] != -1) {
find_Path(u, path[u][v]);
std::cout << path[u][v] << " -> ";
find_Path(path[u][v], v);
}
}