一.Prim算法:
算法思想:可以想象一个二维表格,就是接下来代码中定义的closedge,包含某条边的左右端点以及他们的距离,就像下边这个表格,一开始初始化所有的点的adjvex为1,那个点到1的距离为他们在图中与1的距离,图中没有直接和1相连的就是正无穷
每次找到lowcost最小的点t,然后用它去更新没在集合里的点的lowcost(包括它自己),再把t加到集合里,这里要注意的是下边判断某个点是否在集合里的方式就是他的lowcost是否为0。
#include<bits/stdc++.h>
using namespace std;
#define MaxVertexNum 100 //顶点数目最大值
//typedef char VertexType; //顶点的数据类型
//typedef int EdgeType; //带权图中边上权值的数据类型
const int INF=0x3f3f3f3f;
typedef struct
{
char Vex[100]; //顶点表
int Edge[100][100]; //邻接矩阵,边表
int vexnum, edgenum; //图的顶点数和弧数
}MGraph;
unordered_map<char,int>mp;
/***********创建无向图************/
void create_Graph2(MGraph *G)
{
int i, j;
char start, endd; //边的起点序号、终点序号
int numV, numE;
int w; //边上的权值
char ch;
printf("请输入所创建无向图的顶点数和边数(用空格隔开):");
scanf("%d%d", &numV, &numE);
ch=getchar();//上面的顶点数和边数输入完成后,会按一次enter键,这里的ch用于"吸收"enter防止对下一步输入造成影响
G->vexnum = numV;
G->edgenum = numE;
//printf("\n");
//图的初始化
for (i = 0; i < G->vexnum; i++)
{
for (j = 0; j < G->vexnum; j++)
{
if (i == j)
G->Edge[i][j] = 0;
else
G->Edge[i][j] = 32767;
}
}
//顶点信息存入顶点表
for (i = 0; i < G->vexnum; i++)
{
printf("请输入第%d个顶点的信息:", i + 1);
//ch=getchar();
//scanf("%c", G->Vex[i]);
cin>>G->Vex[i];
mp[G->Vex[i]]=i;
ch=getchar();
}
//printf("\n");
//输入无向图边的信息
for (i = 0; i < G->edgenum; i++)
{
printf("请输入边的起点序号,终点序号,权值(用空格隔开):");
//scanf("%c %c %d", &start, &endd, &w);
cin>>start>>endd>>w;
ch=getchar();
G->Edge[mp[start]][mp[endd]] = w; //有向图只在这里不一样
G->Edge[mp[endd]][mp[start]] = w;
}
}
typedef struct
{
int adjvex;
int lowcost;
}closedge;
void Prim(MGraph G)
{
int v=0;//初始节点
closedge C[100];
int mincost = 0; //记录最小生成树的各边权值之和
//初始化
for (int i = 0; i < G.vexnum; i++)
{
C[i].adjvex = v;
C[i].lowcost = G.Edge[v][i];
}
cout << "最小生成树的所有边:"<< endl;
//初始化完毕,开始G.vexnum-1次循环
for (int i = 1; i < G.vexnum; i++)
{
int k;
int min = INF;
//求出与集合U权值最小的点 权值为0的代表在集合U中
for (int j = 0; j<G.vexnum; j++)
{
if (C[j].lowcost != 0 && C[j].lowcost<min)
{
min = C[j].lowcost;
k = j;
}
}
//输出选择的边并累计权值
cout << "(" << G.Vex[C[k].adjvex] << "," << G.Vex[k] <<") ";
mincost += C[k].lowcost;
//更新最小边
for (int j = 0; j<G.vexnum; j++)
{
if (C[j].lowcost != 0 && G.Edge[k][j]<C[j].lowcost)
{
C[j].adjvex = k;
C[j].lowcost= G.Edge[k][j];
}
}
}
cout << "最小生成树权值之和:" << mincost;
}
int main()
{
MGraph G;
create_Graph2(&G);
Prim(G);
system("pause");
return 0;
}
/*测试样例
6 10
a b c d e f
a b 6
a c 1
a d 5
b c 5
b e 3
c d 5
c e 6
c f 4
d f 2
e f 6
*/
2.Kruskal算法:
算法思想:先将图中所有的边按照权值大小从小到大排序,,枚举每一条边,如果该边的左右两个端点不在一个集合里,那就把他们加到一个集合里,这里会用到并查集,并查集的话,主要是有一段代码,就是这个find函数
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
kruskal的总代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
using namespace std;
const int N=110;
int n,m;//n个点,m条边
typedef struct node{
char a,b;
int w;
}Node;
Node W[N];
char jd[N];
unordered_map<char,int>mp;
queue<Node>q;
/*void quick_sort(Node W[],int l,int r)
{
if(l>=r) return;
int x=W[(l+r)/2].w,i=l-1,j=r+1;
while(i<j)
{
do i++;while(W[i].w<x);
do j--;while(W[j].w>x);
if(i<j) swap(W[i],W[j]);
}
quick_sort(W,l,j);
quick_sort(W,j+1,r);
}*/
bool cmp(Node A,Node B)
{
if(A.w!=B.w) return A.w<B.w;
else return A.a<B.a;
}
int p[N];
int find(int k)//并查集找是否在一个集合里
{
if(p[k]!=k) p[k]=find(p[k]);
return p[k];
}
void Kruskal(Node W[])
{
for(int i=1;i<=n;i++) p[i]=i;//初始化每一个人都是自己一个结点
for(int j=0;j<m;j++)
{
int k=mp[W[j].a],l=mp[W[j].b];
k=find(k);
l=find(l);
if(k!=l)//他俩不在一个集合里面
{
q.push(W[j]);//给他俩连一块
p[l]=k;//放到一个集合里
}
}
}
int main()
{
printf("请输入无向图有几个点,几条边:\n");
scanf("%d%d",&n,&m);
printf("请输入%d个结点的编号,注意要输入字母栓Q:\n",n);
for(int i=0;i<n;i++)
{
//scanf("%c",&jd[i]);//输入该图中所有的结点是什么abcdef,
cin>>jd[i];
mp[jd[i]]=i+1;//下标分别对应123456
}
printf("请输入%d条边都是哪几条边:\n",m);
for(int i=0;i<m;i++)
{
cin>>W[i].a>>W[i].b>>W[i].w;
}
//quick_sort(W,0,m-1);//快排的话不知道怎么当他们w相等时按照第一个字母在字母表中的顺序排
sort(W,W+m,cmp);//所以就用sort吧
Kruskal(W);
printf("输出最后的形成的权值最小的图的信息:\n");
while(q.size())
{
Node kk=q.front();
cout<<kk.a<<kk.b<<kk.w<<endl;
q.pop();
}
system("pause");
return 0;
}