目录
一、最小生成树
1.1 定义
1.2 性质
示意图:
二、Prim算法
2.1 基本思想
可以看出,Prim算法逐步向顶点集U中增加顶点, 故又称为“加点法”。
注意:选择最小边时,条件是边的一个顶点属于U,而另一个顶点属于V-U,即保证加点后不构成回路。在有多条同样权值的边可选时,可任选其一。
2.2 算法思想
说明:
下面给出一个实例。
CloseEdge[i].lowcost = 0 代表顶点 i 已被并入顶点集U中。
2.3 算法实现
定义数组CloseEdge[ ],用于记录从U到V-U的最小边。
typedef struct {
int adjvex;
int lowcost;
} CloseEdge[MAX_VERTEX_NUM];
Minium函数用于找出CloseEdge[ ]中最小的最小边。
int Minium(CloseEdge ce, int n) {
int i, j = -1;
int c = -1;
for (i = 0; i < n; ++i) /* n是图G的顶点个数 */
if (ce[i].lowcost != 0 && (c == -1 || c > ce[i].lowcost)) {
c = ce[i].lowcost;
/* j用于记录当前最小的最小边的顶点下标 */
j = i;
}
return j;
}
Prim算法。
void MiniSpanTree_Prim(Graph *G, VertexData k) {
int i, e;
int u, v;
CloseEdge ce;
/* 确定起始顶点的下标 */
u = LocateVertex(G, k);
/* 初始化数组ce */
ce[u].lowcost = 0;
for (i = 0; i < G->vexnum; ++i)
if (i != u) {
ce[i].adjvex = u;
ce[i].lowcost = G->arcs[u][i].adj;
}
/* 选出最小生成树的G->vexnum - 1条边 */
for (e = 1; e <= G->vexnum - 1; ++e) {
v = Minium(ce, G->vexnum);
u = ce[v].adjvex;
/* 打印得到的最小边 */
printf("(%s, %s, %d)\n",
Vertex2Name(G->vertex[u].data),
Vertex2Name(G->vertex[v].data),
ce[v].lowcost);
/* 将顶点v纳入集合U */
ce[v].lowcost = 0;
for (i = 0; i < G->vexnum; ++i)
/* 更新数组ce */
if (G->arcs[v][i].adj < ce[i].lowcost) {
ce[i].adjvex = v;
ce[i].lowcost = G->arcs[v][i].adj;
}
}
}
三、Kruskal算法
3.1 基本思想
可以看出,Kruskal算法逐步增加生成树的边(注意加边后不能构成回路),与Prim算法相比,可称为“加边法”。
3.2 算法思想
下面给出一个实例。
初始化。
选出第一条边。
选出第二条边。
选出第三条边。
以此类推,直到所有的顶点都在一个顶点集合内。
3.3 算法实现
定义边的类型;数组VertexSet用于记录顶点存储状态。
typedef struct {
int u, v;
int lowcost;
} Edge;
int VertexSet[MAX_VERTEX_NUM];
Minium函数用于找出图G中权值最小的边。
bool Minium(Graph *G, Edge *e) {
int i, j;
/* 清除使用痕迹 */
/* 因为e是指针,所以其中的数据会被带出函数 */
e->u = e->v = -1;
e->lowcost = INFINITY;
for (i = 0; i < G->vexnum; ++i)
for (j = 0; j < G->vexnum; ++j)
/* 要求顶点i和顶点j不能在同一个顶点集合内,否则会形成回路 */
if (VertexSet[i] != VertexSet[j] && e->lowcost > G->arcs[i][j].adjvex) {
e->lowcost = G->arcs[i][j].adjvex;
e->u = i;
e->v = j;
}
return e->u != -1;
}
Kruskal算法。
void MiniSpanTree_Kruskal(Graph *gn) {
Edge e;
int i, setid;
int u, v;
/* 初始化数组VertexSet */
for (i = 0; i < gn->vexnum; ++i)
VertexSet[i] = i;
/* 最大的集合编号 */
setid = i;
while (Minium(gn, &e)) {
/* 打印得到的最小边 */
printf("(%s, %s, %d)\n",
Vertex2Name(gn->vertex[e.u].data),
Vertex2Name(gn->vertex[e.v].data),
e.lowcost);
u = VertexSet[e.u];
v = VertexSet[e.v];
/* 更新顶点集合状态 */
for (i = 0; i < gn->vexnum; ++i)
if (u == VertexSet[i] || v == VertexSet[i])
VertexSet[i] = setid;
++setid;
}
}
说明:
1、每合并得到一个新的顶点集合,其编号都设为当前setid的值。
2、if (u == VertexSet[i] || v == VertexSet[i]) 不仅更新顶点 u 和 v 的集合编号,同时也会更新与顶点 u 或 v 同属一个集合的顶点的集合编号。