最小生成树(邻接矩阵存图)

一.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;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值