数据结构——无向图的最小生成树算法(Prim&Kruskal)

#include<iostream>
#include<time.h>
#include<Windows.h>
#include< queue>
using namespace std;
#define NUM 100
#pragma warning(disable:4996)
typedef struct link
{
	int index;
	int distance;
	struct link *next;
}*Node, node;
typedef struct Link
{
	char word;
	Node first;
}*Head, head;
typedef struct g
{
	head linktable[NUM];
	int n, e;
}*Map, map;
void Allocatemap(Map &G);//给图指针分配一块图的专属区域
void Parmmap(Map G);//传入邻接表参数
void Createmap(Map G);//创建邻接表表头
int Locatev(Map G, char x);//确定关系顶点的位置
void Relatemap(Map G);//创建图的关系链
void Printmap(Map G);//打印图的邻接表
void Findminpath(Map G, int v[], int visited[]);//寻找已经确定结点中所有相邻结点中非环路的最短路径
void Prim(Map G, int v[], int visited[]);//Prim算法
void Findmintree(Map G, Map M, int visited[]);//寻找最小的树枝
int Correctpath(Map M, int i, int j); //进行合格路线的判定,满足两个条件即为合格:①未重复选取②未构成环路
void CreateMap(Map M, int i, int j);//生成树的插入
void Kruskal(Map G, int visited[]);//Kruskal算法
int main()
{
	int visited[NUM] = { 0 };
	int v[NUM] ;
	memset(v, -1, sizeof(v));//v数组是Prim算法的记录工具,全部赋值为-1是因为存放邻接表的数组下标是从0开始的
	Map G;
	Allocatemap(G);
	Parmmap(G);
	Createmap(G);
	Relatemap(G);
	Printmap(G);
	Prim(G, v, visited);
	Kruskal(G, visited);
	return 0;
}
void Allocatemap(Map &G)//给图指针分配一块图的专属区域
{
	G = new map;
}
void Parmmap(Map G)//传入邻接表参数
{
	cout << "请输入图的基本参数,即图的顶点数和边数:";
	cin >> G->n;
	cin >> G->e;
}
void Createmap(Map G)//创建邻接表表头
{
	char a[NUM];
	cout << "请输入顶点的具体数据:";
	cin >> a;
	for (int i = 0; i < G->n; i++)
	{
		G->linktable[i].word = a[i];
		G->linktable[i].first = NULL;
	}
}
int Locatev(Map G, char x)//确定关系顶点的位置
{
	int i;
	for (i = 0; i < G->n; i++)
	{
		if (G->linktable[i].word == x) return i;
	}
	return -1;
}
void Relatemap(Map G)//创建图的关系链
{
	int j = 0, k = 0;
	char a, b;
	int c = 0;
	cout << "请输入图的关系以及相应带权:" << endl;
	for (int i = 0; i < G->e; i++)
	{
		cin >> a >> b >> c;
		j = Locatev(G, a);
		k = Locatev(G, b);
		Node p = new node;
		p->index = k;
		p->distance = c;
		p->next = G->linktable[j].first;
		G->linktable[j].first = p;
	}
}
void Printmap(Map G)//打印图的邻接表
{
	cout << "图的邻接表为:" << endl;
	for (int i = 0; i < G->n; i++)
	{
		Node p = G->linktable[i].first;
		cout << G->linktable[i].word << "--->";
		while (p)
		{
			cout << G->linktable[p->index].word << "  ";
			cout << p->distance;
			p = p->next;
			if (p)cout << "-->";

		}
		cout << endl;
	}
}
void Findminpath(Map G, int v[],int visited[])//寻找已经确定结点中所有相邻结点中非环路的最短路径
{
	Node p;
	int outset=0;
	int end=0;
	int i = 0;
	int min = INT_MAX;
	do
	{
		p = G->linktable[v[i]].first;
		while (p)
		{
			if (p->distance < min && visited[p->index]==0)
			{
				min = p->distance;
				end = p->index;
				outset = v[i];
			}
			p = p->next;
		}
		i++;
	} while (v[i] != -1);
	v[i] = end;
	visited[end] = 1;
	cout << G->linktable[outset].word << "--->" << G->linktable[end].word <<"   "<<min<< endl;
}
void Prim(Map G, int v[],int visited[])//Prim算法
{
	char a;
	cout << "请选择Prim算法的开始顶点:";
	cin >> a;
	visited[Locatev(G, a)] = 1;
	v[0] = Locatev(G, a);
	cout << "Prim算法求得最小生成树为:" << endl;
	while (v[G->n - 1] == -1)
	Findminpath(G, v, visited);
}
void Findmintree(Map G,Map M,int visited[])//寻找最小的树枝
{
	Node p;
	int outset = 0;
	int end = 0;
	int min = INT_MAX;
	for(int i=0;i<G->n;i++)
	{ 
		p = G->linktable[i].first;
		while (p)//选取最短路径且不构成环路的线路
		{
			if (p->distance < min&&Correctpath(M, p->index,i) == 0)
			{
				min = p->distance;
				end = p->index;
				outset = i;
			}
			p = p->next;
		}
	}
	visited[outset] = 1;
	visited[end] = 1;
	CreateMap(M, outset, end);
	CreateMap(M, end, outset);
	cout << G->linktable[outset].word << "--->" << G->linktable[end].word << "   " << min << endl;
}
int Correctpath(Map M,int i,int j)//进行合格路线的判定,满足两个条件即为合格:①未重复选取②未构成环路
{
	Node p = M->linktable[i].first;
	Node q = M->linktable[j].first;
	while (p)
	{
		while (q)
		{
			if (q->index == p->index)return -1;
			else if(q->index == i || p->index == j)return -1;
			q = q->next;
		}
		p = p->next;
	}
	return 0;
}
void CreateMap(Map M, int i, int j)//生成树的插入
{
	Node p = new node;
	p->index = j;
	p->next = M->linktable[i].first;
	M->linktable[i].first = p;
}
void Kruskal(Map G, int visited[])//Kruskal算法
{
	Map M;
	Allocatemap(M);
	for(int i=0;i<G->n;i++)
	M->linktable[i].first = NULL;      //创建一个存储生成树的邻接表并初始化
	cout << "Kruskal算法求得最小生成树为:" << endl;
	for(int i=0;i<G->n-1;i++)
		Findmintree(G, M,visited);
}


在这里插入图片描述
上图左图是无向图演示示例的逻辑结构图,右图是对应图的邻接表
【小结】:①Prim算法求解无向图的核心思想是每一次寻找当前已确定结点与其他相邻结点的最短路径。与迪杰斯特拉算法求最短路径的核心思想“贪心”有异曲同工之妙,此算法虽然时间复杂度较高,但不用每次进行是否构成回路的判断,因为构成回路的前提就是下一个连接的结点一定在之前已经被访问过。此算法比较适用于求解稠密图的最小生成树。
下图是Prim算法过程演示示意图
在这里插入图片描述

②Kruskal算法求解无向图的核心思想是对所有路径长度进行一次排序,排序完成后依次从小到大选取不构成回路的路径,直至所有点被选取完毕。此算法比较适用于求解稀疏图的最小生成树。
下图是Kruskal算法过程演示示意图
在这里插入图片描述

下图是对应代码的运行结果
在这里插入图片描述

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值