重新写了最小生成树的两个经典算法—— prim 和 kruskal算法,之前写的Prim算法,是比较直接的 就是先让边的集合为空,然后在扫描最小边的过程中,也是根据该算法的定义来写的 是比较直接的写法 看这里 就是之前的Prim算法实现 ,这次实现的,个人比较喜欢。
思想是这样的:
(1)先在最小树的顶点集U={v},以和v相邻的所有边为候选边;
(2)重复以下步骤n-1次,使得其他n-1个顶点加入到U中:
1、从候选边中选出一条权值最小的,设该边在V-U中的顶点是vk,将vk加入U中,删除和vk相邻的边(程序中以一个标志来表示)
2、考察当前V-U中所有顶点vj,修改候选边:若(vk,vj)的权值小于原来和vj关联的候选变,就用后者作为候选边。
下面是代码:
//prim.h
#include <iostream>
#include <string>
#include <queue>
using namespace std;
const int MAX_VEX_NUM=20;
const int MAX_NUM=32767;
bool visited[20];
struct Tree_Edge
{
int star;
int end;
int weight;
};
class MGraph
{
private:
string vexs[MAX_VEX_NUM];
int arcs[MAX_VEX_NUM][MAX_VEX_NUM];
int vexnum;
int arcnum;
public:
int Get_VexNum()
{
return vexnum;
}
int Locate_Vex(string v)
{
for(int i=0;i<vexnum;i++)
if(vexs[i] == v)
return i;
return -1;
}
void Create_MG()
{
//构造无向图的邻接矩阵
int i,j,k;
cout<<"输入图的顶点数目(少于20): ";
cin>>vexnum;
while(vexnum>20)
{
cout<<"输入的顶点数超过20,请重新输入:";
cin>>vexnum;
}
cout<<"输入各个顶点名称:";
for(i=0;i<vexnum;i++)
cin>>vexs[i];
for(i=0;i<vexnum;i++)
for(j=0;j<vexnum;j++)
{
arcs[i][j]=MAX_NUM;
}
cout<<"输入边的总数:";
cin>>arcnum;
string v1,v2;
int w;
for(k=0;k<arcnum;k++)
{
cout<<"输入每条边对应的两个顶点和该边对应的权值:";
cin>>v1>>v2>>w;
i=Locate_Vex(v1);
j=Locate_Vex(v2);
while(i == -1 || j == -1)
{
cout<<"输入的顶点中有不符合要求的,重新输入:";
cin>>v1>>v2;
i=Locate_Vex(v1);
j=Locate_Vex(v2);
}
arcs[i][j]=w;
arcs[j][i]=w;
}
cout<<"无向网构造完成"<<endl;
}
void DFS_Traverse()
{
for(int i=0;i<vexnum;i++)
visited[i]=false;
for(i=0;i<vexnum;i++)
if(!visited[i])
DFS(i);
}
void DFS(int v)
{
visited[v]=true;
cout<<vexs[v]<<" ";
for(int j=0;j<vexnum;j++)
if(arcs[v][j] != MAX_NUM && !visited[j])
DFS(j);
}
void BFS_Traverse()
{
queue<int> q;
int u,w,v;
for(v=0;v<vexnum;v++)
visited[v]=false;
for(v=0;v<vexnum;v++)
{
if(!visited[v])
{
visited[v]=true;
cout<<vexs[v]<<" ";
q.push(v);
while(!q.empty())
{
u=q.front();
q.pop();
for(w=0;w<vexnum;w++)
{
if( (arcs[u][w] != MAX_NUM) && !visited[w])
{
visited[w]=true;
cout<<vexs[w]<<" ";
q.push(w);
}
}
}
}
}
}
void Prim_Min_Tree(int v,Tree_Edge c[])
{
int lowcast[MAX_NUM];
int closet[MAX_NUM];
int i,min,j,k;
for(i=0;i<vexnum;i++)
{
lowcast[i]=arcs[v][i];
closet[i]=v;
}
lowcast[v]=0;
for(i=1;i<vexnum;i++)
{
min=MAX_NUM;
for(j=0;j<vexnum;j++)
{
if(lowcast[j]!=0 && lowcast[j]<min)
{
min=lowcast[j];
k=j;
}
}
lowcast[k]=0; //标志该顶点已经是U中顶点
c[i].star=closet[k];
c[i].end=k;
c[i].weight=min;
//下面是修改lowcast和closet
for(j=0;j<vexnum;j++)
{
if(arcs[k][j]<lowcast[j])
{
lowcast[j]=arcs[k][j];
closet[j]=k;
}
}
}
cout<<"最小生成树 完成~~"<<endl;
}
void Print_Tree(Tree_Edge c[])
{
for(int i=1;i<vexnum;i++)
cout<<vexs[c[i].star]<<"-->"<<vexs[c[i].end]<<" : "<<c[i].weight<<endl;
}
};
#include "prim.h"
#include <iostream>
using namespace std;
int main()
{
MGraph G;
G.Create_MG();
cout<<"图的深度优先遍历为:";
G.DFS_Traverse();
cout<<endl;
cout<<"图的广度优先遍历为:";
G.BFS_Traverse();
cout<<endl;
string v;
int pos;
cout<<"输入建立最小树的起点的名称:";
cin>>v;
pos=G.Locate_Vex(v);
while(pos == -1)
{
cout<<"顶点不存在,重新输入:";
cin>>v;
pos=G.Locate_Vex(v);
}
Tree_Edge c[MAX_NUM];
G.Prim_Min_Tree(pos,c);
cout<<"最小树的边的组成如下所示:"<<endl;
G.Print_Tree(c);
return 0;
}
测试结果:
输入图的顶点数目(少于20): 6
输入各个顶点名称:v1 v2 v3 v4 v5 v6
输入边的总数:10
输入每条边对应的两个顶点和该边对应的权值:v1 v2 6
输入每条边对应的两个顶点和该边对应的权值:v1 v3 1
输入每条边对应的两个顶点和该边对应的权值:v1 v4 5
输入每条边对应的两个顶点和该边对应的权值:v2 v5 3
输入每条边对应的两个顶点和该边对应的权值:v2 v3 5
输入每条边对应的两个顶点和该边对应的权值:v3 v4 5
输入每条边对应的两个顶点和该边对应的权值:v3 v6 4
输入每条边对应的两个顶点和该边对应的权值:v4 v6 2
输入每条边对应的两个顶点和该边对应的权值:v3 v5 6
输入每条边对应的两个顶点和该边对应的权值:v5 v6 6
无向网构造完成
图的深度优先遍历为:v1 v2 v3 v4 v6 v5
图的广度优先遍历为:v1 v2 v3 v4 v5 v6
输入建立最小树的起点的名称:v1
最小生成树 完成~~
最小树的边的组成如下所示:
v1-->v3 : 1
v3-->v6 : 4
v6-->v4 : 2
v3-->v2 : 5
v2-->v5 : 3
Press any key to continue
kruskal算法的一个中心思想就是在构造最小树的过程中,不可以使新边和原来的边构成回路,这次写的和之前写的有点不一样的就是,排序时,用了快排,在判断新边是否会和现在的边构成回路时,用了并查集。这个算法也是并查集的一个经典的运用,之前写的那个,用了并查集思想,但是这次是写了一个并查集然后用的,比较明显,看起来也比较清晰。之前写的kruskul算法实现
下面是代码:
//并查集头文件
#ifndef UFSET_H_
#define UFSET_H_
#include <iostream>
using namespace std;
const int MAX_SIZE=100;
int parent[MAX_SIZE];
int rank[MAX_SIZE];
int Find(int x)
{
int y,root,w;
y=x;
while(parent[y]!=y)
{
y=parent[y];
}
root=y;
y=x;
while(parent[y]!=y) //执行路径压缩
{
w=parent[y];
parent[y]=root;
y=w;
}
return root;
}
void Union(int root1,int root2)
{
int u,v;
u=Find(root1);
v=Find(root2);
if(rank[u]<=rank[v])
{
parent[u]=v;
if(rank[u]==rank[v])
++rank[v];
}
else
parent[v]=u;
}
void Init()
{
memset(rank,0,sizeof(rank));
for(int i=1;i<MAX_SIZE;i++)
parent[i]=i;
}
#endif
//图的定义以及实现的头文件
#ifndef GRAPH_H_
#define GRAPH_H_
#include "ufset.h"
#include <iostream>
#include <string>
#include <queue>
using namespace std;
const int MAX_VEX_NUM=20;
const int MAX_NUM=32767;
bool visited[MAX_VEX_NUM];
struct Tree_Edge
{
int star;
int end;
int weight;
}c[MAX_NUM];
void Quick_Sort(int low,int high);
class MGraph
{
private:
string vexs[MAX_VEX_NUM];
int arcs[MAX_VEX_NUM][MAX_VEX_NUM];
int arcnum;
int vexnum;
public:
int Locate_Vex(string v)
{
for(int i=0;i<vexnum;i++)
if(vexs[i] == v)
return i;
return -1;
}
void Create_Graph()
{
int i,j,v1,v2,w;
string u,v;
cout<<"输入定点数目(少于20): ";
cin>>vexnum;
while(vexnum>20)
{
cout<<"请输入小于20的整数:";
cin>>vexnum;
}
cout<<"输入顶点名称:";
for(i=0;i<vexnum;i++)
cin>>vexs[i];
//初始化邻接矩阵
for(i=0;i<vexnum;i++)
for(j=0;j<vexnum;j++)
arcs[i][j]=MAX_NUM;
cout<<"输入边的总数:";
cin>>arcnum;
for(i=0;i<arcnum;i++)
{
cout<<"输入每一条边对应的两个顶点和该边权值:";
cin>>u>>v>>w;
v1=Locate_Vex(u);
v2=Locate_Vex(v);
while(v1 == -1 || v2 == -1)
{
cout<<"顶点中有不符合要求的,请重新输入:";
cin>>u>>v;
v1=Locate_Vex(u);
v2=Locate_Vex(v);
}
arcs[v1][v2]=w;
arcs[v2][v1]=w;
c[i].star=v1;
c[i].end=v2;
c[i].weight=w;
}
cout<<"无向网构造完成"<<endl;
}
void DFS_Traverse()
{
for(int i=0;i<vexnum;i++)
visited[i]=false;
for(i=0;i<vexnum;i++)
if(!visited[i])
DFS(i);
}
void DFS(int v)
{
visited[v]=true;
cout<<vexs[v]<<" ";
for(int i=0;i<vexnum;i++)
if(arcs[v][i]<MAX_NUM && !visited[i])
DFS(i);
}
void BFS_Traverse()
{
for(int i=0;i<vexnum;i++)
visited[i]=false;
for(i=0;i<vexnum;i++)
if(!visited[i])
BFS(i);
}
void BFS(int v)
{
visited[v]=true;
cout<<vexs[v]<<" ";
queue<int> qu;
int w;
qu.push(v);
while(!qu.empty())
{
w=qu.front();
qu.pop();
for(int i=0;i<vexnum;i++)
{
if(arcs[w][i]<MAX_NUM && !visited[i])
{
visited[i]=true;
cout<<vexs[i]<<" ";
qu.push(i);
}
}
}
}
void KrusKal_Min_Tree(int v,Tree_Edge c2[])
{
int i,j,k,u1,v1,sn1,sn2;
Init(); //初始化并查集
Quick_Sort(0,arcnum-1);
k=1;
j=0;
while(k<vexnum)
{
u1=c[j].star;
v1=c[j].end;
sn1=Find(u1); //并查集中查找u1的父亲
sn2=Find(v1);
if(sn1 != sn2)
{
c2[k]=c[j];
k++;
Union(u1,v1); //合并两个集合
}
j++;
}
cout<<"最小树构造完成"<<endl;
}
void Print_Tree(Tree_Edge c2[])
{
for(int i=1;i<vexnum;i++)
cout<<vexs[ c2[i].star ]<<"-->"<<vexs[ c2[i].end ]<<" : "<<c2[i].weight<<endl;
}
};
void Quick_Sort(int low,int high)
{
int i,j;
Tree_Edge t;
i=low;
j=high;
t=c[low];//把第一个元素作为基准
while(i<j)
{
while((i<j)&&(c[j].weight >= t.weight))
j--;
if(i<j)
c[i]=c[j];
i++;
while((i<j)&&(c[i].weight <= t.weight))
i++;
if(i<j)
c[j]=c[i];
j--;
}
c[i]=t;
if(low<high)
{
Quick_Sort(low,i-1);//排列t的左边部分
Quick_Sort(i+1,high);//排列右边部分
}
}
#endif
#include "graph.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
MGraph G;
G.Create_Graph();
cout<<"图的深度优先遍历为:";
G.DFS_Traverse();
cout<<endl;
cout<<"图的广度优先遍历为:";
G.BFS_Traverse();
cout<<endl;
string v;
int pos;
cout<<"输入建立最小树的起点的名称:";
cin>>v;
pos=G.Locate_Vex(v);
while(pos == -1)
{
cout<<"顶点不存在,重新输入:";
cin>>v;
pos=G.Locate_Vex(v);
}
Tree_Edge Edges[MAX_NUM];
G.KrusKal_Min_Tree(pos,Edges);
cout<<"最小树的边的组成如下所示:"<<endl;
G.Print_Tree(Edges);
return 0;
}
测试结果:
输入定点数目(少于20): 6
输入顶点名称:v1 v2 v3 v4 v5 v6
输入边的总数:10
输入每一条边对应的两个顶点和该边权值:v1 v2 6
输入每一条边对应的两个顶点和该边权值:v1 v3 1
输入每一条边对应的两个顶点和该边权值:v1 v4 5
输入每一条边对应的两个顶点和该边权值:v2 v3 5
输入每一条边对应的两个顶点和该边权值:v3 v4 5
输入每一条边对应的两个顶点和该边权值:v2 v5 3
输入每一条边对应的两个顶点和该边权值:v3 v5 6
输入每一条边对应的两个顶点和该边权值:v3 v6 4
输入每一条边对应的两个顶点和该边权值:v4 v6 2
输入每一条边对应的两个顶点和该边权值:v5 v6 6
无向网构造完成
图的深度优先遍历为:v1 v2 v3 v4 v6 v5
图的广度优先遍历为:v1 v2 v3 v4 v5 v6
输入建立最小树的起点的名称:v1
最小树构造完成
最小树的边的组成如下所示:
v1-->v3 : 1
v4-->v6 : 2
v2-->v5 : 3
v3-->v6 : 4
v2-->v3 : 5
Press any key to continue
下面是测试中生产的无向图 以及生产的最小树: