Prim算法
Prim算法是通过连通网找最小生成树(mst)的算法
过程:
1.首先选取任意顶点作为树的根
2.遍历其他(不在树中)的顶点,对于每个顶点,找到一条到mst路径最短的边,这样,每个顶点都有一条通往mst的边(不是邻接的就设权值无限大)
这里路径的意思是两顶点的直接距离,不相邻的顶点,路径距离就是∞,否则就是这条边的权值
3.在所有的边中,找到权值最小的一条,将对应顶点加入到mst中
循环2,3(对于n个顶点,循环n-1次)
例如,求下图的最小生成树
首先,选择v0作为起点,其他顶点到mst的距离分别为[0, 4, ∞, ∞, 3, 1]
接着,重复上面步骤,各顶点到mst的最小距离为:[0, 4, 7, 8, 3, 0]
v2从(v2, v0) 和 (v2, v5)中选择了(v2, v5),与树的距离从原来的无穷大更新为7,同样,v3也是这样,从∞更新为8
从这些边中选择最小的非零边(v0, v4),将v4加入到mst中
…一直循环,最终结果如下:
代码实现
使用邻接表来存储图结构:
typedef struct EdgeNode{
int adjIndex; //邻接到下标为adjindex的顶点
WEIGHT weight;
struct EdgeNode * next;
}EdgeNode; //边节点
typedef struct VertexNode{
DataElement data;
int id;
EdgeNode* firstEdge;
}VertexNode; //顶点节点
typedef struct AdjList{
VertexNode* VerNode[MAXSIZE];
int verQuantity; //顶点数量
int edgeQuantity; //边数量
}AdjList;
创建最小生成树需要三个辅助的数组:
- bool isAdded[MAXSIZE]
- int lowcost[MAXSIZE]
- int adjVex[MAXSIZE]
isAdded储存某个定点是否已经加入到mst中
这个数组在邻接矩阵表示法中是没有的,因为邻接矩阵中可以用0和INFINITY表示顶点已经加入到mst中以及边权值为无穷大,而在邻接表表示法中只能通过指针指向NULL表示边的权值无穷大,因此需要一个辅助数组isAdded
lowcost储存某个结点到mst的最短路径
例如mst中有三个顶点A,B,C,此时一个非mst的顶点D到mst就有三条路径:(A,D),(B,D),(C,D),lowcost[D]储存了最短的一条(B,D)
adjVex储存mst中各个顶点中到其他顶点路径最短的那个顶点
比如上例中mst中各个顶点到顶点D最短的为B顶点,则adjVex[D]储存了顶点B
这三个数组是从算法思路上需要的,具体的实现需要根据实际代码修改
/** 普里姆算法生成最小生成树 */
void PrimCreat(AdjList* adj)
{
int min; //最小权值
int minIndex; //最小权值的边的下标
int isAdded[MAXSIZE] = {0};
EdgeNode* lowcost[MAXSIZE] = {NULL}; //保存边的信息,如果为NULL,则表示权值无穷大
VertexNode* adjVex[MAXSIZE] = {NULL}; //保存另一顶点信息 Vi 与 Vadjvex[i] 的权值为lowcost[i]
EdgeNode* temp = adj->VerNode[0]->firstEdge;
isAdded[0] = 1;
for(int i = 0; i < adj->verQuantity; i++) //两个数组的初始化
{
adjVex[adj->VerNode[i]->id] = adj->VerNode[0]; //都指向第一个顶点
if(temp)
{
lowcost[temp->adjIndex] = temp;
temp = temp->next;
}
}
for(int i = 0; i < adj->verQuantity - 1; i++)
{
min = INFINITY;
//首先找到lowcost中权值最小的边以及对应的非mst顶点下标
for(int k = 0; k < adj->verQuantity; k++)
{
if(lowcost[k] != NULL && lowcost[k]->weight < min)
{
min = lowcost[k]->weight;
minIndex = k;
}
}
//打印
printf("(%d, %d)%d - ", adjVex[minIndex]->id, minIndex, min);
isAdded[minIndex] = 1;
//minIndex这个顶点已经加入到mst中了,它与mst的距离为0了(这里没有另一种状态表示距离为0,只能指向NULL)
//如果没有isAdded这个数组,则不能区分lowcost中NULL是表示距离为0还是距离为无穷大
lowcost[minIndex] = NULL;
//更新lowcost数组
//mst中新加了一个顶点,所有的更新都与这个顶点相关,因此用与依附于这个顶点的所有边进行更新
temp = adj->VerNode[minIndex]->firstEdge;
while(temp)
{
//更新就是找到一条更短的路径,因此对方顶点不在mst中并且权值更小就可以更新
if(isAdded[temp->adjIndex] != 1 && (lowcost[temp->adjIndex] == NULL || temp->weight < lowcost[temp->adjIndex]->weight))
{
lowcost[temp->adjIndex] = temp;
adjVex[temp->adjIndex] = adj->VerNode[minIndex]; //同时更新adjVex数组
}
temp = temp->next;
}
}
}
具体的思路都写在注释里面了