typedef struct MGraph {
char Vex[MaxSize];//顶点(vertex)中数据
int Edge[MaxSize][MaxSize];//边
int vexnum, arcnum;//顶点数量和边的数量
}MGraph;
图中涉及到.和->的区别。
弧与边:弧:有向图中连接两个节点的媒介通常叫做“弧”。 边:无向图中连接两个节点的媒介通常叫做“边”。而arc表示弧的意思,希望可以对大家记录这几个单词有所帮助。
//边表结点
typedef struct ArcNode {
int adjvex;//边指向顶点的存储位置
struct ArcNode* nextarc;//指向下一个边表结点的指针域
}ArcNode;
//顶点结点
typedef struct VNode {
char data;//顶点中数据
ArcNode* firstarc;//指向第一个边表结点的指针域
}VNode;
//图
typedef struct AGraph {
VNode adjlist[MaxSize];//邻接表
int vexnum, arcnum;//顶点数量和边的数量
}AGraph;
用邻接表存储图,需要两部分,顶点和边,这里定义了两种结点,
顶点结点
:顶点结点用来存放顶点,包括数据域和指针域,其指针域用来指向这个顶点的第一条边
边表结点
:边表结点用来表示边之间的关系,也包括数据域和指针域,指针域指向该顶点结点指向边。
顶点结点的指针域是ArcNode类型,也就是边表结点类型,其指向第一个边表结点。
图中定义的是顶点结点类型的数组(数组中的每个元素是顶点结点类型)。
参数定义说明
:定义函数传参时,MGraph G是把邻接矩阵表示的图直接传进去了,而AGraph G是直接把邻接表表示的图传进去,但是这种情况比较少见,且主流的参考资料中,在邻接表存储的图中,使用的是AGraph* G,即传进去一个指针,这个指针指向这个图。用指针的方式,则取结构体中的内容时候,需要用->
。
void Func(AGraph* G) {
int a = G->vexnum;//a获取图的顶点数量
int b = G->arcnum;//b获取图的边的数量
printf("%c", G->adjlist[0].data);//打印第一个顶点结点的数据
ArcNode* p;//定义一个ArcNode类型的p指针;
p = G->adjlist[0].firstarc;//p初始时指向图的第一个顶点结点的指针域,也就是边表结点
printf("%c", G->adjlist[p->adjvex].data);//通过p指针指向的边表结点的数据域,打印顶点结点
}
上述代码是图的部分操作代码,这里尤其要注意用**.
还是->
**来表示结构体中内容。
void Func(MGraph& G1, AGraph* G2) {//MGraph表示邻接矩阵,AGraph表示邻接表
G1.vexnum = G2->vexnum;//给图的顶点数量赋值
G1.arcnum = G2->arcnum;//给图的边数量赋值
//初始化邻接矩阵
for (int i = 0; i < G2->vexnum; i++)
for (int j = 0; j < G2->vexnum; j++)
G1.Edge[i][j] = 0;
ArcNode* p;//定义遍历指针
for (int i = 0; i < G2->vexnum; i++) {
//先复制到邻接矩阵的顶点表中
G1.Vex[i] = G2->adjlist[i].data;//将顶点数据值存储在 Vex 数组中
//然后处理边,也就是填充二维数组
p = G2->adjlist[i].firstarc;//指针 p 指向顶点 i 的第一个边表结点
while (p != NULL) {//遍历顶点 i 的所有边表结点
G1.Edge[i][p->adjvex] = 1;//将邻接矩阵中边对应位置改为 1
p = p->nextarc;//指针 p 继续向后遍历
}
}
}
void Func(MGraph G1, AGraph * G2) {//MGraph表示邻接矩阵,AGraph表示邻接表
G2->vexnum = G1.vexnum;//给图的顶点数量赋值
G2->arcnum = G1.arcnum;//给图的边数量赋值
//邻接表的顶点结点的填充
for (int i = 0; i < G1.vexnum; i++) {//遍历存储顶点数据的 Vex 数组
G2->adjlist[i].data = G1.Vex[i];//将各顶点数据赋值给邻接表的顶点结点中
G2->adjlist[i].firstarc = NULL;//初始化顶点结点的指针域
}
ArcNode* p;//定义遍历指针
for (int i = 0; i < G1.vexnum; i++) {//遍历邻接矩阵
for (int j = 0; j < G1.vexnum; j++) {
if (G1.Edge[i][j] != 0) {//若两顶点存在边则需建立边表结点
p = (ArcNode*)malloc(sizeof(ArcNode));//创建边表结点
p->adjvex = j;//为边表结点数据域赋值,注意是j不是i
//将边表结点头插到对应位置
p->nextarc = G2->adjlist[i].firstarc;
G2->adjlist[i].firstarc = p;
}
}
}
}
对于邻接表转邻接矩阵:
①复制边和顶点数量,②初始化邻接矩阵,③填充顶点表和填充邻接矩阵在一个for循环里操作;
对于邻接矩阵转邻接表:
①复制边和顶点数量,②填充邻接表的顶点结点,③两个for循环遍历邻接矩阵找边创建结点头插。
BFS 为什么需要队列?
对于 BFS 算法,正如上面所说的,我们需要一层一层遍历所有的相邻结点。那么相邻结点之间的先后顺序如何确定?因此我们需要一个数据结构来进行存储和操作,需要使得先遍历的结点先被存储,直到当前层都被存储后,按照先后顺序,先被存储的结点也会被先取出来,继续遍历它的相邻结点。因此我们可以发现,这个需求不就是我们的队列吗,First In First Out (FIFO) 。因此对于 BFS 我们需要使用 Queue 这样的一个数据结构,来存储每一层的结点,同时**维护『先进先出 FIFO』**的顺序。
void BFS(MGraph G, int v, int visited[]) {//visited[]已经全部初始化为0了
Queue Q;//定义辅助队列 Q
InitQueue(Q);//初始化队列 Q
printf("%c", G.Vex[v]);//打印该顶点数据
visited[v] = 1;//更新遍历数组
EnQueue(Q, v);//将遍历顶点地址入队
while (!IsEmpty(Q)) {//队列不为空则继续循环
DeQueue(Q, v);//出队并让 v 接收出队顶点地址
for (int j = 0; j < G.vexnum; j++){//遍历邻接矩阵的第 v 行
if (G.Edge[v][j] == 1 && visited[j] == 0) {//寻找有边且未被遍历过的顶点
printf("%c", G.Vex[j]);//打印该顶点的数据
visited[j] = 1;//更新遍历数组
EnQueue(Q, j);//将该顶点地址入队
}
}
}
}
void Func(MGraph G, int v) {
int visited[G.vexnum];//定义遍历数组
for (int i = 0; i < G.vexnum; i++)//初始化遍历数组
visited[i] = 0;
BFS(G, v, visited);//从顶点 v 开始进行广度优先遍历
for (int i = 0; i < G.vexnum; i++)//检查图 G 是否有未被遍历到的顶点
if (visited[i] == 0)//若有未被遍历到的顶点则需再次执行 BFS 算法
BFS(G, i, visited);
}
void BFS(AGraph * G, int v, int visited[]) {
Queue Q;//定义辅助队列 Q
InitQueue(Q);//初始化辅助队列 Q
printf("%c", G->adjlist[v].data);//打印地址为 v 的顶点值
visited[v] = 1;//更新遍历数组
EnQueue(Q, v);//将遍历顶点地址入队
ArcNode* p;//定义遍历指针 p
while (!IsEmpty(Q)) {//队列不为空则需继续循环
DeQueue(Q, v);//出队并让 v 接收出队顶点地址
p = G->adjlist[v].firstarc;//p 指针指向顶点 v 的第一个边表结点
while (p != NULL) {//遍历顶点 v 的所有边表结点
if (visited[p->adjvex] == 0) {//判断遍历边表结点对应顶点是否被遍历过
printf("%c", G->adjlist[p->adjvex].data);//遍历其顶点
visited[p->adjvex] = 1;//更新遍历数组
EnQueue(Q, p->adjvex);//将此遍历顶点地址入队
}
p = p->nextarc;//遍历指针继续向后遍历
}
}
}
void Func(AGraph* G, int v) {
int visited[G->vexnum];//定义遍历数组
for (int i = 0; i < G->vexnum; i++)//初始化遍历数组
visited[i] = 0;
BFS(G, v, visited);//从顶点 v 开始进行广度优先遍历
for (int i = 0; i < G->vexnum; i++)//检查图 G 是否有未被遍历到的顶点
if (visited[i] == 0)//若有未被遍历到的顶点则需再次执行 BFS 算法
BFS(G, i, visited);
}
int Func(AGraph * G, int v, int visited[]) {//改写 BFS,最后一个出队顶点即为所求
for (int i = 0; i < G->vexnum; i++)//初始化遍历数组
visited[i] = 0;
Queue Q;//定义辅助队列 Q
InitQueue(Q);//初始化队列 Q
visited[v] = 1;//更新遍历数组
EnQueue(Q, v);//令遍历顶点地址入队
ArcNode* p;//定义遍历指针 p
while (!IsEmpty(Q)) {//队列不为空则继续循环
DeQueue(Q, v);//出队并让 v 接收出队顶点地址
p = G->adjlist[v].firstarc;//遍历指针 p 指向顶点 v 的第一个边表结点
while (p != NULL) {//遍历顶点 v 的所有边表结点
if (visited[p->adjvex] == 0) {//若遍历边表结点对应顶点还未被遍历过
visited[p->adjvex] = 1;//更新遍历数组
EnQueue(Q, p->adjvex);//将该遍历顶点地址入队
}
p = p->nextarc;//指针 p 继续向后遍历
}
}
return v;//返回最后一个出队顶点地址
}
void BFS_MIN_Distance(AGraph * G, int v, int visited[], int d[]) {
for (int i = 0; i < G->vexnum; i++) {//初始化遍历数组和距离数组
visited[i] = 0;
d[i] = INT_MAX;
}
Queue Q;//定义辅助队列 Q
InitQueue(Q);//初始化辅助队列 Q
ArcNode* p;//定义遍历指针
visited[v] = 1;//更新遍历数组
d[v] = 0;//单源顶点地址为 v,该顶点到自己的距离为 0
EnQueue(Q, v);//将该顶点地址入队
while (!IsEmpty(Q)) {//队列不为空则继续遍历
DeQueue(Q, v);//出队并让 v 接收出队顶点地址
p = G->adjlist[v].firstarc;//遍历指针 p 指向顶点 v 的第一个边表结点
while (p != NULL) {//遍历顶点 v 的所有边表结点
if (visited[p->adjvex] == 0) {//若遍历边表结点对应的顶点还未被遍历过
visited[p->adjvex] = 1;//更新遍历数组
d[p->adjvex] = d[v] + 1;//更新距离数组
EnQueue(Q, p->adjvex);//将遍历顶点地址入队
}
p = p->nextarc;//遍历指针继续向后遍历
}
}
}
void DFS(MGraph G, int v, int visited[]) {
printf("%c", G.Vex[v]);//打印顶点 v 的数据
visited[v] = 1;//更新遍历数组
for (int j = 0; j < G.vexnum; j++) {//遍历邻接矩阵的第 v 行
if (G.Edge[v][j] == 1 && visited[j] == 0)//寻找第一个有边且未被遍历过的顶点
DFS(G, j, visited);//递归打印顶点 j 的数据值
}
}
void Func(MGraph G, int v) {
int visited[G.vexnum];//定义遍历数组
for (int i = 0; i < G.vexnum; i++)//初始化遍历数组
visited[i] = 0;
DFS(G, v, visited);//从顶点 v 开始进行深度优先遍历
//非连通图的时候需要检查没有被遍历的顶点
for (int i = 0; i < G.vexnum; i++)//检查图 G 是否有未被遍历到的顶点
if (visited[i] == 0)//若有未被遍历到的顶点则需再次执行 DFS 算法
DFS(G, i, visited);
}
void DFS(AGraph * G, int v, int visited[]) {
printf("%c", G->adjlist[v].data);//打印顶点 v 的数据
visited[v] = 1;//更新遍历数组
ArcNode* p = G->adjlist[v].firstarc;//遍历指针 p 指向顶点 v 的第一个边表结点
while (p != NULL) {//遍历顶点 v 的所有边表结点
if (visited[p->adjvex] == 0)//寻找其未被遍历过的边表结点
DFS(G, p->adjvex, visited);//递归打印该顶点的数据值
p = p->nextarc;//遍历指针继续向后遍历
}
}
void Func(AGraph* G, int v) {
int visited[G->vexnum];//定义遍历数组
for (int i = 0; i < G->vexnum; i++)//初始化遍历数组
visited[i] = 0;
DFS(G, v, visited);//从顶点 v 开始进行深度优先遍历
for (int i = 0; i < G->vexnum; i++)//检查图 G 是否有未被遍历到的顶点
if (visited[i] == 0)//若有未被遍历到的顶点则需再次执行 DFS 算法
DFS(G, i, visited);
}
int Path_i_j(AGraph* G, int i, int j) {//此算法省略了 DFS 算法书写
int visited[G->vexnum];//定义遍历数组
for (int k = 0; k < G->vexnum; k++)//初始化遍历数组
visited[k] = 0;
DFS(G, i, visited);//从顶点 i 开始进行深度优先遍历,也可改为 BFS(G,i,visited)
if (visited[j] == 1)//若深度优先遍历执行结束后遍历数组中顶点 j 被遍历了
return 1;//则说明顶点 i 到顶点 j 有路径
else//若顶点 j 未被遍历到,则说明无路径
return 0;
}
int Func(AGraph* G) {//此算法省略了 DFS 算法的书写
int visited[G->vexnum];//定义遍历数组
for (int i = 0; i < G->vexnum; i++)//初始化遍历数组
visited[i] = 0;
int count = 0;//定义变量 count 负责记录连通分量的个数
for (int i = 0; i < G->vexnum; i++)//遍历辅助数组
if (visited[i] == 0) {//若存在未被遍历的顶点则以此顶点开始执行 DFS 算法
DFS(G, i, visited);//也可改为 BFS(G,i,visited)
count++;//每执行一次 DFS 算法连通分量个数就加一
}
return count;//最后返回 count 值即为连通分量个数
}
int IsTree(AGraph * G) {//此算法省略了 DFS 算法的书写
int visited[G->vexnum];//定义遍历数组
for (int i = 0; i < G->vexnum; i++)//初始化遍历数组
visited[i] = 0;
DFS(G, 0, visited);//遍历图 G,也可改为 BFS(G,0,visited)
for (int i = 0; i < G->vexnum; i++)//检查图 G 是否有未被遍历到的顶点
if (visited[i] == 0)//若有未遍历顶点,则图 G 不连通,不是树,返回 0
return 0;
if (G->arcnum == G->vexnum - 1)//若图 G 是连通图且满足边数等于顶点数减一
return 1;//则图 G 是树,返回 1
else//若图 G 是连通图但不满足边数等于顶点数减一,则不是树,返回 0
return 0;
}
void DFS(MGraph G, int v, int visited[]) {
for (int i = 0; i < G.vexnum; i++)//初始化遍历数组
visited[i] = 0;
Stack S;//定义辅助栈 S
InitStack(S);//初始化栈 S
printf("%c", G.Vex[v]);//打印顶点 v 的数据
visited[v] = 1;//更新遍历数组
Push(S, v);//将该顶点地址压入栈中
int j;
while (!IsEmpty(S)) {//栈不为空则需继续循环
GetTop(S, v);//让变量 v 接收栈顶顶点地址但不出栈
for (j = 0; j < G.vexnum; j++) {//遍历邻接矩阵的第 v 行
if (G.Edge[v][j] == 1 && visited[j] == 0)//寻找有边且还未被遍历的顶点
break;//找到第 v 行满足条件的顶点后,则直接跳出循环
}
if (j == G.vexnum)//若第 v 行没有满足条件的顶点,则出栈
Pop(S, v);
else {//若找到了第 v 行满足条件的顶点
printf("%c", G.Vex[j]);//打印其数据值
visited[j] = 1;//更新遍历数组
Push(S, j);//将该顶点地址压入栈中
}
}
}
void DFS(AGraph * G, int v, int visited[]) {
for (int i = 0; i < G->vexnum; i++)//初始化遍历数组
visited[i] = 0;
Stack S;//定义辅助栈 S
InitStack(S);//初始化栈 S
printf("%c", G->adjlist[v].data);//打印顶点 v 的数据
visited[v] = 1;//更新遍历数组
Push(S, v);//将该顶点地址值压入栈中
ArcNode* p;//定义遍历指针 p
while (!IsEmpty(S)) {//栈不为空则需继续循环
GetTop(S, v);//让变量 v 接收栈顶顶点地址但不出栈
p = G->adjlist[v].firstarc;//遍历指针 p 指向顶点 v 的第一个边表结点
while (p != NULL && visited[p->adjvex] == 1)//寻找边表结点中未遍历的顶点
p = p->nextarc;
if (p == NULL)//若 p 为空顶点 v 的所有的边表结点都已处理完毕
Pop(S, v);//顶点 v 地址出栈
else {//若指针 p 所指边表结点对应顶点还未遍历过
printf("%c", G->adjlist[p->adjvex].data);//打印其数据值
visited[p->adjvex] = 1;//更新遍历数组
Push(S, p->adjvex);//将该顶点地址压入栈中
}
}
}
邻接矩阵和邻接表的DFS非递归算法的第二个while循环中都需要判断是否整行都不符合往下遍历的条件。
void PrintPath(AGraph * G, int u, int v, int visited[], char path[], int d) {//d初始时为-1
d++;//变量 d 为路径数组索引
path[d] = G->adjlist[u].data;//从 u 开始寻找路径
visited[u] = 1;//更新遍历数组
if (u == v) {//若此时开始寻找顶点即为目标顶点则打印路径
for (int i = 0; i <= d; i++)//打印路径数组中存储的路径
printf("%c", path[i]);
visited[u] = 0;//回溯
return;
}
ArcNode* p = G->adjlist[u].firstarc;//深度优先遍历改写
while (p != NULL) {
if (visited[p->adjvex] == 0)
PrintPath(G, p->adjvex, v, visited, path, d);//当前顶点开始继续寻找路径
//上面的if判断如果成立,从顶点结点指向的边表结点开始继续递归,直到最上面的
p = p->nextarc;
}
visited[u] = 0;//更新遍历数组,回溯寻找是否有其它路径
}
//主函数
void Func(AGraph* G, int u, int v) {
int visited[G->vexnum];//定义遍历数组
char path[G->vexnum];//定义路径数组,这里可以不初始化,因为后面是先更新path数组,然后打印。
for (int i = 0; i < G->vexnum; i++)//初始化遍历数组
visited[i] = 0;
PrintPath(G, u, v, visited, path, -1);//调用函数输出两顶点的所有简单路径
}
大家可以参考下回溯法的模板
//一定要分成横纵两个方面思考回溯
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {//注意i=0,i=start的区别
处理节点;
backtracking(路径,选择列表); // 递归 注意(i)和(i++)的区别 后面会懂
回溯,撤销处理结果
}
}
for/while循环横向遍历,递归纵向遍历,回溯不断调整结果集
结构体定义
//边表结点
typedef struct ArcNode {
int adjvex;
struct ArcNode* nextarc;
}ArcNode;
//顶点结点
typedef struct VNode {
char data;
int indegree;//顶点结点结构体增加了一个记录入度的成员
ArcNode* firstarc;
}VNode;
typedef struct AGraph {
VNode adjlist[MaxSize];
int vexnum, arcnum;
}AGraph;
int Top(AGraph* G) {
int i = 0, count = 0;//i在入栈出栈时使用,定义变量 count 记录拓扑排序顶点的个数
ArcNode* p;//定义遍历指针 p
Stack S;//定义栈 S
InitStack(S);//初始化栈 S
for (; i < G->vexnum; i++)//遍历邻接表的顶点结点
if (G->adjlist[i].indegree == 0)//将入度为 0 的顶点压入栈中
Push(S, i);
while (!IsEmpty(S)) {//栈不为空则证明还有入度为 0 的顶点需要处理
Pop(S, i);//出栈,让变量 i 接收出栈顶点地址
printf("%c", G->adjlist[i].data);//打印出栈顶点数据值
count++;//记录打印过的顶点个数
//⭐将出栈的顶点指向的所有顶点的入度减一
p = G->adjlist[i].firstarc;//遍历指针 p 指向出栈顶点的第一个边表结点
while (p != NULL) {//遍历出栈顶点的所有边表结点
G->adjlist[p->adjvex].indegree--;//该顶点指向的顶点入度减一
if (G->adjlist[p->adjvex].indegree == 0)//判断入度减一后是否等于 0
Push(S, p->adjvex);//若等于 0 则压入栈中,等待处理
p = p->nextarc;//遍历指针继续遍历
}
}
if (count == G->vexnum)//若循环结束所有顶点都已打印,则拓扑排序成功
return 1;
else//若循环结束打印顶点数小于顶点个数,则该图有环,拓扑排序失败
return 0;
}
int Func(AGraph * G) {//函数功能:计算图 G 中连通分量个数
int visited[G->vexnum];//定义遍历数组
for (int i = 0; i < G->vexnum; i++)//初始化遍历数组
visited[i] = 0;
int count = 0;//定义变量 count 负责记录连通分量的个数
for (int i = 0; i < G->vexnum; i++)//遍历辅助数组
if (visited[i] == 0) {//若存在未被遍历的顶点则以此顶点开始执行 DFS 算法
DFS(G, i, visited);//此算法省略了 DFS 算法的书写
count++;//每执行一次 DFS 算法连通分量个数就加一
}
return count;//最后返回 count 值即为连通分量个数
}
int IsLoop(AGraph* G) {
int n = Func(G);//定义变量 n 记录图中连通分量个数
if (G->arcnum == G->vexnum - n)//若满足临界条件,则该无向图不存在环
return 0;
else//若边的数量大于临界条件,则该无向图存在环
return 1;
}
如何判断有向图中是否有环呢--拓扑排序
,拓扑排序失败,说明有环。
void Prim(MGraph G, int v) {
int visited[G.vexnum];//记录当前最小生成树中包含的顶点
int lowcost[G.vexnum];//记录当前最小生成树到其它各顶点的最短距离
for (int i = 0; i < G.vexnum; i++) {//初始化辅助数组
visited[i] = 0;
lowcost[i] = G.Edge[v][i];
}
printf("%d", G.Vex[v]);//打印当前加入最小生成树的顶点
visited[v] = 1;//更新辅助数组
int min, k;//定义变量 min 记录当前最小生成树到其它顶点的最小距离
for (int i = 0; i < G.vexnum - 1; i++) {//共需找顶点个数减一条边
min = INT_MAX;//每一次寻找前都需要初始化 min
for (int j = 0; j < G.vexnum; j++)//遍历记录距离的数组
if (visited[j] == 0 && lowcost[j] < min) {//寻找此时距离最小的边
min = lowcost[j];//min 记录最小生成树到其它顶点的最小距离
k = j;//k 记录最小权值边对应的顶点
}
printf("%d", G.Vex[k]);//打印当前加入最小生成树的顶点
visited[k] = 1;//更新辅助数组
for (int j = 0; j < G.vexnum; j++)//更新记录最小生成树到其它顶点距离的数组
if (visited[j] == 0 && G.Edge[k][j] < lowcost[j])
lowcost[j] = G.Edge[k][j];
}
}
typedef struct {//存储边的结构体
int s;//记录边的起点start
int e;//记录边的终点end
int weight;//记录边的权值
}edge;
int Find(int S[], int x) {//在并查集 S 中查找 x 所在集合的根
while (S[x] >= 0)
x = S[x];
return x;
}
void Kruskal(MGraph G) {
edge e[G.arcnum];//定义保存图中所有边的辅助数组
int k = 0;//记录数组下标
for (int i = 0; i < G.vexnum; i++)//①遍历邻接矩阵,将所有的边存储到辅助数组中
for (int j = i + 1; j < G.vexnum; j++) {
e[k].s = i;//边的起点
e[k].e = j;//边的终点
e[k].weight = G.Edge[i][j];//边的权值
k++;//继续处理下一个数组元素
}
//②递增排序
edge temp;//定义辅助变量 temp 保存待排序元素
for (int i = 1; i < G.arcnum; i++)//直接插入排序
if (e[i].weight < e[i - 1].weight) {
temp = e[i];
for (int j = i - 1; temp.weight < e[j].weight; j--)
e[j + 1] = e[j];
e[j + 1] = temp;
}
//③并查集操作
int S[G.vexnum];//定义数组存储并查集
int count = 0;//定义变量 count 记录符合要求边的数量
int start, end;//定义两变量分别保存边起点和终点所在集合的根
for (int i = 0; i < G.vexnum; i++)//初始化并查集数组
S[i] = -1;
for (int i = 0; i < G.arcnum; i++) {//遍历保存边的辅助数组
start = Find(S, e[i].s);//查找边的起点所在集合的根
end = Find(S, e[i].e);//查找边的终点所在集合的根
if (start != end) {//若边的起点和终点在不同集合,则此条边符合要求
S[start] = end;//将边的起点和终点合并为一个集合里
printf("%d - %d", e[i].s, e[i].e);//打印此边
count++;//更新计数变量
if (count == G.vexnum - 1)//保留边的数量等于顶点数减一则任务完成
break;
}
}
}
void Dijkstra(MGraph G, int v) {
int visited[G.vexnum];//记录各顶点是否找到最短路径,找到置为1,没找到置为0
int dist[G.vexnum];//记录目前源点到各顶点的最短距离
int path[G.vexnum];//记录目前源点到各顶点最短路径的前驱顶点
for (int i = 0; i < G.vexnum; i++)//初始化辅助数组
visited[i] = 0;
for (int i = 0; i < G.vexnum; i++) {//初始化辅助数组
dist[i] = G.Edge[v][i];
path[i] = v;
}
visited[v] = 1;//已确定源点 v 的最短路径
int min, k;//定义变量 min 记录目前未确定最短路径顶点中的最小距离,k对应最短路径的顶点
//已经确定了一个顶点,只需要在确定顶点数-1个顶点就好。
for (int i = 0; i < G.vexnum - 1; i++) {//每次循环确定一个顶点的最短路径
min = INT_MAX;//初始化记录最小值的变量 min
for (int j = 0; j < G.vexnum; j++) {//遍历记录距离的数组
if (visited[j] == 0 && dist[j] < min) {
min = dist[j];//变量 min 记录未确定最短路径顶点中的最小距离
k = j;//变量 k 记录最小距离对应的顶点
}
}
visited[k] = 1;//已确定顶点 k 的最短路径
for (int j = 0; j < G.vexnum; j++) {//更新距离数组
if (visited[j] == 0 && dist[k] + G.Edge[k][j] < dist[j]) {
dist[j] = dist[k] + G.Edge[k][j];//发现更短路径则更新距离数组
path[j] = k;//更新路径数组
}
}
}
}
void Floyd(MGraph G) {
int i, j, k;
int A[MaxSize][MaxSize];//定义二维数组记录两顶点间最短距离
int path[MaxSize][MaxSize];//定义二维数组记录最短路径中转点
for (i = 0; i < G.vexnum; i++) {//初始化两个二维数组
for (j = 0; j < G.vexnum; j++) {
A[i][j] = G.Edge[i][j];//根据顶点间的直连边计算初始顶点间最短距离
path[i][j] = -1;//初始时顶点间最短路径没有中转点
}
}
for (k = 0; k < G.vexnum; k++) {//变量 k 为此次循环的中转点
for (i = 0; i < G.vexnum; i++) {//遍历两个二维数组
for (j = 0; j < G.vexnum; j++) {
if (A[i][k] + A[k][j] < A[i][j]) {//中转点加入是否需要更新最短距离
A[i][j] = A[i][k] + A[k][j];//更新顶点 i 到 j 的最短距离
path[i][j] = k;//更新顶点 i 到 j 的最短路径中转点
}
}
}
}
}
void PrintPath(int u, int v, int A[][MaxSize], int path[][MaxSize]) {//打印最短路径
if (A[u][v] == INT_MAX)//顶点 u 到 v 没有路径
printf("顶点%d到顶点%d没有路径", u, v);
else if (path[u][v] == -1)//顶点 u 到 v 有直达路径
printf(" %d - %d", u, v);
else {//顶点 u 到 v 有中转路径
int mid = path[u][v];//定义变量 mid 记录顶点 u 到 v 的最短路径中转点
PrintPath(u, mid, A, path);//递归打印顶点 u 到中转点 mid 的最短路径
PrintPath(mid, v, A, path);//递归打印中转点 mid 到顶点 v 的最短路径
}
}