#Kruskal算法最小生成树
代码思路见注释和printf内容。
本代码在code::blocks 17.12中可以运行。
#include <stdio.h>
#include <stdlib.h>
#define MAX 64
typedef struct Graph{
char* vexs[MAX];//节点名称
int AdjMartix[MAX][MAX];//邻接矩阵
int vexnum;//节点数
int edgenum;
int visit[MAX];//每个节点是否访问过
int dist[MAX];//每个点到操作点的距离
}Graph;
typedef struct edgeweight{
int weight;
int i;
int j;
}Edg;
typedef struct Heap{
struct edgeweight* data;
int size;
int capacity;
}Heap;
///图:输出图
void output_graph(Graph* g){
printf("邻接矩阵输出-----\n");
printf(" ");
for(int i=0;i<g->vexnum;i++){
printf("%8s",g->vexs[i]);
}
printf("\n");
for(int i=0;i<g->vexnum;i++){
printf("%s ",g->vexs[i]);
for(int j=0;j<g->vexnum;j++){
if(g->AdjMartix[i][j]==16843009){
printf(" /");
continue;
}
printf("%8d",g->AdjMartix[i][j]);
}
printf("\n");
}
}
///图:创建一个模板图
Graph* create_sample_graph(void){
Graph* pG;
pG=(Graph*)malloc(sizeof(Graph));
memset(pG,1,sizeof(Graph));
pG->vexnum=7;
pG->edgenum=12;
pG->vexs[0]="1";
pG->vexs[1]="2";
pG->vexs[2]="3";
pG->vexs[3]="4";
pG->vexs[4]="5";
pG->vexs[5]="6";
pG->vexs[6]="7";
//邻接矩阵 横行表示与该点的相邻点
pG->AdjMartix[get_position(pG,"1")][get_position(pG,"2")]=2;
pG->AdjMartix[get_position(pG,"2")][get_position(pG,"1")]=2;
pG->AdjMartix[get_position(pG,"2")][get_position(pG,"5")]=10;
pG->AdjMartix[get_position(pG,"5")][get_position(pG,"2")]=10;
pG->AdjMartix[get_position(pG,"2")][get_position(pG,"4")]=3;
pG->AdjMartix[get_position(pG,"4")][get_position(pG,"2")]=3;
pG->AdjMartix[get_position(pG,"4")][get_position(pG,"5")]=7;
pG->AdjMartix[get_position(pG,"5")][get_position(pG,"4")]=7;
pG->AdjMartix[get_position(pG,"5")][get_position(pG,"7")]=6;
pG->AdjMartix[get_position(pG,"7")][get_position(pG,"5")]=6;
pG->AdjMartix[get_position(pG,"4")][get_position(pG,"7")]=4;
pG->AdjMartix[get_position(pG,"7")][get_position(pG,"4")]=4;
pG->AdjMartix[get_position(pG,"7")][get_position(pG,"6")]=1;
pG->AdjMartix[get_position(pG,"6")][get_position(pG,"7")]=1;
pG->AdjMartix[get_position(pG,"6")][get_position(pG,"4")]=8;
pG->AdjMartix[get_position(pG,"4")][get_position(pG,"6")]=8;
pG->AdjMartix[get_position(pG,"3")][get_position(pG,"6")]=5;
pG->AdjMartix[get_position(pG,"6")][get_position(pG,"3")]=5;
pG->AdjMartix[get_position(pG,"3")][get_position(pG,"4")]=2;
pG->AdjMartix[get_position(pG,"4")][get_position(pG,"3")]=2;
pG->AdjMartix[get_position(pG,"1")][get_position(pG,"3")]=4;
pG->AdjMartix[get_position(pG,"3")][get_position(pG,"1")]=4;
pG->AdjMartix[get_position(pG,"1")][get_position(pG,"4")]=1;
pG->AdjMartix[get_position(pG,"4")][get_position(pG,"1")]=1;
return pG;
}
///图:获得节点编号
int get_position(Graph* g,char* c){
int i,j;
for(i=0;i<g->vexnum;i++){
//printf(".");
if(strcmp(g->vexs[i],c)==0){
//printf("输入有效[%d]\n",i,c);
return i;
}
}
//printf("未找到\n");
return -1;
}
///图:初始化图
void initvisit(Graph* g){
for(int i=0;i<g->vexnum;i++){
g->visit[i]=0;
g->dist[i]=16843009;
}
}
///最小堆:创建空最小堆
Heap* create_heap(Graph* g){
Heap* New=malloc(sizeof(Heap));
New->capacity=g->edgenum;
New->data=malloc((g->edgenum+1)*sizeof(Edg));
New->data[0].weight=-10000;
New->size=0;
return New;
}
///最小堆:给最小堆插入新数
void insert(Heap* H,Edg x){
int i=++H->size;
Edg temp;
H->data[i]=x;
while(H->data[i/2].weight>=x.weight){
temp=H->data[i/2];
H->data[i/2]=H->data[i];//pdata[i/2]指的就是父节点!!
H->data[i]=temp;
i=i/2;
}
}
///最小堆:以完全二叉树的形式输出最小堆
void output_heap(Heap* H){
printf("\n------\n");
int limt=1;
int r=1;//总的计数值(1~size)
int i=1;//每层树的计数值(1~limt)
for(;;){
for(i=1;i<=limt;i++,r++){
printf("[%d](%d--%d)%d ",r,H->data[r].i,H->data[r].j,H->data[r].weight);
if(r==H->size){
printf("\n-------\n");
return;
}
}
printf("\n");
limt=limt*2;
}
}
///最小堆:输出并删除最小堆最小的的节点
Edg delete_min(Heap* H){
Edg res=H->data[1];
H->data[1]=H->data[H->size--];
Edg temp;
int child;
for(int i=1;i*2<H->size;){
if(H->data[2*i+1].weight<H->data[2*i].weight){
child=2*i+1;
}else{
child=2*i;
}
if((H->data[i].weight)>(H->data[child].weight)){
temp=H->data[i];
H->data[i]=H->data[child];
H->data[child]=temp;
}else{
break;
}
i=child;
}
return res;
}
///最小生成树:生成
void Kruskal(Graph* g){
Edg tempedge;
//创建一个最小堆
Heap* edgeheap=create_heap(g);
for(int i=0;i<g->vexnum;i++){
for(int j=i;j<g->vexnum;j++){
if(g->AdjMartix[i][j]!=16843009){
tempedge.i=i;
tempedge.j=j;
tempedge.weight=g->AdjMartix[i][j];
insert(edgeheap,tempedge);
//printf("插入(%d--%d)%d\n",i,j,g->AdjMartix[i][j]);
}
}
}
output_heap(edgeheap);
//堆已经建好,每次输出一组点,用并查集的思想,只收录非环路点
int parent[g->vexnum];
int jroot,iroot;
for(int i=0;i<g->vexnum;i++){
parent[i]=-1;
}
while(1){
tempedge=delete_min(edgeheap);//每次都找到权值最小的两组点:i,j
if(tempedge.weight==-10000){//如果出现哨兵,说明已经遍历完毕,可以退出
break;
}
printf("本轮%d(%d---%d)\n",tempedge.weight,tempedge.i,tempedge.j);
iroot=root(tempedge.i,parent);//找到i的最终父节点iroot
jroot=root(tempedge.j,parent);//找到j的最终父节点jroot
printf("iroot=%d,jroot=%d\n",iroot,jroot);
if(jroot!=iroot){//1 如果两个最终父节点不等,说明是两颗独立树,将他们合并
if(parent[tempedge.i]==-1){//哪个节点是独立的,就把这个独立节点附到已有的树上。因为已经有父节点的点不能有两个父节点。
parent[tempedge.i]=tempedge.j;
printf(">>parent[%d]=%d------------\n",tempedge.i,tempedge.j);
}else if(parent[tempedge.j]==-1){
parent[tempedge.j]=tempedge.i;
printf(">>parent[%d]=%d------------\n",tempedge.j,tempedge.i);
}
}else{//2 如果两个最终父节点相等,说明两点之间已经有其他回路,直接下一组节点
printf("%d与%d之间已有回路\n",tempedge.i,tempedge.j);
continue;
}
}
}
///并查集:找到x在parent树里的最终父节点
int root(int x,int* parent){
if(parent[x]==-1){//如果x是独立节点,其本身就是最终父节点
return x;
}else{
while(parent[x]!=-1){
x=parent[x];
}
return x;//否则,一直上溯到parent[x]=-1的x才是最终父节点,并把parent[x]设为向上追溯的级数
}
}
int main()
{
Graph* graph=create_sample_graph();
initvisit(graph);//初始化所有的visit节点
output_graph(graph);
Kruskal(graph);
}