- OS: Mac Catalina 10.15.4
- Hardware: Intel Core i9/16G 2667MHz DDR4
- 编译器版本:Mac Xcode 11.6
第06章 图
目录
6.1 图的存储结构
6.1.1 邻接矩阵表示法
6.1.1.1 邻接矩阵的存储结构定义
#include <stdio.h>
#include <limits.h>
#include <float.h>
typedef char VertexType; // 定点类型假设为char型
typedef float Adjmatrix; // 邻接矩阵类型,假设为float型
#define MaxVertexNum 50 // 最大顶点数
typedef struct
{
VertexType vexs[MaxVertexNum]; // 顶点数组
Adjmatrix arcs[MaxVertexNum][MaxVertexNum]; // 邻接矩阵
}MGraph;
typedef struct // 构建边存储结构
{
int v1, v2; // 顶点序号1,定点序号2
Adjmatrix weight; // 权值
}Edge;
6.1.1.2 创建邻接矩阵
// 构造无向图
// 邻接矩阵表示法,无向图未经压缩存储
// 参数:
// MGraph *G: 无向图
// VertexType v: 顶点一维数组列表
// int n: 图的顶点数
//
// int e: 图的边数
// 返回:
// void
void CreateMGraph(MGraph *G, VertexType v[], int n, Edge edge[], int e)
{
int i, j, k, t;
Adjmatrix w; // 权值
for (i = 0; i < n; i++) // 写入顶点信息
G->vexs[i] = v[i];
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
G->arcs[i][j] = FLT_MAX; // 初始化邻接矩阵元素为无穷大
for (k = 0; k < e; k++) // 读入e条边,建立邻接矩阵
{
for (t = 0; t < e; t++) // 读取出边上的左右顶点序号和权值并写入到图中
{
i = edge[t].v1;
j = edge[t].v2;
w = edge[t].weight;
G->arcs[i][j] = w;
G->arcs[j][i] = w; // 置矩阵对称元素权值
}
}
}
6.1.1.3 邻接矩阵示例
#include <stdio.h>
#include "MGraph.h"
int main(void)
{
const int n = 5;
const int e = 7;
int i, j;
MGraph G;
VertexType v[n];
Edge edge[e];
printf("开始输入顶点数=%d 边数=%d\n", n, e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("开始输入边信息(顶点序号及权值):\n");
getchar();
for (i = 0; i < e; i++)
{
printf("%d: ", i + 1);
scanf("%d,%d,%f", &edge[i].v1, &edge[i].v2, &edge[i].weight);
getchar();
}
printf("开始创建无向图...\n");
CreateMGraph(&G, v, n, edge, e);
printf("\n创建完成,开始输出结果...\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if(G.arcs[i][j] == FLT_MAX)
printf(" ∞");
else
printf("%4.0f", G.arcs[i][j]);
}
printf("\n");
}
return 0;
}
6.1.2 邻接表表示法
6.1.2.1 邻接表的存储结构定义
#define MaxVertexNum 20
typedef char VertexType;
typedef struct node // 边表结点类型
{
int adjvex; // 顶点的序号
struct node *next; // 指向下一条边的指针
}EdgeNode;
typedef struct vnode // 顶点表结点
{
VertexType vertex; // 顶点域
EdgeNode *link; // 边表头指针
}VNode, Adjlist[MaxVertexNum]; // 邻接表
typedef Adjlist ALGraph; // 定义为图类型
6.1.2.2 邻接表无向图的建表算法
// 初始化领接表结构的图
// 参数:
// ALGraph GL: 邻接表结构的图
// int n: 结点数量
// VertexType v[]: 结点数组
// 返回:
// void
void InitVertex(ALGraph GL, int n, VertexType v[])
{
int i;
for (i = 0; i < n; i++) { // 建立顶点表
GL[i].vertex = v[i]; // 读入顶点信息
GL[i].link = NULL; // 边表头指针置空
GL[i].lastNode = NULL; // 最后一个节点置空
}
}
// 采用尾插法添加无向图结点
// 参数:
// ALGraph GL: 邻接表结构的图
// int i: 顶点1
// int j: 顶点2
// float w: 权值
// 返回:
// void
void AddUndirectedGraphNode(ALGraph GL, int v1, int v2, float w)
{
AddNode(GL, v1, v2, w); // 添加出边
AddNode(GL, v2, v1, w); // 添加入边
}
// 采用尾插法添加有向图结点
// 参数:
// ALGraph GOut: 邻接表结构图的出
// ALGraph GIn: 邻接表结构图的逆(入)
// int i: 顶点1
// int j: 顶点2
// float w: 权值
// 返回:
// void
void AddDirectedGraphNode(ALGraph GOut, ALGraph GIn, int v1, int v2, float w)
{
AddNode(GOut, v1, v2, w); // 添加出边表
AddNode(GIn, v2, v1, w); // 添加入边表
}
// 采用尾插法建立顶点的领接表
// 参数:
// ALGraph GL: 邻接表结构的图
// int i: 顶点1
// int j: 顶点2
// float w: 权值
// 返回:
// void
void AddNode(ALGraph GL, int v1, int v2, float w)
{
EdgeNode *p1 = NULL, *p2 = NULL;
if(GL[v1].link == NULL) //如果存在顶点表指向非空,则表示存在第1个节点
{
p1 = (EdgeNode*)malloc(sizeof(EdgeNode)); // 生成新的边表结点
p1->adjvex = v1; // 将邻接点序号j赋给新结点的邻接点域
p1->next = NULL; // 下一个指向置空
GL[v1].link = p1; // 将新结点插入到顶点vi的边表头部
GL[v1].lastNode = p1; // 更新最后一个节点的指向
}
p2 = (EdgeNode*)malloc(sizeof(EdgeNode)); // 生成新的边表结点
p2->adjvex = v2; // 将邻接点序号i赋给新结点的邻接点域
p2->next = NULL;
p1 = GL[v1].lastNode; // 提取边表最后一个节点
p1->next = p2; // 将变表结点加长一个
p1->weight = w; // 设置权重
GL[v1].lastNode = p2; // 更新最后一个节点的指向
}
6.1.2.3 邻接表的程序示例(无向图)
//
// main.c
// 图的邻接表表示法
//
// Created by turbo on 2022/4/10.
// Copyright © 2022 LiangNuo Software Technology Co., LTD. All rights reserved.
//
#include <stdio.h>
#include "ALGraph.h"
int main(void)
{
const int n = 4;
const int e = 5;
int i, v1, v2;
float w;
ALGraph G;
VertexType v[n];
EdgeNode *p;
printf("顶点数=%d, 边数=%d, 表结点数=%d\n", n, e, 2*e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("初始化邻接表...\n");
InitVertex(G, n, v);
printf("初始化完成,开始输入顶点序号及权值:\n");
for (i = 0; i < e; i++)
{
printf("[%d]:顶点1 顶点2 权值:", i);
scanf("%d %d %f", &v1, &v2, &w);
AddUndirectedGraphNode(G, v1, v2, w);
}
printf("创建完成,开始输出无向图:\n");
for (i = 0; i < n; i++)
{
printf("(%c)-->", G[i].vertex);
p = G[i].link;
while (p != NULL)
{
printf("%d", p->adjvex); // 输出节点
if (p->next != NULL)
{
printf("(%4.2f)", p->weight); // 输出权值
printf("-->");
}
p = p->next;
}
printf("^\n");
}
printf("\n");
return 0;
}
6.1.2.4 邻接表的程序示例(有向图)
//
// main.c
// 图的邻接表有向图
//
// Created by turbo on 2022/5/18.
// Copyright © 2022 LiangNuo Software Technology Co., LTD. All rights reserved.
//
#include <stdio.h>
#include "ALGraph.h"
int main(void)
{
const int n = 5;
const int e = 9;
int i, v1, v2;
float w;
ALGraph G1, G2;
VertexType v[n];
EdgeNode *p;
printf("顶点数=%d, 边数=%d, 表结点数=%d\n", n, e, 2*e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("初始化邻接表...\n");
InitVertex(G1, n, v); // 出邻接表
InitVertex(G2, n, v); // 入邻接表
printf("初始化完成,开始输入顶点序号及权值:\n");
for (i = 0; i < e; i++)
{
printf("[%d]:顶点1 顶点2 权值:", i);
scanf("%d %d %f", &v1, &v2, &w);
AddDirectedGraphNode(G1, G2, v1, v2, w);
}
printf("创建完成,开始输出有向图---邻接表:\n");
for (i = 0; i < n; i++)
{
printf("(%c)-->", G1[i].vertex);
p = G1[i].link;
while (p != NULL)
{
printf("%d", p->adjvex); // 输出节点
if (p->next != NULL)
{
printf("(%4.2f)", p->weight); // 输出权值
printf("-->");
}
p = p->next;
}
printf("^\n");
}
printf("开始输出有向图---逆邻接表:\n");
for (i = 0; i < n; i++)
{
printf("(%c)-->", G2[i].vertex);
p = G2[i].link;
while (p != NULL)
{
printf("%d", p->adjvex); // 输出节点
if (p->next != NULL)
{
printf("(%4.2f)", p->weight); // 输出权值
printf("-->");
}
p = p->next;
}
printf("^\n");
}
return 0;
}
6.2 图的遍历
6.2.1 深度优先搜索遍历
6.2.1.1 邻接矩阵结构
// 从顶点vi出发,深度优先搜索遍历图G
// 邻接矩阵结构
// 参数:
// MGraph G 图G
// int i 从i点出发,确保顶点i有邻接点
// int n 顶点总数
// 返回:
// void
int visited[MaxVertexNum]; // 用来标记是否访问过
void DepthFirstSearch(MGraph G, int i, int n)
{
int j;
printf("v%d->", i); // 假定访问顶点vi以输出该顶点的序号代之
visited[i] = 1; // 标记vi已经访问过
for (j = 0; j < n; j++) // 依次搜索vi的每个邻接点
{
if (G.arcs[i][j] == 1 && !visited[j])
// 若(vi,vj)包含E(G),且vj未被访问过,则从开始递归调用
DepthFirstSearch(G, j, n);
}
}
6.2.1.2 邻接矩阵结构深度优先遍历程序示例
#include <stdio.h>
#include "MGraph.h"
int main(void)
{
const int n = 9;
const int e = 10;
int i, m, j;
m = n * (n + 1) / 2;
MGraph G;
VertexType v[n];
Edge edge[e];
printf("开始输入顶点数=%d 边数=%d\n", n, e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("开始输入变信息(顶点序号,不考虑权值):\n");
getchar();
for (i = 0; i < e; i++)
{
printf("%d: ", i + 1);
scanf("%d,%d", &edge[i].v1, &edge[i].v2);
edge[i].weight = 1.0; // 指定权值=1
getchar();
}
printf("开始创建无向图...\n");
CreateMGraph(&G, v, n, edge, e);
printf("创建完成,开始输出矩阵...\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if(G.arcs[i][j] == FLT_MAX)
printf(" ∞");
else
printf("%4.0f", G.arcs[i][j]);
}
printf("\n");
}
printf("开始使用深度优先遍历输出结果...\n");
printf("输入起始顶点(确保顶点有邻接点): ");
scanf("%d", &i);
DepthFirstSearch(G, i, n);
printf("\n");
return 0;
}
6.2.1.3 邻接表结构
// 深度优先搜索遍历图G,从顶点vi出发,邻接表结构
// 确保vi有邻接顶点
// 参数:
// ALGraph G: 邻接表结构的图
// int i: 从顶点vi开始
// 返回:
// void
int visited[MaxVertexNum];
void DepthFirstSearch(ALGraph G, int i)
{
EdgeNode *p;
int j;
printf("%d->", i);
visited[i] = 1;
p = G[i].link;
while (p != NULL) {
j = p->adjvex;
if (!visited[j])
DepthFirstSearch(G, j);
p = p->next;
}
}
6.2.1.4 邻接表结构深度优先遍历程序示例
#include <stdio.h>
#include "ALGraph.h"
int main(void)
{
const int n = 9;
const int e = 10;
int i, v1, v2;
float w;
ALGraph G;
VertexType v[n];
EdgeNode *p;
printf("顶点数=%d, 边数=%d, 表结点数=%d\n", n, e, 2*e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("初始化邻接表...\n");
InitVertex(G, n, v);
printf("初始化完成,开始输入顶点序号及权值:\n");
for (i = 0; i < e; i++)
{
printf("[%d]:顶点1 顶点2 权值=1无需输入:", i);
scanf("%d %d", &v1, &v2);
w = 1; //指定权值
AddUndirectedGraphNode(G, v1, v2, w);
}
printf("创建完成,开始输出无向图:\n");
for (i = 0; i < n; i++)
{
printf("(%c)-->", G[i].vertex);
p = G[i].link;
while (p != NULL)
{
printf("%d", p->adjvex); // 输出节点
if (p->next != NULL)
{
//printf("(%4.2f)", p->weight); // 输出权值
printf("-->");
}
p = p->next;
}
printf("^\n");
}
printf("开始使用深度优先搜索遍历:\n");
printf("输入开始顶点:");
scanf("%d", &i);
DepthFirstSearch(G, i);
printf("\n遍历输出完成.\n");
return 0;
}
6.2.2 广度优先搜索遍历
6.2.2.1 邻接矩阵结构
// 从顶点vi出发,广度优先搜索遍历图G
// 不支持具有子图的图
// 时间复杂度:
// O^2
// 参数:
// MGraph G 图G
// int i 从i点出发,确保顶点i有邻接点
// int n 顶点总数
// 返回:
// void
void BreachFirstSearch(MGraph G, int i, int n)
{
CirQueue Q;
int k, j;
InitQueue(&Q); // 初始化队列
printf("v%d->", i); // 假定访问顶点vi以输出该顶点的序号代之
visited[i] = 1; // 标记vi以访问过
EnQueue(&Q, i); // 将已访问过的顶点序号i入队
while (!IsQueueEmpty(&Q)) // 当队列非空时,循环处理vi的每个邻接点
{
k = ExQueue(&Q); // 删除队头元素
for (j = 0; j < n; j++) // 依次搜索vk的每一个可能的邻接点
{
if (G.arcs[k][j] == 1 && !visited[j])
{
printf("v%d->", j); // 访问未曾访问过的顶点vj
visited[j] = 1; // 标记vj已访问过
EnQueue(&Q, j); // 定点序号j入队
} // End If
} // End For
} // End While
}
6.2.2.2 邻接矩阵结构广度优先遍历程序示例
#include <stdio.h>
#include "MGraph.h"
int main(void)
{
const int n = 9;
const int e = 10;
int i, m, j;
m = n * (n + 1) / 2;
MGraph G;
VertexType v[n];
Edge edge[e];
printf("开始输入顶点数=%d 边数=%d\n", n, e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("开始输入变信息(顶点序号,不考虑权值):\n");
getchar();
for (i = 0; i < e; i++)
{
printf("%d: ", i + 1);
scanf("%d,%d", &edge[i].v1, &edge[i].v2);
edge[i].weight = 1.0; // 指定权值=1
getchar();
}
printf("开始创建无向图...\n");
CreateMGraph(&G, v, n, edge, e);
printf("创建完成,开始输出矩阵...\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if(G.arcs[i][j] == FLT_MAX)
printf(" ∞");
else
printf("%4.0f", G.arcs[i][j]);
}
printf("\n");
}
printf("开始使用广度优先遍历输出结果...\n");
printf("输入起始顶点(确保顶点有邻接点): ");
scanf("%d", &i);
BreachFirstSearch(G, i, n);
printf("\n输出完成.\n");
return 0;
}
6.2.2.3 邻接表结构
// 广度优先搜索遍历图G,从顶点vi出发,邻接表结构
// 不支持含有子图的图
// 时间复杂度:
// O(n + e)
// 参数:
// ALGraph G: 邻接表结构的图
// int i: 从顶点vi开始
// int n: 顶点数
// 返回:
// void
void BreadthFirstSearch(ALGraph G, int i, int n)
{
CirQueue Q; // 定义一个队列指针
int j, k;
InitQueue(&Q); // 初始化队列
EdgeNode *p;
int visited[MaxVertexNum];
for (int j = 0; j < MaxVertexNum; j++)
visited[j] = 0; // 初始化已访问数组
printf("v%d->", i); // 假定访问顶点vi以输出该顶点的序号代之
visited[i] = 1; // 标记vi已访问过
EnQueue(&Q, i); // 将已访问的顶点序号i入队
while (!IsQueueEmpty(&Q)) // 循环处理vi的每个邻接点
{
k = ExQueue(&Q); // 删除队头元素
p = G[k].link; // 取vk邻接表的表头指针
while (p != NULL) // 依次搜索vk的每一个可能的邻接点
{
j = p->adjvex; // vj为vk的一个邻接点
if (!visited[j]) // 若vj未被访问过
{
printf("v%d->", j); // 访问未曾访问过的顶点vj
visited[j] = 1; // 标记vj已经访问过
EnQueue(&Q, j); // 定点序号j入队
}
p = p->next; // 使p指向vk邻接表的下一个邻接点
} // End While
} // End While
}
6.2.2.4 邻接表结构广度优先遍历程序示例
#include <stdio.h>
#include "ALGraph.h"
int main(void)
{
const int n = 9;
const int e = 10;
int i, v1, v2;
float w;
ALGraph G;
VertexType v[n];
EdgeNode *p;
printf("顶点数=%d, 边数=%d, 表结点数=%d\n", n, e, 2*e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("初始化邻接表...\n");
InitVertex(G, n, v);
printf("初始化完成,开始输入顶点序号及权值:\n");
for (i = 0; i < e; i++)
{
printf("[%d]:顶点1 顶点2 权值=1无需输入:", i);
scanf("%d,%d", &v1, &v2);
w = 1; //指定权值
AddUndirectedGraphNode(G, v1, v2, w);
}
printf("创建完成,开始输出无向图:\n");
for (i = 0; i < n; i++)
{
printf("(%c)-->", G[i].vertex);
p = G[i].link;
while (p != NULL)
{
printf("%d", p->adjvex); // 输出节点
if (p->next != NULL)
{
//printf("(%4.2f)", p->weight); // 输出权值
printf("-->");
}
p = p->next;
}
printf("^\n");
}
printf("开始使用广度优先搜索遍历:\n");
printf("输入开始顶点:");
scanf("%d", &i);
BreadthFirstSearch(G, i, n);
printf("\n遍历输出完成.\n");
return 0;
}
6.2.3 例题6.1
假设有如图6.13所示的图G9,试写出该图的邻接矩阵和邻接表以及该图的邻接矩阵存储表示下从顶点v3开始搜索所得的深度优先(DFS)和广度优先(BFS)便利序列。
#include <stdio.h>
#include "Graph.h"
int main()
{
const int n = 5, e = 7;
int i, j;
VertexType v[n];
Edge edge[e];
EdgeNode *p;
MGraph MG;
ALGraph LG;
printf("顶点数n: %d", n);
printf(" 边数e: %d", e);
printf("\n开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("初始化邻接表...\n");
InitVertex(LG, n, v);
printf("开始输入变信息(顶点序号,不考虑权值):\n");
getchar();
for (i = 0; i < e; i++)
{
printf("%d: ", i + 1);
scanf("%d,%d", &edge[i].v1, &edge[i].v2);
edge[i].weight = 1.0; // 指定权值=1
AddUndirectedGraphNode(LG, edge[i].v1, edge[i].v2, edge[i].weight);
getchar();
}
printf("开始创建无向图...[邻接矩阵表示法]\n");
CreateMGraph(&MG, v, n, edge, e);
printf("创建完成,开始输出矩阵...[邻接矩阵表示法]\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if(MG.arcs[i][j] == FLT_MAX)
printf(" 0");
else
printf("%4.0f", MG.arcs[i][j]);
}
printf("\n");
}
printf("开始输出矩阵...[邻接表表示法]\n");
for (i = 0; i < n; i++)
{
printf("(%c)-->", LG[i].vertex);
p = LG[i].link;
while (p != NULL)
{
printf("%d", p->adjvex); // 输出节点
if (p->next != NULL)
{
//printf("(%4.2f)", p->weight); // 输出权值
printf("-->");
}
p = p->next;
}
printf("^\n");
}
printf("出入要遍历的起始点: "); scanf("%d", &i);
printf("[邻接矩阵表示]下的----[深度优先遍历]\n");
DepthFirstSearchMGraph(MG, i, n);
printf("\n ----[广度优先遍历]\n");
BreadthFirstSearchMGraph(MG, i, n);
printf("\n[邻接表表示]下的----[深度优先遍历]\n");
InitVisited();
DepthFirstSearchALGraph(LG, i);
printf("\n ----[广度优先遍历]\n");
BreadthFirstSearchALGraph(LG, i, n);
printf("\n输出结束.\n");
return 0;
}
6.2.4 例题6.2
试编写一个实现连通图G的深度优先遍历(从顶点v出发)的非递归算法
// 深度优先搜索遍历图G,从顶点vi出发,邻接表结构,顺序栈方法
// 不支持含有子图的图
// 时间复杂度:
// O(n + e)
// 参数:
// ALGraph G: 邻接表结构的图
// int vi: 从顶点vi开始
// int n: 顶点总数
// 返回:
// void
void DepthFirstSearchALGraphUseSeqStack(ALGraph G, int vi, int n)
{
SeqStack S; // 定义一个栈,存放访问过的结点
EdgeNode *p;
InitVisited(); // 假设G的顶点都未曾访问过
InitStack(&S); // 初始化顺序栈
printf("v%c->", G[vi].vertex); // 访问定点vi
visited[vi] = 1; // 标记vi已访问过
p = G[vi].link; // 取vi边表头指针
while (S.top > 0 || p != NULL)
{
while (p)
{
if (visited[p->adjvex]) // 如果已经访问过,则转到下一个结点
p = p->next;
else // 如果没有访问过,则输出该接点,并将结点入栈
{
printf("v%c->", G[p->adjvex].vertex);
visited[p->adjvex] = 1; // 结点访问过
Push(&S, p); // 将访问过的结点入栈
p = G[p->adjvex].link; // 转到结点的顶点表所指向的边表表头
} // End if
} // End While
if (S.top !=0)
{
p = Pop(&S); // 弹出顺序栈中,栈头结点
p = p->next; // 转到该结点的下一个邻接结点
} // End If
} // End While
}
//
// main.c
// 例6.2深度优先遍历非递归栈实现
//
// Created by turbo on 2022/5/23.
// Copyright © 2022 LiangNuo Software Technology Co., LTD. All rights reserved.
//
#include <stdio.h>
#include "Graph.h"
int main(void)
{
const int n = 9;
const int e = 10;
int i, v1, v2;
float w;
ALGraph G;
VertexType v[n];
EdgeNode *p;
printf("顶点数=%d, 边数=%d, 表结点数=%d\n", n, e, 2*e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("初始化邻接表...\n");
InitVertex(G, n, v);
printf("初始化完成,开始输入顶点序号及权值:\n");
for (i = 0; i < e; i++)
{
printf("[%d]:顶点1 顶点2 权值=1无需输入:", i);
scanf("%d,%d", &v1, &v2);
w = 1; //指定权值
AddUndirectedGraphNode(G, v1, v2, w);
}
printf("创建完成,开始输出无向图:\n");
for (i = 0; i < n; i++)
{
printf("(%c)-->", G[i].vertex);
p = G[i].link;
while (p != NULL)
{
printf("%d", p->adjvex); // 输出节点
if (p->next != NULL)
{
//printf("(%4.2f)", p->weight); // 输出权值
printf("-->");
}
p = p->next;
}
printf("^\n");
}
printf("开始使用深度优先搜索遍历(顺序栈方法):\n");
printf("输入开始顶点:");
scanf("%d", &i);
DepthFirstSearchALGraphUseSeqStack(G, i, n);
printf("\n遍历输出完成.\n");
return 0;
}
6.3 图的最小生成树
6.3.1 普里姆方法
// 取顶点u在辅助数组中的下标
int VertexIndex(MGraph G, VertexType u, int n)
{
int i;
for (i = 0; i < n; i++)
{
if (u == G.vexs[i])
break;
}
return i;
}
// 满足条件的最小边(u,k),u属于U,k属于V-U
int min(minedgeArray L, int n)
{
int min_index, i;
Adjmatrix min = FLT_MAX;
//先假设第一个元素既是最小值
min_index = -1;
for (i = 0; i < n; i++)
{
if (L[i].lowcost > 0 && L[i].lowcost < min)
{
min = L[i].lowcost;
min_index = i;
}
}
return min_index;
}
// 最小生成树,普里姆方法
// 参数:
// MGraph G 图G
// VertexType u 出发顶点
// int n 顶点总数
// 时间复杂度:
// O(n^2)
// 返回:
// void
void MininumSpanningTreeMGraphUsePrim(MGraph G, VertexType u, int n)
{
int k, v, j, a;
minedgeArray minedge;
k = VertexIndex(G, u, n); // 取顶点u在辅助数组中的下标
for (v = 0; v < n; v++) // 辅助数组初始化
{
if (v != k)
{
minedge[v].ver = u;
minedge[v].lowcost = G.arcs[k][v];
} // End If
} // End For
for (a = 1; a < n; a++)
{
if (minedge[a].lowcost != FLT_MAX)
printf("minedge[%d].ver=%c, minedge[%d].lowcost=%4.0f\n", a, minedge[a].ver, a, minedge[a].lowcost);
}
printf("%c\n", u);
minedge[k].lowcost = 0; // 初始,U={u}
for (j = 1; j < n; j++) // 选择其余的n-1个顶点
{
k = min(minedge, n); // 1<=j<=n-1,找一个满足条件的最小边(u,k),u属于U,k属于V-U
printf("k=%d, v=%c\n", k, G.vexs[k]);
minedge[k].lowcost = 0; // 第k个顶点并入U
for (v = 0; v < n; v++)
{
if (G.arcs[k][v] < minedge[v].lowcost) // 重新选择最小边
{
minedge[v].ver = G.vexs[k];
minedge[v].lowcost = G.arcs[k][v];
} // End If
} // End For
for (a = 1; a < n; a++)
{
if (minedge[a].lowcost != FLT_MAX && minedge[a].lowcost > 0)
printf("minedge[%d].ver=%c, minedge[%d].lowcost=%4.0f\n", a, minedge[a].ver, a, minedge[a].lowcost);
}
} // End For
}
6.3.2 例6.3 普里姆方法最小生成树
利用普里姆算法,给出求图6.17(a)所示的无向图网络的最小生成树的过程。
#include <stdio.h>
#include "Graph.h"
int main(void)
{
const int n = 6;
const int e = 10;
int i, j;
VertexType v1, v2;
MGraph G;
VertexType v[n], vi;
Edge edge[e];
printf("开始输入顶点数=%d 边数=%d\n", n, e);
printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);
printf("开始输入边信息(顶点1,顶点2,权值):\n");
getchar();
for (i = 0; i < e; i++)
{
printf("%d: ", i + 1);
scanf("%c,%c,%f", &v1, &v2, &edge[i].weight);
for (j = 0; j < n; j++)
{
if (v[j] == v1)
{
edge[i].v1 = j;
break;
}
}
for (j = 0; j < n; j++)
{
if (v[j] == v2)
{
edge[i].v2 = j;
break;
}
}
getchar();
}
printf("开始创建无向图...\n");
CreateMGraph(&G, v, n, edge, e);
printf("创建完成,开始输出结果...\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if(G.arcs[i][j] == FLT_MAX)
printf(" ∞");
else
printf("%4.0f", G.arcs[i][j]);
}
printf("\n");
}
printf("输入最小生成树开始结点:"); scanf("%c", &vi);
printf("开始最小生成树 普里姆方法:\n");
MininumSpanningTreeMGraphUsePrim(G, vi, n);
printf("\n输出完成.\n");
return 0;
}
6.3.3 克鲁斯卡尔算法
typedef struct // 定义树的结点
{
VertexType node1; // 树的结点1
Adjmatrix w; // 结点1到结点2的权
VertexType node2; // 树的结点1
}TreeEdge;
typedef struct // 定义最小树
{
TreeEdge edgs[MaxVertexNum * (MaxVertexNum - 1) / 2]; // 定义树边最大的容量(按无向边定义)
VertexType vexs[MaxVertexNum]; // 定义树节点容器
unsigned int vexNum; // 定义结点数量
unsigned int edgNum; // 定义边数量
}Tree;
typedef struct // 定义森林
{
Tree trees[MaxVertexNum]; // 定义森林里树容器
unsigned int treeNum; // 定义森林里树的数量
}Forest;
//-----------------------------------------------------------
// 初始化树
// 参数:
// Tree *T 树
// 返回:
// void
//-----------------------------------------------------------
void initTree(Tree *T)
{
T->edgNum = 0; // 边数=0
T->vexNum = 0; // 结点数=0
}
//-----------------------------------------------------------
// 初始化森林
// 参数:
// Forest *F 森林
// 返回:
// void
//-----------------------------------------------------------
void initForest(Forest *F)
{
int i;
F->treeNum = 0; // 树木数=0
for (i = 0; i < MaxVertexNum; i++) // 遍历所有可能的结点
initTree(&F->trees[i]); // 初始化每一棵树
}
//-----------------------------------------------------------
// 获得节点在森林里第一次出现的所在树的索引号
// 参数:
// Forest *F 森林
// VertexType u 结点
// 返回:
// void
//-----------------------------------------------------------
int getTreeIndex(Forest *F, VertexType u)
{
int i, j;
for (i = 0; i < MaxVertexNum; i++) // 遍历所有可能的树
{
for (j = 0; j < F->trees[i].vexNum; j++) // 遍历每一棵树的结点列表
{
if (u == F->trees[i].vexs[j]) // 如果列表里有这个结点,则立即返回该树所在森林的索引
return i;
}
}
return -1; // 没有找到就返回-1
}
//-----------------------------------------------------------
// 添加一个节点到树
// 参数:
// Tree *T 数
// VertexType u 结点
// 返回:
// void
//-----------------------------------------------------------
void addVexToTree(Tree *T, VertexType u)
{
T->vexs[T->vexNum++] = u; // 设置结点,并使结点数量+1
}
//-----------------------------------------------------------
// 从森林里删除一棵树
// 参数:
// Forest *F 森林
// int treeIndex 删除树所在森林的索引
// 返回:
// void
//-----------------------------------------------------------
void removeTreeFromForest(Forest *F, int treeIndex)
{
F->trees[treeIndex].edgNum = 0; // 设置边数=0
F->trees[treeIndex].vexNum = 0; // 设置结点数=0
F->treeNum--; // 设置森林里树木棵树-1
}
//-----------------------------------------------------------
// 添加一条边到树
// 参数:
// Tree *T 树
// TreeEdge e 边
// 返回:
// void
//-----------------------------------------------------------
void addEdgeToTree(Tree *T, TreeEdge e)
{
T->edgs[T->edgNum].node1 = e.node1; // 结点1
T->edgs[T->edgNum].node2 = e.node2; // 结点2
T->edgs[T->edgNum++].w = e.w; // 权,随后将边的数量+1
}
//-----------------------------------------------------------
// 在森林里合并两棵树
// 参数:
// Forest *F 森林
// int treeIndexA 合并目树的索引
// int treeIndexB 合并源树的索引
// TreeEdge e 两者联系的边
// 返回:
// void
//-----------------------------------------------------------
void unionTree(Forest *F, int treeIndexA, int treeIndexB, TreeEdge e)
{
Tree *treeA, *treeB;
int i;
treeA = &F->trees[treeIndexA]; // 提取出树A
treeB = &F->trees[treeIndexB]; // 提取出树B
// 将B树所有边合并到A树
for (i = 0; i < treeB->edgNum; i++)
addEdgeToTree(treeA, treeB->edgs[i]);
// 将B树所有结点合并到A树
for (i = 0; i < treeB->vexNum; i++)
addVexToTree(treeA, treeB->vexs[i]);
// A树的边+e
addEdgeToTree(treeA, e);
// 从森林中删除B树
removeTreeFromForest(F, treeIndexB);
}
//-----------------------------------------------------------
// 带有权的边排序,按权的大小排序
// 参数:
// Edge edge[] 边列表
// int e 边的数量
// 返回:
// void
//-----------------------------------------------------------
void orderEdgeAsc(Edge edge[], int e)
{
int i, j;
Edge eTemp; // 过渡比较用的边
for (i = 0; i < e - 1; i++) // 以下为冒泡排序法,按权从小到大排列
{
for (j = i + 1; j < e; j++)
{
if (edge[i].weight > edge[j].weight)
{
eTemp.v1 = edge[i].v1; eTemp.v2 = edge[i].v2; eTemp.weight = edge[i].weight;
edge[i].v1 = edge[j].v1; edge[i].v2 = edge[j].v2; edge[i].weight = edge[j].weight;
edge[j].v1 = eTemp.v1; edge[j].v2 = eTemp.v2; edge[j].weight = eTemp.weight;
}
}
}
}
//-----------------------------------------------------------
// 复制树
// 参数:
// Tree *source 原树
// Tree *dest 目的树
// 返回:
// void
//-----------------------------------------------------------
void copyTree(Tree *source, Tree *dest)
{
int i;
dest->edgNum = source->edgNum; // 复制边数
dest->vexNum = source->vexNum; // 复制结点数
for (i = 0; i < source->edgNum; i++) // 复制边列表
{
dest->edgs[i].node1 = source->edgs[i].node1; // 节点1
dest->edgs[i].node2 = source->edgs[i].node2; // 节点2
dest->edgs[i].w = source->edgs[i].w; // 权
}
for (i = 0; i < source->vexNum; i++) // 复制结点列表
{
dest->vexs[i] = source->vexs[i];
}
}
//-----------------------------------------------------------
// 添加一棵树到森林
// 参数:
// Forest *G 森林
// Tree *T 添加的树
// 返回:
// void
//-----------------------------------------------------------
void addTreeToForest(Forest *F, Tree *T)
{
copyTree(T, &(F->trees[F->treeNum++])); // 在森林末尾追加一棵树,并将森林树木棵树+1
}
//-----------------------------------------------------------
// 从森林转换到树,仅有唯一一棵树的情况下才能转换
// 参数:
// Forest *G 森林
// Tree *T 转换出来的树
// 返回:
// void
//-----------------------------------------------------------
void forestToTree(Forest *F, Tree *T)
{
int i;
if (F->treeNum != 1) return; // 只有在一棵树的情况下才能从森林转换成树
for (i = 0; i < MaxVertexNum; i++) // 找到唯一的那棵所在森林的索引
if (F->trees[i].vexNum > 0) break;
if (i >= MaxVertexNum) return; // 如果没有找到则返回
copyTree(&F->trees[i], T); // 将树复制出来
}
//-----------------------------------------------------------
// 最小生成树,克鲁斯卡尔方法
// 参数:
// MGraph G 图G
// VertexType u 出发顶点
// int n 顶点总数
// 时间复杂度:
// O(eloge)
// 返回:
// void
//-----------------------------------------------------------
void MininumSpanningTreeMGraphUseKruskal(Tree *T, MGraph G, Edge edge[], int n, int e)
{
int i, uIndex, vIndex;
VertexType u, v;
Forest forest;
Tree tree;
TreeEdge treeEdge;
//初始化森林,树木数量=0
initForest(&forest);
initTree(T);
// 生成一个仅含有顶点的无边森林,每个顶点是一棵树
for (i = 0; i < n; i++)
{
initTree(&tree);
addVexToTree(&tree, G.vexs[i]);
addTreeToForest(&forest, &tree);
}
//按权值升序对边集E中的边进行排序,结果存入E[0..e-1]中
orderEdgeAsc(edge, e);
printf("第3步: 开始计算并将最小权值的边赋给结点:\n");
for (i = 0; i < e; i++)
{
//取第i条边(u,v)
u = G.vexs[edge[i].v1];
v = G.vexs[edge[i].v2];
treeEdge.node1 = u;
treeEdge.node2 = v;
treeEdge.w = edge[i].weight;
uIndex = getTreeIndex(&forest, u);
vIndex = getTreeIndex(&forest, v);
printf("\tv1=%c,v2=%c,w=%1.0f,uIndex=%d,vIndex=%d\t\t", u, v, edge[i].weight, uIndex, vIndex);
if (uIndex != vIndex) // u和v分别属于两棵不同的树
{
printf("并入\n");
unionTree(&forest, uIndex, vIndex, treeEdge);
} else
printf("忽略\n");
if (forest.treeNum == 1)// T 已经是一棵树
break;
}
forestToTree(&forest, T); // 将森林转换成树
}
6.3.4 克鲁斯卡尔算法程序示例
#include <stdio.h>
#include "Graph.h"
int main(void)
{
const int n = 6;
const int e = 10;
int i, j;
MGraph G;
Tree T;
VertexType v[n], vi;
Edge edge[e];
TreeEdge treeEdge;
printf("开始输入顶点数=%d 边数=%d\n", n, e);
printf("开始输入顶点信息,即顶点名称:");
v[0] = 'A'; v[1] = 'B'; v[2] = 'C'; v[3] = 'D'; v[4] = 'E'; v[5] = 'F';
edge[0].v1 = 0; edge[0].v2 = 1; edge[0].weight = 6;
edge[1].v1 = 0; edge[1].v2 = 2; edge[1].weight = 1;
edge[2].v1 = 0; edge[2].v2 = 3; edge[2].weight = 5;
edge[3].v1 = 1; edge[3].v2 = 2; edge[3].weight = 5;
edge[4].v1 = 1; edge[4].v2 = 4; edge[4].weight = 3;
edge[5].v1 = 2; edge[5].v2 = 3; edge[5].weight = 5;
edge[6].v1 = 2; edge[6].v2 = 4; edge[6].weight = 6;
edge[7].v1 = 3; edge[7].v2 = 5; edge[7].weight = 2;
edge[8].v1 = 4; edge[8].v2 = 5; edge[8].weight = 6;
edge[9].v1 = 2; edge[9].v2 = 5; edge[9].weight = 4;
printf("开始创建无向图...\n");
CreateMGraph(&G, v, n, edge, e);
printf("创建完成,开始输出结果...\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if(G.arcs[i][j] == FLT_MAX)
printf(" ∞");
else
printf("%4.0f", G.arcs[i][j]);
}
printf("\n");
}
j = VertexIndex(G, vi, n);
printf("开始最小生成树 克鲁斯卡尔算法:\n");
MininumSpanningTreeMGraphUseKruskal(&T, G, edge, n, e);
printf("开始输出树:\n");
for (i = 0; i < T.edgNum; i++)
{
treeEdge = T.edgs[i];
printf("节点:%c-(权:%2.0f)->节点:%c\n", treeEdge.node1, treeEdge.w, treeEdge.node2);
}
printf("输出完成.\n");
return 0;
}
6.4 最短路径
6.4.1 迪杰斯特拉算法
// 使用迪杰斯特拉算法求有向图G的startVertexIndex起始点索引的最短路径
// 参数:
// MGraph G 邻接矩阵G
// int startVertexIndex 起始节点索引号
// int n 结点总数量
// Adjmatrix distance[] 到各节点的最短距离数组
// bool pathMatrix[5][5] 路径矩阵(不考虑先后顺序)
// 返回:
// void
void MinPathMGraphUseDijkstra(MGraph G, VertexType v0, int n, Adjmatrix distance[], bool pathMatrix[5][5])
{
Adjmatrix min;
int v, i, w, a, startVertexIndex;
bool final[n];
startVertexIndex = VertexIndex(G, v0, n); // 获取起始点索引
for (v = 0; v < n; v++) // 初始化
{
final[v] = false; // 置空最短路径终点集
distance[v] = G.arcs[startVertexIndex][v]; // 置初始的最短路径值
for (w = 0; w < n; w++) // 设置空路径
pathMatrix[v][w]= false;
if (distance[v] < FLT_MAX)
{
pathMatrix[v][startVertexIndex] = true; // startVertex是v的前趋(双亲)
pathMatrix[v][v] = true;
}
}
distance[startVertexIndex] = 0; // fianl集初始时只有源点,源点到源点的距离为0
final[startVertexIndex] = true;
for (i = 1; i < n; i++) // 开始循环,每次求得v1到某个v定点的最短路径,并加v到final集中
{
min = FLT_MAX;
for (w = 0; w < n; w++) // 取得路径长度最小的顶点索引
{
if (!final[w] && distance[w] < min)
{
v = w; min = distance[w];
} // End If
} // End For w
final[v] = true; // 找到后,加到final集中
for (w = 0; w < n; w++) // 更新当前最短路径及距离
{
if(!final[w] && (min + G.arcs[v][w] < distance[w])) // 修改D[w]和P[w], w属于V-S
{
distance[w] = min + G.arcs[v][w];
for (a = 0; a < n; a++)
pathMatrix[w][a] = pathMatrix[v][a];
pathMatrix[w][w] = true; // P[w] = P[v] + [w]
} // End If
} // End For w
} // End For i
}
6.4.2 迪杰斯特拉算法程序示例
// 构造有向图
// 邻接矩阵表示法,有向图
// 参数:
// MGraph *G: 无向图
// VertexType v[]: 顶点一维数组列表
// int n: 图的顶点数
// Edge edge[]: 边一维数组列表
// int e: 图的边数
// 返回:
// void
void CreateMGraphDirection(MGraph *G, VertexType v[], int n, Edge edge[], int e)
{
int i, j, k, t;
Adjmatrix w; // 权值
for (i = 0; i < n; i++) // 写入顶点信息
G->vexs[i] = v[i];
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
G->arcs[i][j] = FLT_MAX; // 初始化邻接矩阵元素为无穷大
for (k = 0; k < e; k++) // 读入e条边,建立邻接矩阵
{
for (t = 0; t < e; t++) // 读取出边上的左右顶点序号和权值并写入到图中
{
i = edge[t].v1;
j = edge[t].v2;
w = edge[t].weight;
G->arcs[i][j] = w;
}
}
}
#include <stdio.h>
#include "Graph.h"
int main(void)
{
const int n = 5;
const int e = 7;
int i, j;
MGraph G;
VertexType v[n], startV;
Edge edge[e];
Adjmatrix distance[n + 1];
bool pathMatrix[n][n];
printf("开始输入顶点数=%d 边数=%d\n", n, e);
v[0] = 'A'; v[1] = 'B'; v[2] = 'C'; v[3] = 'D'; v[4] = 'E';
/* printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);*/
edge[0].v1 = 0; edge[0].v2 = 1; edge[0].weight = 10;
edge[1].v1 = 0; edge[1].v2 = 2; edge[1].weight = 3;
edge[2].v1 = 0; edge[2].v2 = 3; edge[2].weight = 20;
edge[3].v1 = 1; edge[3].v2 = 3; edge[3].weight = 5;
edge[4].v1 = 2; edge[4].v2 = 1; edge[4].weight = 2;
edge[5].v1 = 2; edge[5].v2 = 4; edge[5].weight = 15;
edge[6].v1 = 3; edge[6].v2 = 4; edge[6].weight = 9;
/* printf("开始输入边信息(顶点序号及权值):\n");
getchar();
for (i = 0; i < e; i++)
{
printf("%d: ", i + 1);
scanf("%d,%d,%f", &edge[i].v1, &edge[i].v2, &edge[i].weight);
getchar();
}*/
printf("开始创建有向图...\n");
CreateMGraphDirection(&G, v, n, edge, e);
printf("\n创建完成,开始输出结果...\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if(G.arcs[i][j] == FLT_MAX)
printf(" ∞");
else
printf("%4.0f", G.arcs[i][j]);
}
printf("\n");
}
startV = 'A';
printf("求最短路径,迪杰斯特拉算法,出发点: %c\n", startV);
MinPathMGraphUseDijkstra(G, startV, n, distance, pathMatrix);
printf("计算完成,最短路径为:\n");
for (i = 1; i < n; i++)
printf("distance[%d]=%3.0f\n", i, distance[i]);
printf("路径矩阵为: \n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
printf("%d ", pathMatrix[i][j]);
printf("\n");
}
return 0;
}
6.4.3 例6.4
#include <stdio.h>
#include "Graph.h"
int main(void)
{
const int n = 5;
const int e = 7;
int i, j;
MGraph G;
VertexType v[n], startV;
Edge edge[e];
Adjmatrix distance[n + 1];
bool pathMatrix[n][n];
printf("开始输入顶点数=%d 边数=%d\n", n, e);
v[0] = 'A'; v[1] = 'B'; v[2] = 'C'; v[3] = 'D'; v[4] = 'E';
/* printf("开始输入顶点信息,即顶点名称:");
for (i = 0; i < n; i++)
scanf("%c", &v[i]);*/
edge[0].v1 = 0; edge[0].v2 = 1; edge[0].weight = 9;
edge[1].v1 = 0; edge[1].v2 = 4; edge[1].weight = 18;
edge[2].v1 = 1; edge[2].v2 = 2; edge[2].weight = 5;
edge[3].v1 = 2; edge[3].v2 = 3; edge[3].weight = 3;
edge[4].v1 = 2; edge[4].v2 = 4; edge[4].weight = 2;
edge[5].v1 = 3; edge[5].v2 = 4; edge[5].weight = 2;
edge[6].v1 = 4; edge[6].v2 = 1; edge[6].weight = 5;
/* printf("开始输入边信息(顶点序号及权值):\n");
getchar();
for (i = 0; i < e; i++)
{
printf("%d: ", i + 1);
scanf("%d,%d,%f", &edge[i].v1, &edge[i].v2, &edge[i].weight);
getchar();
}*/
printf("开始创建有向图...\n");
CreateMGraphDirection(&G, v, n, edge, e);
printf("\n创建完成,开始输出结果...\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if(G.arcs[i][j] == FLT_MAX)
printf(" ∞");
else
printf("%4.0f", G.arcs[i][j]);
}
printf("\n");
}
startV = 'A';
printf("求最短路径,迪杰斯特拉算法,出发点: %c\n", startV);
MinPathMGraphUseDijkstra(G, startV, n, distance, pathMatrix);
printf("计算完成,最短路径为:\n");
for (i = 1; i < n; i++)
printf("distance[%d]=%3.0f\n", i, distance[i]);
printf("路径矩阵为: \n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
printf("%d ", pathMatrix[i][j]);
printf("\n");
}
return 0;
}
6.5 拓扑排序
6.5.1 拓扑排序方法
// 对用邻接表G表示的有向图进行拓扑排序
// 参数:
// ALGraph G 有向图
// int n 顶点数
// 返回:
// void
void TopuSort(ALGraph G, int n)
{
int i, j, m = 0; // m用来统计输出的顶点数
int inde[MaxVertexNum]; // 定义入度数组
SeqStack S;
EdgeNode *p, *stockItem;
for (i = 0; i < n; i++) // 初始化数组inde的每个元素值为0
inde[i] = 0;
for (i = 0; i < n; i++) // 扫描每个顶点vi的出边表,统计每个顶点的入度数
{
p = G[i].link;
if (p != NULL) p = p->next; // 出度表,第一个不含
while (p) {
inde[p->adjvex]++;
p = p->next;
}
}
InitStack(&S); // 初始化栈,并将入度为零的顶点i入栈
for (i = 0; i < n; i++)
{
if (inde[i] == 0)
{
stockItem = G[i].link;
Push(&S, stockItem);
}
}
while (!IsStackEmpty(&S)) // 拓扑排序开始
{
stockItem = Pop(&S); // 删除入度为0的顶点
i = stockItem->adjvex;
printf("C%d ", stockItem->adjvex + 1); // 输出一个顶点
m++;
p = G[i].link;
while (p) // 扫描顶点i的出边表
{
j = p->adjvex;
inde[j]--; // vj的入度减1,相当于删除边<vi,vj>
if (inde[j] == 0) // 若vj的入度为0,则入栈
Push(&S, p);
p = p->next;
}
}
}
6.5.2 拓扑排序程序示例
#include <stdio.h>
#include "Graph.h"
int main(void)
{
const int n = 9;
const int e = 11;
int i, j;
ALGraph G1, G2;
VertexType v[n];
EdgeNode *p;
printf("开始输入顶点数=%d 边数=%d\n", n, e);
v[0] = '1'; v[1] = '2'; v[2] = '3'; v[3] = '4'; v[4] = '5';
v[5] = '6'; v[6] = '7'; v[7] = '8'; v[8] = '9';
printf("初始化邻接表...\n");
InitVertex(G1, n, v); // 出邻接表
InitVertex(G2, n, v); // 入邻接表
printf("初始化完成,开始输入顶点序号及权值:\n");
AddDirectedGraphNode(G1, G2, 0, 3, 1);
AddDirectedGraphNode(G1, G2, 0, 6, 1);
AddDirectedGraphNode(G1, G2, 3, 2, 1);
AddDirectedGraphNode(G1, G2, 3, 7, 1);
AddDirectedGraphNode(G1, G2, 2, 7, 1);
AddDirectedGraphNode(G1, G2, 2, 4, 1);
AddDirectedGraphNode(G1, G2, 6, 2, 1);
AddDirectedGraphNode(G1, G2, 1, 6, 1);
AddDirectedGraphNode(G1, G2, 1, 8, 1);
AddDirectedGraphNode(G1, G2, 8, 5, 1);
AddDirectedGraphNode(G1, G2, 5, 4, 1);
printf("创建完成,开始输出有向图---邻接表(出边表):\n");
for (i = 0; i < n; i++)
{
printf("(%c)-->", G1[i].vertex);
p = G1[i].link;
while (p != NULL)
{
printf("C%d", p->adjvex + 1); // 输出节点
if (p->next != NULL)
{
printf("-->");
}
p = p->next;
}
printf("^\n");
}
printf("开始进行拓扑排序...\n");
TopuSort(G1, n);
printf("\n排序完成.\n");
return 0;
}
输出结果: