咕了无数天之后猛更一波。
其实是因为前两天忙于数据结构结课大作业,正好需要图的各种知识,所以暂时把树这一部分先跳过了。离期末还有一个月的感觉就是,我真的学不完辣!!!
数据结构大作业给我的感觉是盲整邻接表(其实还没太学明白但是硬着头皮写出来了…)然后我明白了之前被它困扰其实不是概念问题,道理大家都懂,只是我的C语言太惨不忍睹了…
经过这么将近十篇博客多多少少还是学懂了一些,弥补了C在指针啊,结构体啊这些地方的缺憾,总之慢慢来吧,鼓励一下自己!
废话不多说了,来分析一下这个最小生成树之普里姆(Prim)算法!
最小生成树其实是为了找遍历一张带权图里所有的结点的最短路径,老实说Prim算法并不好懂,我从《大话数据结构》看到小甲鱼的数据结构教学课程磕磕碰碰总算摸出点门道,帮助理解的话推荐小甲鱼数据结构b站视频的这一章,最后那个动图真的很好理解了,帮助很大!!!
直接用书上的图好了(懒)
首先,这个算法是利用邻接矩阵来保存图的边信息:
分段写一下核心算法:
- 定义两个数组,作用如注释;初始化数组第一个元素
int adjvex[MAXVEX]; //定义数组保存顶点下标
int lowcost[MAXVEX]; //保存相关顶点边权值
lowcost[0] = 0; //将v0顶点加入生成树(其实是任意的)
adjvex[0] = 0; //初始化第一个顶点下标
- 将目前根节点(v0)相关信息存入数组
for (i = 1; i < G->numVertexes; i++)
{
lowcost[i] = G->arc[0][i]; //作为初始化,先将v0顶点相关权值存入数组
adjvex[i] = 0;
}
- for循环,对于图中所有结点进行遍历
for (i = 1; i < G->numVertexes; i++)
{
min = INFINITE;
j = 1;
k = 0;
while (j < G->numVertexes) //循环所有顶点
{
if (lowcost[j] != 0 && lowcost[j] < min)
{
min = lowcost[j];
k = j;
}
j++;
}
printf("(%d, %d)", adjvex[k], k);
lowcost[k] = 0; //把选中的顶点权值设为0,表示已经经过了这个顶点
//对选中的这个顶点,初始化它的lowcost数组
for (j = 1; j < G->numVertexes; j++)
{
if (lowcost[j] != 0 && G->arc[k][j] < lowcost[j])
{
lowcost[j] = G->arc[k][j];
adjvex[j] = k;
}
}
}
通俗一点理解,可以这样思考:每经过一次遍历后,将已经入树的结点看做一个整体,其它顶点到“这个整体”的最短的路径标记下来,对应顶点进入树,再作为一个整体如此循环直至所有顶点都入树。
最后生成的最小生成树是这样的:
下面放上全部可运行代码:
#include <stdio.h>
#include <stdlib.h>
#define MAXVEX 9
#define INFINITE 65535
typedef char VertexType;
typedef int EdgeType;
typedef struct
{
EdgeType arc[MAXVEX][MAXVEX];
VertexType vexs[MAXVEX];
int numVertexes, numEdges; //顶点个数,边数目
} MGraph;
//初始化邻接矩阵
void InitialGraph(MGraph *G)
{
int i, j;
for (i = 0; i < MAXVEX; i++)
{
for (j = 0; j < MAXVEX; j++)
{
if (i != j) //初始化时,给所有边赋成极大值
{
G->arc[i][j] = INFINITE;
}
else //i和j相等时对应权值为0
{
G->arc[i][j] = 0;
}
}
}
}
//建立邻接矩阵储存图结构
void CreateGraph(MGraph *G)
{
int i = 0;
int x, y, data = 0;
char c;
printf("请输入顶点数目:");
scanf("%d", &G->numVertexes);
printf("请输入边的数目:");
scanf("%d", &G->numEdges);
printf("请依次输入顶点名称:\n");
getchar();
while ((c = getchar()) != '\n')
{
if (c == ' ')
continue;
else
{
G->vexs[i] = c;
i++;
}
}
printf("请依次输入两个顶点下标和之间的权值:\n");
for (i = 0; i < G->numEdges; i++)
{
scanf("%d,%d %d", &x, &y, &data);
G->arc[x][y] = data;
G->arc[y][x] = data;
}
printf("图建立完毕...\n");
}
//Prim算法生成最小生成树
void MiniSpanTree_Prim(MGraph *G)
{
int min, i, j, k;
int adjvex[MAXVEX]; //定义数组保存顶点下标
int lowcost[MAXVEX]; //保存相关顶点边权值
lowcost[0] = 0; //将v0顶点加入生成树(其实是任意的)
adjvex[0] = 0; //初始化第一个顶点下标
for (i = 1; i < G->numVertexes; i++)
{
lowcost[i] = G->arc[0][i]; //作为初始化,先将v0顶点相关权值存入数组
adjvex[i] = 0;
}
for (i = 1; i < G->numVertexes; i++)
{
min = INFINITE;
j = 1;
k = 0;
while (j < G->numVertexes) //循环所有顶点
{
if (lowcost[j] != 0 && lowcost[j] < min)
{
min = lowcost[j];
k = j;
}
j++;
}
printf("(%d, %d)", adjvex[k], k);
lowcost[k] = 0; //把选中的顶点权值设为0,表示已经经过了这个顶点
//对选中的这个顶点,初始化它的lowcost数组
for (j = 1; j < G->numVertexes; j++)
{
if (lowcost[j] != 0 && G->arc[k][j] < lowcost[j])
{
lowcost[j] = G->arc[k][j];
adjvex[j] = k;
}
}
}
}
int main()
{
MGraph G;
int i, j;
InitialGraph(&G);
CreateGraph(&G);
for (i = 0; i < MAXVEX; i++)
{
for (j = 0; j < MAXVEX; j++)
{
printf("%d\t", G.arc[i][j]);
}
printf("\n");
}
MiniSpanTree_Prim(&G);
return 0;
}
程序运行结果:
- 输入过程
- 打印邻接矩阵
- 打印最小生成树的边: