最小生成树【做题记录】c++(Prim,Kruskal)

目录

Prim算法求最小生成树

【算法思想】

【算法实现】

【数据结构设计】

【算法步骤】

【输入输出】

【代码示例】

Kruskal算法求最小生成树

【算法思想】

判断是否会产生回路的方法

【算法描述】

【图的存储结构】

【输入输出】

【代码示例】


Prim算法求最小生成树

【算法思想】

普里姆算法在找最小生成树时,将顶点分为两类,一类是在查找的过程中已经包含在树中的(设为 已选集合),剩下的是另一类(设为 待选集合)。

对于给定的连通网,起始状态全部顶点都归为 待选集合。在找最小生成树时,选定任意一个顶点作为起始点,并将之从 待选集合移至已选集合;然后找出 待选集合中到已选集合中的顶点之间权值最小的顶点,将之从 待选集合移至已选集合,如此重复,直到 待选集合中没有顶点为止。

所走过的顶点和边就是该连通图的最小生成树。

【算法实现】

输入:网(带权值的图)

输出:依次访问各个顶点 创建一个辅助数组,每次将当前已选类顶点到待选类顶点的距离填写到数组中。然后从中筛选出权值最小的边的邻接点。

在辅助数组中找出权值最小的边的数组下标,就可以间接找到此边的终点顶点。找到后lowcost标记为0。

【数据结构设计】

  1. 图的存储结构:由于在算法执行过程中,需要不断读取任意两个顶点之间边的权值,所以,图采用邻接矩阵存储。
  2. 候选最短边集:设置数组shortEdge[n]表示候选最短边集,数组元素包括adjvex和lowcost两个域,分别表示候选最短边的邻接点和权值。

【算法步骤】

  1. 初始化两个辅助数组lowcost和adjvex;
  2. 输出顶点u0,将顶点u0加入集合U中;
  3. 重复执行下列操作n-1次 3.1 在lowcost中选取最短边,取adjvex中对应的顶点序号k; 3.2 输出顶点k和对应的权值; 3.3 将顶点k加入集合U中; 3.4 调整数组lowcost和adjvex;

【输入输出】

第一行输入顶点的个数n

第二行输入n个顶点的值

第三行输入边的条数m

下面m行依次输入边的起点的值,终点的值和权值。

输出最小生成树,每行输出它的一条边。

【测试数据】

输入:

请输入顶点的个数:6

请依次输入6个顶点的值:

A B C D E F

请输入边的个数:9

请依次输入9个边的起点、终点和权值:

A C 46

A F 19

A B 34

B E 12

E F 26

D E 38

D F 25

C D 17

C F 25

输出:

最小生成树是:

(AF)19

(FC)25

(CD)17

(FE)26

(EB)12

【代码示例】

#include <iostream>
#include <limits.h>
#include <string> // 包含string以便处理顶点的字符表示
#define MAX_V_NUM 20

using namespace std;

struct closedge
{
    char adjvex; // 邻接点的字符表示
    int lowcost; // 到邻接点的最小权值
};

closedge shortEdge[MAX_V_NUM]; // 存储候选最短边集

class MGraph
{
private:
    char vertex[MAX_V_NUM]; // 图的顶点数组
    int arc[MAX_V_NUM][MAX_V_NUM]; // 图的邻接矩阵
    int vexNum, arcNum; // 顶点数和边数

public:
    MGraph(); // 构造函数
    void Prim(int start); // 实现Prim算法
};

MGraph::MGraph()
{
    cout << "请输入顶点的个数:";
    cin >> vexNum;
    cout << "请依次输入" << vexNum << "个顶点的值:" << endl;
    for (int i = 0; i < vexNum; i++)
    {
        cin >> vertex[i];
    }
    cout << "请输入边的个数:";
    cin >> arcNum;
    char vi, vj;
    int w;
    // 初始化邻接矩阵
    for (int i = 0; i < vexNum; i++)
    {
        for (int j = 0; j < vexNum; j++)
        {
            if(i==j)
            {
                arc[i][j] =0;
            }
            else
            {
                arc[i][j]=INT_MAX;
            }
        }
    }
    cout << "请依次输入" << arcNum << "个边的起点、终点和权值:" << endl;
    for (int i = 0; i < arcNum; i++)
    {
        cin >> vi >> vj >> w;
        arc[vi - 'A'][vj - 'A'] = w;
        arc[vj - 'A'][vi - 'A'] = w; // 因为是无向图
    }
    // 假设从顶点0开始Prim算法
    Prim(0);
}

void MGraph::Prim(int start)
{
    // 初始化shortEdge数组
    for (int i = 0; i < vexNum; i++)
    {
        shortEdge[i].lowcost = arc[start][i];
        shortEdge[i].adjvex = start;
    }
    shortEdge[start].lowcost = 0;
    cout << "最小生成树是:" << endl;
    for (int i = 0; i < vexNum - 1; i++)
    {
        int k = 0;
        for (int j = 0; j < vexNum; j++)
        {
            if(shortEdge[j].lowcost!=0&&shortEdge[j].lowcost!=INT_MAX){
                    k=j;
                    break;
            }
        }
        for (int j = 0; j < vexNum; j++){
               if (shortEdge[j].lowcost!=0&&shortEdge[k].lowcost>shortEdge[j].lowcost)
                {
                    k =j;
                    break;
                }
        }
        // 输出最小边
        cout << "(" << vertex[shortEdge[k].adjvex] << vertex[k] << ")" << shortEdge[k].lowcost << endl;
        // 将找到的最小边标记为0,表示已访问
        shortEdge[k].lowcost = 0;
        for (int j = 0; j < vexNum; j++)
        {
            if (arc[k][j]!=0&&arc[k][j] < shortEdge[j].lowcost)
            {
                shortEdge[j].lowcost = arc[k][j];
                shortEdge[j].adjvex = k;
            }
        }
    }
}

int main()
{
    MGraph graph;
    return 0;
}

Kruskal算法求最小生成树

【算法思想】

将所有边按照权值的大小进行升序排序,然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。筛选出来的边和所有的顶点构成此连通网的最小生成树。

注意:排序时如果两条边权值相同,起点下标较小边排在前面,保证测试数据的准确。

判断是否会产生回路的方法

1、标记法:在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接。 假设遍历到一条由顶点 A 和 B 构成的边,而顶点 A 和顶点 B 标记不同,此时不仅需要将顶点 A 的标记更新为顶点 B 的标记,还需要更改所有和顶点 A 标记相同的顶点的标记,全部改为顶点 B 的标记。

2、初始时每个顶点构成一棵生成树,然后每生长一次就将两棵树合并,到最后合并成一棵树。判断两个顶点是否在同一课树中,如果在同一棵树则产生了回路。

判断顶点是否在同一棵树中,可以从顶点出发,寻找树的根结点。如果两个顶点的根结点相同,则属于同一棵树。

【算法描述】

设无向连通网为G=(V, E),令G的最小生成树为T=(U, TE),其初态为U=V,TE={ },然后,按照边的权值由小到大的顺序,考察G的边集E中的各条边。

若被考察的边的两个顶点属于T的两个不同的连通分量,则将此边作为最小生成树的边加入到T中,同时把两个连通分量连接为一个连通分量;若被考察边的两个顶点属于同一个连通分量,则舍去此边,以免造成回路,如此下去,当T中的连通分量个数为1时,此连通分量便为G的一棵最小生成树。

【图的存储结构】

因为Kruskal算法是依次对图中的边进行操作,因此考虑用边集数组存储图中的边,为了提高查找速度,将边集数组按边上的权排序。

struct EdgeType

{

int from,to; //边依附的两个顶点

int weight; //边上的权值

};

【输入输出】

请输入图的顶点数和边数:6 9

请输入顶点的值:0 1 2 3 4 5

请输入边的起点终点和权值:

1 4 12

2 3 17

0 5 19

2 5 25

3 5 25

4 5 26

0 1 34

3 4 38

0 2 46

用Kruskal算法生成最小生成树的生成次序为:

(1,4)12

(2,3)17

(0,5)19

(2,5)25

(4,5)26

【代码示例】

#include <iostream>
#include <algorithm>
using namespace std;
const int MAX = 10000;
template <class T>
struct EdgeType
{
	T from, to;     //边依附的两个顶点
	int weight;      //边上的权值
};
template <class T>
class EdgeGraph
{
private:
	char* vertex;  //存放图顶点的数组
	EdgeType<char>* edge;      //存放边的数组
	int vertexNum, edgeNum;         //图的顶点数和边数
public:
	EdgeGraph(int vNum, int eNum);//构造函数
	void inputGraph();//输入图
	void Kruskal();
	int getIndex(char v)
	{
		for (int i = 0; i < vertexNum; i++) {
			if (vertex[i] == v) {
				return i;
			}
		}
		return -1; // 如果找不到顶点,返回-1
	}
	void sorts();
private:
	T findRoot(int parent[], T v);//找到树的根
};
template <class T>
EdgeGraph<T>::EdgeGraph(int vNum, int eNum)
{
	vertexNum = vNum;
	edgeNum = eNum;
	vertex = new char[vertexNum];
	edge = new EdgeType<T>[edgeNum];
	char v;
	T f, t;
	int w;
	cout << "请输入顶点的值:";
	for (int i = 0; i < vertexNum; i++)
	{
		cin >> v;
		vertex[i] = v;
	}
	cout << "请输入边的起点终点和权值:\n";
	for (int i = 0; i < edgeNum; i++)
	{
		cin >> f >> t >> w;
		edge[i].from = f;
		edge[i].to = t;
		edge[i].weight = w;
	}
	sorts();

}
//对weight排序
// 最开始写的冒泡排序,但是有问题
// 冒泡排序不是对边的数组edge中的元素进行排序,而是对数组的索引进行排序。这意味着数组edge中的元素并没有按照权值从小到大进行排序,而是数组的索引被重新排列,使得具有较小权值的边的索引在前。这并不是Kruskal算法所需要的。
// 需要对边的数组edge中的EdgeType对象按照权值进行排序,而不是对索引进行排序。
// 修正后,使用了C++11的lambda表达式来定义排序准则,即按照weight成员进行比较。这样,edge数组中的EdgeType对象将根据它们的权值进行排序,而不是它们的索引。
template <class T>
void EdgeGraph<T>::sorts()
{
		// 使用标准库的sort函数对边按照权值进行排序
        
		sort(edge, edge + edgeNum, [](const EdgeType<T>& a, const EdgeType<T>& b) {
			return a.weight < b.weight;
			});

}
template <class T>
void EdgeGraph<T>::Kruskal()
{
	int parent[MAX] = { 0 };
	T vex1, vex2;
	sorts();
	for (int i = 0; i < vertexNum; i++)
	{
		parent[i] = -1;
	}
	
	for (int num = 0, i = 0; i < edgeNum; i++)
	{
		//找到所在生成树的根节点
		int idx1 = getIndex(edge[i].from);
		int idx2 = getIndex(edge[i].to);
		vex1 = findRoot(parent, idx1);
		vex2 = findRoot(parent, idx2);
		if (vex1 != vex2) //如果两个根节点不同,不会构成环
		{
			cout << "(" << edge[i].from << "," << edge[i].to << ")" << edge[i].weight << endl;
			parent[vex2] = vex1;//合并生成树
			num++;
			if (num == vertexNum - 1)  //循环了顶点数-1次,提前返回
				return;
		}
	}
}
//求根节点
template <class T>
T EdgeGraph<T>::findRoot(int parent[], T v)
{
	T t = v;
	while (parent[t] > -1)
	{
		t = parent[t];
	}
	return t;
}
int main()
{
	int vNum, eNum;
	cout << "请输入图的顶点数和边数:";
	cin >> vNum >> eNum;
	EdgeGraph<char> edgegraph(vNum, eNum);
	cout << "用Kruskal算法生成最小生成树的生成次序为:\n";
	edgegraph.Kruskal();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/642859.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Reactor设计模式

Reactor设计模式 Reactor模式称为反应器模式或应答者模式&#xff0c;是基于事件驱动的设计模式&#xff0c;拥有一个或多个并发输入源&#xff0c;有一个服务处理器和多个请求处理器&#xff0c;服务处理器会同步的将输入的请求事件以多路复用的方式分发给相应的请求处理器。…

Android 自定义图片进度条

用系统的Progressbar&#xff0c;设置图片drawable作为进度条会出现图片长度不好控制&#xff0c;容易被截断&#xff0c;或者变形的问题。而我有个需求&#xff0c;使用图片背景&#xff0c;和图片进度&#xff0c;而且在进度条头部有个闪光点效果。 如下图&#xff1a; 找了…

Nginx部署静态网页

1、首先拿到前端给的dist包&#xff0c;上传到服务器指定位置&#xff1a;/ajd/dist 2、找到nginx.conf配置文件&#xff0c;修改 server {listen 9300;server_name xxx.xx.xx.xx;location / {root /ajd/dist;try_files $uri $uri/ /index.html;index index.html …

电赛一等奖!基于TMS320F2812的简易数字频率计

电赛一等奖&#xff01;简易数字频率计设计&#xff08;原理图、PCB、源码、分析报告&#xff09; 这份文件是关于合肥工业大学电气与自动化工程学院的一个项目报告&#xff0c;题目为“基于TMS320F2812的简易数字频率计”。项目由方敏、侯其立、李苗、张巧云四位本科生完成&am…

区块链技术和应用

文章目录 前言 一、区块链是什么&#xff1f; 二、区块链核心数据结构 2.1 交易 2.2 区块 三、交易 3.1 交易的生命周期 3.2 节点类型 3.3 分布式系统 3.4 节点数据库 3.5 智能合约 3.6 多个记账节点-去中心化 3.7 双花问题 3.8 共识算法 3.8.1 POW工作量证明 总结 前言 学习长…

XILINX FPGA DDR 学习笔记(一)

DDR 内存的本质是数据的存储器&#xff0c;首先回到数据的存储上&#xff0c;数据在最底层的表现是地址。为了给每个数据进行存放并且在需要的时候读取这个数据&#xff0c;需要对数据在哪这个抽象的概念进行表述&#xff0c;我们科技树发展过程中把数据在哪用地址表示。一个数…

2. C++服务器编程-信号

什么是信号 其实信号就是一个中断。就是在执行程序的时候突然来了一个信号&#xff0c;然后我们去执行这个新来的程序了&#xff0c;这就是中断。 处理方法 信号的处理方式∶忽略、捕获、默认处理 linux中都有那些信号 man7 signal 比如说kill -9 安装man中文手册 自己百…

单片机LCD1602显示电子时钟设计

基于52单片机电子时钟的设计 摘要 本次设计的多功能时钟系统采用STC89C52单片机为核心器件&#xff0c;利用其定时器/计数器定时和记数的原理&#xff0c;结合液晶显示电路、时钟芯片DS1302电路、电源电路以及按键电路来设计计时器。将软硬件有机地结合起来&#xff0c;使得系…

【CSP CCF记录】202012-2 期末预测之最佳阈值

题目 过程 思路 第一次没用前缀和&#xff0c;暴力求解得50分。 采用前缀和方法。 1. 对原数组stu[i]进行排序。 2. 计算前缀和数组s[]&#xff0c;s[i]表示安全指数的y_i的前缀和&#xff0c;即安全指数小于等于y_i时的实际挂科情况&#xff0c;y_i之前有多少个未挂科&am…

边用边充电影响寿命吗?看看计算机指令组成与操作类型

计算机指令集体系结构之指令 指令由操作码和地址码字段组成。 操作码指明了指令要完成的操作。 长度可以固定&#xff1a;比如RISC&#xff08;reduced instruction set computer&#xff09;精简指令集计算机 与之对应的RISC&#xff08;复杂指令集计算机&#xff09;&…

【css3】02-css3新特性之选择器篇

目录 1 属性选择器 2 结构伪类选择器 3 其他选择器 :target和::selection ::first-line和::first-letter 4 伪类和伪元素的区别 伪类&#xff08;Pseudo-classes&#xff09; 伪元素&#xff08;Pseudo-elements&#xff09; 伪类和伪元素的区别 1 属性选择器 ☞ 属性选…

失落的方舟台服预下载教程 一键下载+账号注册教程

失落的方舟台服预下载教程 一键下载&#xff0b;账号注册教程 是一款今年备受瞩目的游戏&#xff0c;将于5月30日正式上线&#xff0c;这款游戏搭建在虚幻引擎的基础上&#xff0c;为玩家们带来了极佳的视觉体验。这款游戏秉承着MMO类型游戏一贯的玩法&#xff0c;但是制作组在…

【面试干货】数据库乐观锁,悲观锁的区别,怎么实现

【面试干货】数据库乐观锁&#xff0c;悲观锁的区别&#xff0c;怎么实现 1、乐观锁&#xff0c;悲观锁的区别2、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、乐观锁&#xff0c;悲观锁的区别 悲观锁&#xff08;Pessimistic Lo…

在Spring 当中存在的八大模式

在Spring 当中存在的八大模式 文章目录 在Spring 当中存在的八大模式每博一文案1. 简单工厂模式2. 工厂方法模式3. 单例模式4. 代理模式5. 装饰器模式6. 观察者模式7. 策略模式8. 模板方法模式最后&#xff1a; 每博一文案 我认为 “知世故而不世故” 才是真正意义上的成熟。回…

又有人叫嚣:AI取代前端,来给你几张图,看能不能憋死AI。

总有自媒体人&#xff0c;为了些许流量&#xff0c;在大放厥词&#xff0c;说截个图给AI&#xff0c;AI就能输出前端代码&#xff0c;这是啥都敢说&#xff0c;吹牛不上税。 我来给你几张贝格前端工场日常接的大数据项目相关的图&#xff0c;你让AI生成代码&#xff0c;取代前…

fastapi中实现多个路由请求

大家伙&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 前言 最近在写机器人相关的接口&#xff0c;顺手学了学python&#xff0c;发现这是个好东西&#xff0c;写代码效率比java要高很多&#xff0c;比如写个词云呀&#xff0c;写个回调呀&am…

【C++】详解AVL树——平衡二叉搜索树

个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 祝福语&#xff1a;愿你拥抱自由的风 目录 二叉搜索树 AVL树概述 平衡因子 旋转情况分类 左单旋 右单旋 左右双旋 右左双旋 AVL树节点设计 AVL树设计 详解单旋 左单旋 右单旋 详解双旋 左右双旋 平衡因子情况如…

GAW-1000D 微机控制钢绞线拉力试验机

一、整机外观图与示意图 外观示意图 性能说明&#xff1a; GAW-1000D型微机控制电液伺服钢绞线拉力试验机主要用于对预应力钢绞线进行抗拉强度测试。由宽调速范围的电液比例伺服阀与计算机及测控单元所组成伺服控制系统&#xff0c;能精确的控制和测量试验全过程。整机由主机…

使用kubesphere部署微服务的时候,节点的镜像不是最新的导致部署到旧版本问题

我使用kubesphere部署微服务的时候&#xff0c;发现有很多次&#xff0c;我修改了配置文件&#xff0c;但是部署完才发现部署的是旧版本。 然后我查看了该微服务部署在哪个节点上&#xff1a; kubectl get pods --all-namespaces -o wide例如 gulimall-gateway 这个服务&…

【会议征稿,IEEE出版】第九届信息科学、计算机技术与交通运输国际学术会议(ISCTT 2024,6月28-30)

第九届信息科学、计算机技术与交通运输国际学术会议&#xff08;ISCTT 2024&#xff09;将于2024年6月28-30日在中国绵阳举行。 ISCTT 2024将围绕 “信息科学”、"计算机技术”、“交通运输” 等最新研究领域&#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专…