kruskal最小生成树,邻接表

zedzhao 2008-11-29 05:22:53


#define MAX_VERTEX_NUM 20
#define VISITED 1
#define UNVIST 0

typedef char VertexType;
typedef int InfoType;

#define OK 1
#define ERROR 0
#define NFOUND -1
#define DG 0 //有向图
#define UDG 1 //无向图


typedef struct ArcNode //弧节点的结构
{
int adjvex; //该弧指向的顶点的位置(下标)
struct ArcNode *nextarc;
InfoType *info;

}ArcNode;

typedef struct VNode //顶点节点的结构
{
VertexType data;
ArcNode *firstarc;

}VNode,AdjList[MAX_VERTEX_NUM];

typedef struct //图的邻接表结构定义
{
AdjList vertex;
int vexnum,arcnum;
int kind;

}ALGraph;


int LocateVex(ALGraph G,VertexType v) //返回v节点在图中的位置,即下标.
{
int i;
for(i=0 ; i<G.vexnum ; ++i)
if( v == G.vertex[i].data)
return i;
return NFOUND;
}


void CreatG(ALGraph &G)
{
ArcNode *p;
int i,j,k;
int isWeight;
VertexType v1,v2;

cout <<"enter vernum:"; cin>>G.vexnum; //输入顶点个数
cout <<"enter arcnum:"; cin>>G.arcnum; //输入弧的数目
cout <<"enter graph kind:(0-DG ; 1-UDG):"; cin>>G.kind; //输入创建图的类型
cout <<"创建的图是否带有权值?(0-不带,1-带):"; cin>>isWeight;

cout << endl;

for(i=0; i <G.vexnum ; ++i) //输入每个顶点的数据
{
cout << "enter vertex["<<i+1<<"]:";
cin >>G.vertex[i].data;
G.vertex[i].firstarc = NULL;
}

for(k=0; k <G.arcnum ; ++k) //建图
{
cout << "#####enter origin vertex:"; cin>>v1; //起点
cout << "enter destination vertex:"; cin>>v2; //终点
i = LocateVex(G,v1); j = LocateVex(G,v2);

p = new ArcNode;
p->adjvex = j;
if(isWeight) //如果带权值就输入该弧的权值
{
p->info = new InfoType;
cout << "############enter weight:";
cin >> *p->info;
}
p->nextarc = G.vertex[i].firstarc;
G.vertex[i].firstarc = p;

if(G.kind == UDG) //如果是无向图,建立逆向链接
{
p = new ArcNode;
p->adjvex = i;
p->nextarc = G.vertex[j].firstarc;
G.vertex[j].firstarc = p;
}
}

}//建图

求kruskal最小生成数算法,还有遍历该生成树的算法.

...全文
677 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
zfdforever 2011-12-23
  • 打赏
  • 举报
回复
学习了,感谢楼主分享
pd031401 2010-06-24
  • 打赏
  • 举报
回复
厉害啊楼主~~~~膜拜
zedzhao 2008-12-03
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 Johnson_Ho 的回复:]
数据结构书和算法书上一般都会有这类问题的伪代码算法
你把那些伪代码改写一下
应该可以实现的!
如果你想看图形实现的话,
你可看看严蔚敏的数据结构的演示程序
[/Quote]


这算法道理挺简单的 ,谁都懂,但是代码实现起来还是有点问题,不过还是搞出来了


void MakeSet(ALGraph G,EdgeType *edge,int *parent) //将图中所有边的信息放在集合edge中
{

int i,j;
int *isPutEdge = new int[G.arcnum]; //对于无向图,防止重复读取相同的边
ArcNode *p;

for(i=0; i<G.vexnum ; ++i)
isPutEdge[i] = 1;

for(i=0,j=0 ; i<G.vexnum; ++i) //将图中所有的边的信息收入到edge中 类似MAKE-SET操作
{

p = G.vertex[i].firstarc;
while(p)
{
edge[j].vex1 = i;
if(isPutEdge[p->adjvex]) //判断vertex[p->adjvex]的所有连通量是否已被包含到edge中,防止取到相同的边
{
edge[j].vex2 = p->adjvex;
edge[j].weight = *(p->info);
++j;
}
p=p->nextarc;
}
isPutEdge[i] = 0; //与vertex[i]相关的边已被全部录入到edge中,
}
}

void Sort(EdgeType *edge,int n) //对集合edge中的元素按权值大小进行升序排列
{
int i,j;
EdgeType tmp;
for(i =1; i<n; ++i)
{
for(j=i; j>0; --j)
if(edge[j].weight < edge[j-1].weight)
{
tmp = edge[j];
edge[j] = edge[j-1];
edge[j-1] = tmp;
}
}
}

int FindSet(int *parent,int v)
{
if(parent[v] == -1)
return v;
else
FindSet(parent, parent[v]);
}

void Union(int *parent, int v1,int v2)
{
int i,j;

i = FindSet(parent,v1);
j = FindSet(parent,v2);

if(parent[i] == -1 && parent[j] == -1) //如果该边的两个顶点分属不同的树,则将这两颗树合并.
{
parent[j] = i;
}

else if(i!=j)
{
if(parent[v2] == -1) //判断顶点v2是否有为子树,若是则让v1成为v2的子树,否则v2为v1的子树
parent[v1] = v2;
else
parent[v2] = v1;
}


}

void print(ALGraph G,int *parent) //按层次遍历生成树的方式输出kruskal最小生成树
{
int i,j,k;
int *visited = new int[G.vexnum];
Queue Q;
InitQ(Q);

for(i=0; i<G.vexnum ; ++i)
visited[i] = UNVIST;

for(i=0 ; i<G.vexnum ; ++i)
if(parent[i] == -1) break;

EnQueue(Q,i);
while(!IsEmpty(Q))
{
for(j=0 ; j<G.vexnum ; ++j)
{
if(parent[j] == i)
if(!visited[j])
EnQueue(Q,j);
}
DeQueue(Q,k);
if(!visited[k])
{
i = k;
cout << G.vertex[k].data<<" ";
visited[k] = VISITED;
}

}
}


void Kruskal(ALGraph G)
{
int *parent = new int[G.vexnum];
EdgeType *edge = new EdgeType[G.arcnum]; //存放边的信息

int i,j,k,n;
ArcNode *p;
for(i=0 ; i<G.vexnum ; ++i)
parent[i] = -1;

MakeSet(G,edge,parent); //建立边的集合edge;
Sort(edge,G.arcnum); //对集合edge按照权值大小进行升序排列;

for(i=0; i< G.arcnum; ++i)
{
k = FindSet(parent,edge[i].vex1);
j = FindSet(parent,edge[i].vex2);
if( k!= j)
{
Union(parent,edge[i].vex1, edge[i].vex2); //将边edge[i]收入到子树中
cout << G.vertex[edge[i].vex1].data <<"---" <<G.vertex[edge[i].vex2].data <<" weight="
<< edge[i].weight<<endl; //输出边的2个顶点及权值
}
}
cout <<endl<<endl;
print(G,parent);

}

Union()里面加了将2颗树合并起来的判断,....
明天结帖,谢谢1楼和7楼,给了些提示
xiaopoy 2008-12-03
  • 打赏
  • 举报
回复
纯帮顶。
nicholasfly 2008-12-03
  • 打赏
  • 举报
回复
俺是来学习的
cattycat 2008-12-02
  • 打赏
  • 举报
回复
这个经典算法数据结构和算法课本上有吧,抄一下,看看弄懂。
罗曼零 2008-12-02
  • 打赏
  • 举报
回复
数据结构书和算法书上一般都会有这类问题的伪代码算法
你把那些伪代码改写一下
应该可以实现的!
如果你想看图形实现的话,
你可看看严蔚敏的数据结构的演示程序
zedzhao 2008-12-02
  • 打赏
  • 举报
回复


搞的差不多了,还有一点小问题...

typedef struct //最小生成树用到的,记录顶点和之间的权值
{
int vex1; //2个顶点
int vex2;
int weight; //边的权值

}EdgeType;


void MakeSet(ALGraph G,EdgeType *edge,int *parent) //将图中所有边的信息放在集合edge中
{

int i,j;
int *isPutEdge = new int[G.arcnum]; //对于无向图,防止重复读取相同的边
ArcNode *p;

for(i=0; i<G.vexnum ; ++i)
isPutEdge[i] = 1;

for(i=0,j=0 ; i<G.vexnum; ++i) //将图中所有的边的信息收入到edge中 类似MAKE-SET操作
{

p = G.vertex[i].firstarc;
while(p)
{
edge[j].vex1 = i;
if(isPutEdge[p->adjvex]) //判断vertex[p->adjvex]的所有连通量是否已被包含到edge中,防止取到相同的边
{
edge[j].vex2 = p->adjvex;
edge[j].weight = *(p->info);
++j;
}
p=p->nextarc;
}
isPutEdge[i] = 0; //与vertex[i]相关的边已被全部录入到edge中,
}
}

void Sort(EdgeType *edge,int n) //对集合edge中的元素按权值大小进行升序排列
{
int i,j;
EdgeType tmp;
for(i =1; i<n; ++i)
{
for(j=i; j>0; --j)
if(edge[j].weight < edge[j-1].weight)
{
tmp = edge[j];
edge[j] = edge[j-1];
edge[j-1] = tmp;
}
}
}

int FindSet(int *parent,int v)
{
if(parent[v] == -1)
return v;
else
FindSet(parent, parent[v]);
}

void print(ALGraph G,int *parent) //才用层次遍历生成树的方式输出
{
int i,j,k;
int *visited = new int[G.vexnum];
Queue Q;
InitQ(Q);

for(i=0; i<G.vexnum ; ++i)
visited[i] = UNVIST;

for(i=0 ; i<G.vexnum ; ++i)
if(parent[i] == -1) break;

EnQueue(Q,i);
while(!IsEmpty(Q))
{
for(j=0 ; j<G.vexnum ; ++j)
{
if(parent[j] == i)
if(!visited[j])
EnQueue(Q,j);
}
DeQueue(Q,k);
if(!visited[k])
{
i = k;
cout << G.vertex[k].data<<" ";
visited[k] = VISITED;
}

}

}

void Kruskal(ALGraph G)
{
int *parent = new int[G.vexnum];
EdgeType *edge = new EdgeType[G.arcnum]; //存放边的信息

int i,j,k,n;
ArcNode *p;
for(i=0 ; i<G.vexnum ; ++i)
parent[i] = -1;

MakeSet(G,edge,parent); //建立边的集合edge;
Sort(edge,G.arcnum); //对集合edge按照权值大小进行升序排列;

for(i=0; i< G.arcnum; ++i)
{
k = FindSet(parent,edge[i].vex1);
j = FindSet(parent,edge[i].vex2);
if( k!= j)
{
if(parent[edge[i].vex2] == -1) //核心部分 这里怎么写才合适?
parent[edge[i].vex2] = edge[i].vex1;
else
parent[edge[i].vex1] = edge[i].vex2;

cout << G.vertex[edge[i].vex1].data <<"---" <<G.vertex[edge[i].vex2].data <<" weight="
<< edge[i].weight<<endl; //输出边的2个顶点及权值

}
}
cout <<endl<<endl;
print(G,parent);

}

如果建立这样的一个图 : a (3) - b (5) -d
a (7) - c (4) -d
a (6) -d
无相图,()里面的是权值,我用上面的代码跑下来的结果是
------------------
a---b weight=3
c---d weight=4
b---d weight=5
a---d weight=6

这个图生成最小树的话 只要3 4 5这3条边就够了,可是又把6这条边收进去了, 怎么改我上面的代码才可以?
zedzhao 2008-12-02
  • 打赏
  • 举报
回复


搞的差不多了,还有一点小问题...

typedef struct //最小生成树用到的,记录顶点和之间的权值
{
int vex1; //2个顶点
int vex2;
int weight; //边的权值

}EdgeType;


void MakeSet(ALGraph G,EdgeType *edge,int *parent) //将图中所有边的信息放在集合edge中
{

int i,j;
int *isPutEdge = new int[G.arcnum]; //对于无向图,防止重复读取相同的边
ArcNode *p;

for(i=0; i<G.vexnum ; ++i)
isPutEdge[i] = 1;

for(i=0,j=0 ; i<G.vexnum; ++i) //将图中所有的边的信息收入到edge中 类似MAKE-SET操作
{

p = G.vertex[i].firstarc;
while(p)
{
edge[j].vex1 = i;
if(isPutEdge[p->adjvex]) //判断vertex[p->adjvex]的所有连通量是否已被包含到edge中,防止取到相同的边
{
edge[j].vex2 = p->adjvex;
edge[j].weight = *(p->info);
++j;
}
p=p->nextarc;
}
isPutEdge[i] = 0; //与vertex[i]相关的边已被全部录入到edge中,
}
}

void Sort(EdgeType *edge,int n) //对集合edge中的元素按权值大小进行升序排列
{
int i,j;
EdgeType tmp;
for(i =1; i<n; ++i)
{
for(j=i; j>0; --j)
if(edge[j].weight < edge[j-1].weight)
{
tmp = edge[j];
edge[j] = edge[j-1];
edge[j-1] = tmp;
}
}
}

int FindSet(int *parent,int v)
{
if(parent[v] == -1)
return v;
else
FindSet(parent, parent[v]);
}

void print(ALGraph G,int *parent) //才用层次遍历生成树的方式输出
{
int i,j,k;
int *visited = new int[G.vexnum];
Queue Q;
InitQ(Q);

for(i=0; i<G.vexnum ; ++i)
visited[i] = UNVIST;

for(i=0 ; i<G.vexnum ; ++i)
if(parent[i] == -1) break;

EnQueue(Q,i);
while(!IsEmpty(Q))
{
for(j=0 ; j<G.vexnum ; ++j)
{
if(parent[j] == i)
if(!visited[j])
EnQueue(Q,j);
}
DeQueue(Q,k);
if(!visited[k])
{
i = k;
cout << G.vertex[k].data<<" ";
visited[k] = VISITED;
}

}

}

void Kruskal(ALGraph G)
{
int *parent = new int[G.vexnum];
EdgeType *edge = new EdgeType[G.arcnum]; //存放边的信息

int i,j,k,n;
ArcNode *p;
for(i=0 ; i<G.vexnum ; ++i)
parent[i] = -1;

MakeSet(G,edge,parent); //建立边的集合edge;
Sort(edge,G.arcnum); //对集合edge按照权值大小进行升序排列;

for(i=0; i< G.arcnum; ++i)
{
k = FindSet(parent,edge[i].vex1);
j = FindSet(parent,edge[i].vex2);
if( k!= j)
{
if(parent[edge[i].vex2] == -1) //核心部分 这里怎么写才合适?
parent[edge[i].vex2] = edge[i].vex1;
else
parent[edge[i].vex1] = edge[i].vex2;

cout << G.vertex[edge[i].vex1].data <<"---" <<G.vertex[edge[i].vex2].data <<" weight="
<< edge[i].weight<<endl; //输出边的2个顶点及权值

}
}
cout <<endl<<endl;
print(G,parent);

}

如果建立这样的一个图 : a (3) - b (5) -d
a (7) - c (4) -d
a (6) -d
无相图,()里面的是权值,我用上面的代码跑下来的结果是
------------------
a---b weight=3
c---d weight=4
b---d weight=5
a---d weight=6

这个图生成最小树的话 只要3 4 5这3条边就够了,可是又把6这条边收进去了, 怎么改我上面的代码才可以?
就呆在云上 2008-11-30
  • 打赏
  • 举报
回复
帮你贴一个树的生成和打印程序:
/*
Name:最小生成树kruskal算法
Author:wujilin
Description:用邻接矩阵做图
Date: 21-07-06 23:07
Copyright:wujilin
*/

#include<stdio.h>
#include<stdlib.h>
#define M 20
#define MAX 20

typedef struct
{
int begin;
int end;
int weight;
}edge;

typedef struct
{
int adj;
int weight;
}AdjMatrix[MAX][MAX];

typedef struct
{
AdjMatrix arc;
int vexnum, arcnum;
}MGraph;
void CreatGraph(MGraph *);//函数申明
void sort(edge* ,MGraph *);
void MiniSpanTree(MGraph *);
int Find(int *, int );
void Swapn(edge *, int, int);
void CreatGraph(MGraph *G)//构件图
{
int i, j,n, m;

printf(\"请输入边数和顶点数:\");
scanf(\"%d %d\",&G->arcnum,&G->vexnum);

for (i = 1; i <= G->vexnum; i++)//初始化图
{
for ( j = 1; j <= G->vexnum; j++)
{
G->arc[i][j].adj = G->arc[j][i].adj = 0;
}
}

for ( i = 1; i <= G->arcnum; i++)//输入边和权值
{
printf(\"\\n请输入有边的2个顶点\");
scanf(\"%d %d\",&n,&m);
while(n < 0 || n > G->vexnum || m < 0 || n > G->vexnum)
{
printf(\"输入的数字不符合要求 请重新输入:\");
scanf(\"%d%d\",&n,&m);
}

G->arc[n][m].adj = G->arc[m][n].adj = 1;
getchar();
printf(\"\\n请输入%d与%d之间的权值:\", n, m);
scanf(\"%d\",&G->arc[n][m].weight);
}

printf(\"邻接矩阵为:\\n\");
for ( i = 1; i <= G->vexnum; i++)
{
for ( j = 1; j <= G->vexnum; j++)
{
printf(\"%d \",G->arc[i][j].adj);
}
printf(\"\\n\");
}
}

void sort(edge edges[],MGraph *G)//对权值进行排序
{
int i, j;

for ( i = 1; i < G->arcnum; i++)
{
for ( j = i + 1; j <= G->arcnum; j++)
{
if (edges[i].weight > edges[j].weight)
{
Swapn(edges, i, j);
}
}
}

printf(\"权排序之后的为:\\n\");
for (i = 1; i < G->arcnum; i++)
{
printf(\"<< %d, %d >> %d\\n\", edges[i].begin, edges[i].end, edges[i].weight);
}

}

void Swapn(edge *edges,int i, int j)//交换权值 以及头和尾
{

int temp;

temp = edges[i].begin;
edges[i].begin = edges[j].begin;
edges[j].begin = temp;
temp = edges[i].end;
edges[i].end = edges[j].end;
edges[j].end = temp;
temp = edges[i].weight;
edges[i].weight = edges[j].weight;
edges[j].weight = temp;
}

void MiniSpanTree(MGraph *G)//生成最小生成树
{
int i, j, n, m;
int k = 1;
int parent[M];

edge edges[M];

for ( i = 1; i < G->vexnum; i++)
{
for (j = i + 1; j <= G->vexnum; j++)
{
if (G->arc[i][j].adj == 1)
{
edges[k].begin = i;
edges[k].end = j;
edges[k].weight = G->arc[i][j].weight;
k++;
}

}
}

sort(edges, G);
for (i = 1; i <= G->arcnum; i++)
{
parent[i] = 0;
}
printf(\"最小生成树为:\\n\");
for (i = 1; i <= G->arcnum; i++)//核心部分
{
n = Find(parent, edges[i].begin);
m = Find(parent, edges[i].end);
if (n != m)
{
parent[n] = m;
printf(\"<< %d, %d >> %d\\n\", edges[i].begin, edges[i].end, edges[i].weight);
}
}
}

int Find(int *parent, int f)//找尾
{
while ( parent[f] > 0)
{
f = parent[f];
}
return f;
}
帅得不敢出门 2008-11-30
  • 打赏
  • 举报
回复
up
houruifeng 2008-11-29
  • 打赏
  • 举报
回复
是这样的,
/* Kruskal.c
  Copyright (c) 2002, 2006 by ctu_85
  All Rights Reserved.
  */
  /* I am sorry to say that the situation of unconnected graph is not concerned */
  #include "stdio.h"
  #define maxver 10
  #define maxright 100
  int G[maxver][maxver],record=0,touched[maxver][maxver];
  int circle=0;
  int FindCircle(int,int,int,int);
  int main()
  {
  int path[maxver][2],used[maxver][maxver];
  int i,j,k,t,min=maxright,exsit=0;
  int v1,v2,num,temp,status=0;
  restart:
  printf("Please enter the number of vertex(s) in the graph:\n");
  scanf("%d",&num);
  if(num>maxver||num<0)
  {
  printf("Error!Please reinput!\n");
  goto restart;
  }
  for(j=0;j<num;j++)
  for(k=0;k<num;k++)
  {
  if(j==k)
  {
  G[j][k]=maxright;
  used[j][k]=1;
  touched[j][k]=0;
  }
  else
  if(j<k)
  {
  re:
  printf("Please input the right between vertex %d and vertex %d,if no edge exists please input -1:\n",j+1,k+1);
  scanf("%d",&temp);
  if(temp>=maxright||temp<-1)
  {
  printf("Invalid input!\n");
  goto re;
  }
  if(temp==-1)
  temp=maxright;
  G[j][k]=G[k][j]=temp;
  used[j][k]=used[k][j]=0;
  touched[j][k]=touched[k][j]=0;
  }
  }
  for(j=0;j<num;j++)
  {
  path[j][0]=0;
  path[j][1]=0;
  }
  for(j=0;j<num;j++)
  {
  status=0;
  for(k=0;k<num;k++)
  if(G[j][k]<maxright)
  {
  status=1;
  break;
  }
  if(status==0)
  break;
  }
  for(i=0;i<num-1&&status;i++)
  {
  for(j=0;j<num;j++)
  for(k=0;k<num;k++)
  if(G[j][k]<min&&!used[j][k])
  {
  v1=j;
  v2=k;
  min=G[j][k];
  }
  if(!used[v1][v2])
  {
  used[v1][v2]=1;
  used[v2][v1]=1;
  touched[v1][v2]=1;
  touched[v2][v1]=1;
  path[0]=v1;
  path[1]=v2;
  for(t=0;t<record;t++)
  FindCircle(path[t][0],path[t][0],num,path[t][0]);
  if(circle)
  {/*if a circle exsits,roll back*/
  circle=0;
  i--;
  exsit=0;
  touched[v1][v2]=0;
  touched[v2][v1]=0;
  min=maxright;
  }
  else
  {
  record++;
  min=maxright;
  }
  }
  }
  if(!status)
  printf("We cannot deal with it because the graph is not connected!\n");
  else
  {
  for(i=0;i<num-1;i++)
  printf("Path %d:vertex %d to vertex %d\n",i+1,path[0]+1,path[1]+1);
  }
  return 1;
  }
  int FindCircle(int start,int begin,int times,int pre)
  { /* to judge whether a circle is produced*/
  int i;
  for(i=0;i<times;i++)
  if(touched[begin]==1)
  {
  if(i==start&&pre!=start)
  {
  circle=1;
  return 1;
  break;
  }
  else
  if(pre!=i)
  FindCircle(start,i,times,begin);
  else
  continue;
  }
  return 1;
  }
zedzhao 2008-11-29
  • 打赏
  • 举报
回复
能不能给出具体的代码啊,想了很久了,
不知道如何判断回路,还有如果是无向图的话,存放边的时候还要考虑会不会重复放入相同边,
等等 还有许多未发现的问题,冥思苦想ing
哪位高手帮帮忙啊,
200分结帖.
cslxj 2008-11-29
  • 打赏
  • 举报
回复
ZhengZhiRen 2008-11-29
  • 打赏
  • 举报
回复
mark
星羽 2008-11-29
  • 打赏
  • 举报
回复

69,382

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧