最小生成树Prim算法笔记(C语言实现)
最小生成树书上的定义是:一个无向图G点最小生成树就的由该图的那些链接G的所有顶点的边构成的树,且总价值最低
所以,一个最小生成树,要具有原图的所有顶点,且这些顶点用一种方法连接起来,所有边的和具有最小的权值。
一个图:
它的最小生成树:
现在来看如何由Prim算法得到最小生成树
首先,把所有的边都去掉,把这个图上所有的点组成一个点集。可以任选一个初始点(这里选择第1个点作为初始点)。
在到与1相连的边里面,找一个权值最小的:
显然,在与1相连的2,4,1中1是最小的;所以1与4相连,把他们看成一个整体
再继续找与1——4这一个整体相连的权值最小的边:
//这里(1——2),(4——3)都是目前最小边,但他们添加进整体的顺序并不会影响最小生成树的结构(可能是因为这两条边没有连接同一个点),有些情况下一次添加时,如果有有多条最小边的,可能导致最小生成树发生变化。希望路过的大神可以解释一下。
发现有(1——2)是里面最小的,于是把2添加进来:
于是把(2——1——4)看成一个整体,继续从外部找与这个整体相连的边的最小的权值;
注意:这里是从外部找,所以(2——4)这一条边是不可以连接的,(1——2——4)是一个整体
所以,此时最小是(3——4)这一条边,继续添加进来,此时的生成树就变成了:
继续按照上面的思路执行下去:
继续下一个:
继续下一个:
这样就得到了了开头的最小生成树。
分享一下代码实现的思路:(图的结构由散列表来储存)
- 确定一个初始点,把他成一个整体
- 找到与这个整体相连的权值最小的的边,和与这个边相连的点
- 把这个点与原整体连接起来,看成一个新的整体
重复2,3步,直到将所有顶点添加进来。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct node;
typedef struct node * pNode;
struct node{
int num;
int power;
pNode next;
};
struct item{
int key;
int power;
int father;
int k2;
};
void hashAdd(pNode,pNode);
void show(pNode *,int);
int main()
{
pNode * list1;
int i,a,b,j,N;
pNode current,temp;
puts("input the number of element:");
scanf("%d",&N);
list1=(pNode *)malloc(sizeof(pNode)*N);
for(i=0;i<N;i++){
printf("Enter the element connected to the %d element",i+1);
list1[i]=(pNode)malloc(sizeof(struct node));
temp=list1[i];
temp->next=NULL;
temp->num=i+1;
while(scanf("%d",&a)==1){
current=(pNode)malloc(sizeof(struct node));
current->num=a;
scanf("%d",&b);
current->power=b;
current->next=NULL;
temp->next=current;
temp=current;
}
getchar();
}
show(list1,N);
//创建一个新的哈希表,用来储存新的生成子树;
pNode *list2;
list2=(pNode *)malloc(sizeof(struct node)*N);//list2来储存最小生成树
struct item dist[N];
for(i=0;i<N;i++){
dist[i].key=0;
dist[i].power=1000;
dist[i].father=1000;
dist[i].k2=0;
}
for(i=0;i<N;i++){
list2[i]=(pNode)malloc(sizeof(struct node));
list2[i]->num=i+1;
list2[i]->next=NULL;
}
dist[0].key=1;
pNode current1,current2;
int min,minPower,father;
static int Z=1;
do{
for(i=0;i<N;i++){ //先将目前点集外最小权值添加到数组
if(dist[i].key==1){//添加第i个点
current=list1[i]->next;
while(current!=NULL){
if(dist[current->num-1].key==0&&dist[current->num-1].power>current->power){
dist[current->num-1].power=current->power;
dist[current->num-1].father=i+1;
dist[current->num-1].k2=1;
}
current=current->next;
}
}
}
//元素添加进数组完成,找到最小的
puts("ss");
min=1;
minPower=dist[0].power;
for(i=1;i<N;i++){
if(dist[i].k2==1&&dist[i].power<minPower)
{
min=i+1;
minPower=dist[i].power;
father=dist[i].father;
}
}
//成功找到最小元素,添加进新的哈希图 ,最小的是min,权值是minPower,与他相连的数是father
dist[min-1].key=1; //正式入队
current1=(pNode)malloc(sizeof(struct node));
current2=(pNode)malloc(sizeof(struct node));
current1->num=min;
current1->power=minPower;
current2->num=father;
current2->power=minPower;
hashAdd(list2[father-1],current1);
hashAdd(list2[min-1],current2);
Z++;
for(i=0;i<N;i++){ //重置一下 k2,目的是将k2加入到整体中,将他与外部分开
dist[i].power=1000;
dist[i].father=1000;
dist[i].k2=0;
}
}while(Z<N);
show(list2,N);
}
void hashAdd(pNode head,pNode current)
{
pNode temp=head;
while(temp->next!=NULL)
temp=temp->next;
temp->next=current;
current->next=NULL;
}
void show(pNode *list,int N)
{
int i;
pNode current;
for(i=0;i<N;i++)
{
printf("%d -> ",list[i]->num);
current=list[i]->next;
while(current!=NULL){
printf("%d(%d) ",current->num,current->power);
current=current->next;
}
printf("\n");
}
}