图作为一种强大的数据结构,在众多领域有着广泛而重要的应用。从计算机网络到项目管理,从交通规划到电路设计,图的相关算法和概念都发挥着关键作用。本文将详细探讨图的几个基本应用:最小(代价)生成树、最短路径、拓扑排序和关键路径,并结合相关计算公式进行深入剖析。
最小生成树是针对无向带权连通图而言的,它是一棵包含图中所有顶点的无环连通子图,且边的权值之和最小。在一个具有 (n) 个顶点的无向带权连通图中,其最小生成树的边数固定为 (n - 1)。这是因为要连接 (n) 个顶点且保证无环连通,最少需要 (n - 1) 条边,就像构建一个树状结构将所有顶点连接起来。
#include
#include
#include
#define MAX_VERTEX_NUM 100
#define INF 0x3f3f3f3f
// 邻接矩阵存储图
typedef struct {
int adj[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int vexnum;
int arcnum;
} MGraph;
// 记录顶点是否在最小生成树顶点集合中
bool inMST[MAX_VERTEX_NUM];
// 初始化图
void InitGraph(MGraph* G, int vexnum) {
G->vexnum = vexnum;
G->arcnum = 0;
for (int i = 0; i < vexnum; i++) {
for (int j = 0; j < vexnum; j++) {
G->adj[i][j] = INF;
}
}
}
// 添加边
void AddEdge(MGraph* G, int v, int w, int weight) {
G->adj[v][w] = weight;
G->adj[w][v] = weight;
G->arcnum++;
}
// Prim 算法求最小生成树
void Prim(MGraph G) {
int sumWeight = 0;
// 初始化,先将第一个顶点加入最小生成树顶点集合
inMST[0] = true;
for (int i = 1; i < G.vexnum; i++) {
inMST[i] = false;
}
// 循环 n - 1 次,每次找到一条最小生成树的边
for (int k = 0; k < G.vexnum - 1; k++) {
int minWeight = INF;
int minV = -1, minW = -1;
// 遍历已在最小生成树顶点集合中的顶点与不在集合中的顶点之间的边
for (int v = 0; v < G.vexnum; v++) {
if (inMST[v]) {
for (int w = 0; w < G.vexnum; w++) {
if (!inMST[w] && G.adj[v][w] < minWeight) {
minWeight = G.adj[v][w];
minV = v;
minW = w;
}
}
}
}
if (minV!= -1 && minW!= -1) {
sumWeight += minWeight;
inMST[minW] = true;
printf("选择边 (%d, %d) 加入最小生成树,权值为 %d\n", minV, minW, minWeight);
}
}
printf("最小生成树的总权值为: %d\n", sumWeight);
}
#include
#include
#include
#define MAX_VERTEX_NUM 100
#define MAX_EDGE_NUM MAX_VERTEX_NUM * (MAX_VERTEX_NUM - 1) / 2
#define INF 0x3f3f3f3f
// 边结构体
typedef struct Edge {
int v;
int w;
int weight;
} Edge;
// 并查集结构体
typedef struct {
int* parent;
int size;
} UFSet;
// 初始化并查集
void UFSet_Init(UFSet* uf, int n) {
uf->parent = (int*)malloc(n * sizeof(int));
uf->size = n;
for (int i = 0; i < n; i++) {
uf->parent[i] = i;
}
}
// 查找操作
int UFSet_Find(UFSet* uf, int x) {
while (x!= uf->parent[x]) {
x = uf->parent[x];
}
return x;
}
// 合并操作
void UFSet_Union(UFSet* uf, int x, int y) {
int rootx = UFSet_Find(uf, x);
int rooty = UFSet_Find(uf, y);
if (rootx!= rooty) {
uf->parent[rooty] = rootx;
}
}
// 比较函数,用于边的排序
int compare(const void* a, const void* b) {
Edge* e1 = (Edge*)a;
Edge* e2 = (Edge*)b;
return e1->weight - e2->weight;
}
// Kruskal 算法求最小生成树
void Kruskal(MGraph G) {
int sumWeight = 0;
Edge edges[MAX_EDGE_NUM];
int edgeIndex = 0;
// 将图中的边存储到边数组中
for (int v = 0; v < G.vexnum; v++) {
for (int w = v + 1; w < G.vexnum; w++) {
if (G.adj[v][w]!= INF) {
edges[edgeIndex].v = v;
edges[edgeIndex].w = w;
edges[edgeIndex].weight = G.adj[v][w];
edgeIndex++;
}
}
}
// 对边数组进行排序
qsort(edges, edgeIndex, sizeof(Edge), compare);
UFSet uf;
UFSet_Init(&uf, G.vexnum);
// 依次选取边构建最小生成树
for (int i = 0; i < edgeIndex; i++) {
int v = edges[i].v;
int w = edges[i].w;
int rootv = UFSet_Find(&uf, v);
int rootw = UFSet_Find(&uf, w);
if (rootv!= rootw) {
sumWeight += edges[i].weight;
UFSet_Union(&uf, rootv, rootw);
printf("选择边 (%d, %d) 加入最小生成树,权值为 %d\n", v, w, edges[i].weight);
}
}
printf("最小生成树的总权值为: %d\n", sumWeight);
}
最短路径问题是在图中寻找从一个顶点到另一个顶点(或多个顶点)的路径,使得路径上的边权值之和最小。主要分为单源最短路径和多源最短路径问题。
#include
#include
#include
#define MAX_VERTEX_NUM 100
#define INF 0x3f3f3f3f
// 邻接矩阵存储图
typedef struct {
int adj[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int vexnum;
int arcnum;
} MGraph;
// 记录顶点是否已确定最短路径
bool visited[MAX_VERTEX_NUM];
// 记录从源顶点到其他顶点的最短距离
int dist[MAX_VERTEX_NUM];
// 初始化图
void InitGraph(MGraph* G, int vexnum) {
G->vexnum = vexnum;
G->arcnum = 0;
for (int i = 0; i < vexnum; i++) {
for (int j = 0; j < vexnum; j++) {
G->adj[i][j] = INF;
}
}
}
// 添加边
void AddEdge(MGraph* G, int v, int w, int weight) {
G->adj[v][w] = weight;
G->adj[w][v] = weight;
G->arcnum++;
}
// Dijkstra 算法求单源最短路径
void Dijkstra(MGraph G, int source) {
// 初始化距离数组和访问数组
for (int i = 0; i < G.vexnum; i++) {
dist[i] = G.adj[source][i];
visited[i] = false;
}
dist[source] = 0;
visited[source] = true;
// 循环 n - 1 次,确定到其他顶点的最短路径
for (int k = 0; k < G.vexnum - 1; k++) {
int minDist = INF;
int minV = -1;
// 找到未确定最短路径的顶点中距离源顶点最近的顶点
for (int v = 0; v < G.vexnum; v++) {
if (!visited[v] && dist[v] < minDist) {
minDist = dist[v];
minV = v;
}
}
if (minV!= -1) {
visited[minV] = true;
// 更新其他顶点的距离
for (int w = 0; w < G.vexnum; w++) {
if (!visited[w] && G.adj[minV][w]!= INF && dist[minV] + G.adj[minV][w] < dist[w]) {
dist[w] = dist[minV] + G.adj[minV][w];
}
}
}
}
// 输出最短路径信息
printf("从顶点 %d 到其他顶点的最短路径如下:\n", source);
for (int i = 0; i < G.vexnum; i++) {
if (i!= source) {
if (dist[i] == INF) {
printf("到顶点 %d 无可达路径\n", i);
} else {
printf("到顶点 %d 的最短路径长度为 %d\n", i, dist[i]);
}
}
}
}
#include
#include
#include
#define MAX_VERTEX_NUM 100
#define INF 0x3f3f3f3f
// 边结构体
typedef struct Edge {
int v;
int w;
int weight;
} Edge;
// 邻接表存储图
typedef struct {
Edge* edges;
int* head;
int* next;
int vexnum;
int arcnum;
} ALGraph;
// 初始化邻接表图
void InitALGraph(ALGraph* G, int vexnum) {
G->vexnum = vexnum;
G->arcnum = 0;
G->edges = (Edge*)malloc(MAX_EDGE_NUM * sizeof(Edge));
G->head = (int*)malloc(MAX_VERTEX_NUM * sizeof(int));
G->next = (int*)malloc(MAX_EDGE_NUM * sizeof(int));
for (int i = 0; i < vexnum; i++) {
G->head[i] = -1;
}
}
// 添加边到邻接表
void AddArc(ALGraph* G, int v, int w, int weight) {
G->edges[G.arcnum].v = v;
G->edges[G.arcnum].w = w;
G->edges[G.arcnum].weight = weight;
G->next[G.arcnum] = G->head[v];
G->head[v] = G.arcnum;
G->arcnum++;
}
// Bellman - Ford 算法求单源最短路径
bool BellmanFord(ALGraph G, int source) {
// 初始化距离数组
int dist[MAX_VERTEX_NUM];
for (int i = 0; i < G.vexnum; i++) {
dist[i] = INF;
}
dist[source] = 0;
// 进行 n - 1 轮松弛操作
for (int k = 0; k < G.vexnum - 1; k++) {
bool updated = false;
for (int i = 0; i < G.arcnum; i++) {
int v = G.edges[i].v;
int w = G.edges[i].w;
int weight = G.edges[i].weight;
if (dist[v]!= INF && dist[v] + weight < dist[w]) {
dist[w] = dist[v] + weight;
updated = true;
}
}
if (!updated) {
break;
}
}
// 检查是否存在负权回路
for (int i = 0; i < G.arcnum; i++) {
int v = G.edges[i].v;
int w = G.edges[i].w;
int weight = G.edges[i].weight;
if (dist[v]!= INF && dist[v] + weight < dist[w]) {
printf("图中存在负权回路,无法求出最短路径\n");
return false;
}
}
// 输出最短路径信息
printf("从顶点 %d 到其他顶点的最短路径如下:\n", source);
for (int i = 0; i < G.vexnum; i++) {
if (i!= source) {
if (dist[i] == INF) {
printf("到顶点 %d 无可达路径\n", i);
} else {
printf("到顶点 %d 的最短路径长度为 %d\n", i, dist[i]);
}
}
}
return true;
}
#include
#include
#define MAX_VERTEX_NUM 100
#define INF 0x3f3f3f3f
// 邻接矩阵存储
```c
// 邻接矩阵存储图
typedef struct {
int adj[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int vexnum;
int arcnum;
} MGraph;
// Floyd - Warshall 算法求多源最短路径
void FloydWarshall(MGraph G) {
int dist[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
// 初始化距离矩阵
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++) {
dist[i][j] = G.adj[i][j];
}
}
// 动态规划更新最短路径
for (int k = 0; k < G.vexnum; k++) {
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++) {
if (dist[i][k]!= INF && dist[k][j]!= INF && dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
// 输出最短路径信息
printf("多源最短路径矩阵如下:\n");
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++) {
if (dist[i][j] == INF) {
printf("INF ");
} else {
printf("%d ", dist[i][j]);
}
}
printf("\n");
}
}