图:最小生成树

2023-11-01

一、最小生成树

1.1 生成树的定义
      一个连通图的生成树是⼀个极小的连通子图,它包含图中全部的n个顶点,但只有构成⼀棵树的n-1条边
连通图和它相对应的⽣成树,可以⽤于解决实际生活中的问题:假设A、B、C 和 D 为 4 座城市,
为了⽅便⽣产⽣活,要为这 4 座城市建⽴通信。对于 4 个城市来讲,本着节约经费的原则,只需要建⽴3 个通信线路即可,就如图(b)中的任意⼀种⽅式。
在具体选择采用图(b)中哪⼀种⽅式时,需要综合考虑城市之间间隔的距离,建设通信线路的难度等各种因素,将这些因素综合起来⽤⼀个数值表示,当作这条线路的权值。

综合所看,选择权值总和为7的两种⽅案最节约经费 

1.2生成树的属性

⼀个连通图可以有多个⽣成树;
⼀个连通图的所有生成树都包含相同的顶点个数和边数;
生成树当中不存在环;
移除生成树中的任意⼀条边都会导致图的不连通, 生成树的边最少特性;
在⽣成树中添加⼀条边会构成环。 对于包含n个顶点的连通图,⽣成树包含n个顶点和n-1条边;
对于包含n个顶点的无向完全图最多包含n^(n-2)颗生成树。
1.3最小生成树
所谓⼀个 带权图 的最小生成树,就是原图中边的权值最小的生成树。
最小生成树的算法思想就是从顶点集或边集的角度来进行贪婪化。

二、Kruskal算法

代码:

#ifndef KRUSKAL_H
#define KRUSKAL_H
#include "../base.h"
#include "../01.MatrixGraph/matrixGraph.h"
/* Kruskal算法
 * 从边的角度求图的最小生成树,更适合求边稀疏的图
 * 算法思路:
 * a. 将所有的边按照权值的大小进行升序排序,然后从小到大一一判断,判断条件:
 * 	1. 如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;
 * 	2. 放置,舍去
 * b. 循环a,直到具有n个顶点的连通图筛选出n-1条边为止
 * 引入一个数据结构,边集数组,保存边和节点的关系
 * 简化版:从边集数组里,找最小的边,判断2个顶点会不会构成一个环,如果不够,加入结果
 * */

// 使用邻接矩阵表示了无向图,从邻接矩阵中初始化边集数组
int initEdgeSet(const MGraph *graph, EdgeSet *edges);
// 排序边集数组
void sortEdgeSet(EdgeSet *edges, int num);
// Kruskal算法
int KruskalMGraph(const MGraph *graph, const EdgeSet *edges, int num, EdgeSet *result);
#endif
#include <string.h>
#include <stdlib.h>
#include "Kruskal.h"

/* 邻接矩阵无向图,只需要上三角数据即可 */
//把邻接矩阵变成边集数组,边集数组就是把边放进一个数组
int initEdgeSet(const MGraph *graph, EdgeSet *edges) {
	int k = 0;
	for (int i = 0; i < graph->nodeNum; ++i) {			// 遍历每个节点
		for (int j = i + 1; j < graph->nodeNum; ++j) {	// 遍历每个节点,所有的边,i+1,遍历上三角(每一行从对角线开始遍历)
            //ij的边存在,则放入边集数组中
			if (graph->edges[i][j] > 0) {
				edges[k].begin = i;
				edges[k].end = j;
				edges[k].weight = graph->edges[i][j];
				k++;
			}
		}
	}
	return k;
}

/* 按照边的权值从小到大进行排序
 * 冒泡方法,第i个元素和后面元素比较,如果后面的权值小,交换
 * 通过一次遍历,当前的i就是最小值
 * */
void sortEdgeSet(EdgeSet *edges, int num) {
	EdgeSet tmp;
	for (int i = 0; i < num; ++i) {
		for (int j = i + 1; j < num; ++j) {
            //j是i的下一个边,如果j的权值小于i,则i与j交换位置。
			if (edges[j].weight < edges[i].weight) {
                //两个空间的交换
				memcpy(&tmp, &edges[i], sizeof(EdgeSet));
				memcpy(&edges[i], &edges[j], sizeof(EdgeSet));
				memcpy(&edges[j], &tmp, sizeof(EdgeSet));
			}
		}
	}
}


// 从并查集中,找a节点的根节点
static int getRoot(const int *uSet, int a) {
	while (a != uSet[a]) {
		a = uSet[a];
	}
	return a;
}

//Kruskal算法
int KruskalMGraph(const MGraph *graph, const EdgeSet *edges, int num, EdgeSet *result) {
	int *uSet;
	int a;
	int b;
	int count = 0;
	int sum = 0;
	// 1. 初始化并查集,每一个节点的编号就是自己
	uSet = (int *) malloc(sizeof(int ) *graph->nodeNum);
	for (int i = 0; i < graph->nodeNum; ++i) {
		uSet[i] = i;
	}
	// 2. 从已经排序好的边集中,找到最小的边(顺序找),当这个边加入后,不构成闭环,就可以加入结果
	for (int i = 0; (count < (graph->nodeNum - 1)) && i < num; ++i) {
		a = getRoot(uSet, edges[i].begin);
		b = getRoot(uSet, edges[i].end);
		if (a != b) {				// 不构成闭环
			uSet[a] = b;            //b加入a的并查集,且b为a的老爸
			result[count].begin = edges[i].begin;
			result[count].end = edges[i].end;
			result[count].weight = edges[i].weight;
			count++;
			sum += edges[i].weight;
		}
	}
	free(uSet);//释放并查集
	return sum;
}
#include <stdlib.h>
#include <stdio.h>
#include "Kruskal.h"


static void setupMGraph(MGraph *graph) {
	char *names[] = {"A", "B", "C", "D",
					 "E", "F", "G"};
//初始化:图,顶点个数,names,无向图0,边数为0
	initMGraph(graph, sizeof(names)/ sizeof(names[0]), names, 0, 0);
//添加边
	addMGraphEdge(graph, 0, 1, 12);//边AB,权值12
	addMGraphEdge(graph, 0, 5, 16);
	addMGraphEdge(graph, 0, 6, 14);
	addMGraphEdge(graph, 1, 2, 10);
	addMGraphEdge(graph, 1, 5, 7);
	addMGraphEdge(graph, 2, 3, 3);
	addMGraphEdge(graph, 2, 4, 5);
	addMGraphEdge(graph, 2, 5, 6);
	addMGraphEdge(graph, 3, 4, 4);
	addMGraphEdge(graph, 4, 5, 2);
	addMGraphEdge(graph, 4, 6, 8);
	addMGraphEdge(graph, 5, 6, 9);
}

int main() {
	MGraph graph;//邻接矩阵
	EdgeSet *edges;

	setupMGraph(&graph);
    //申请边
	edges = (EdgeSet *) malloc(sizeof(EdgeSet) * graph.edgeNum);

	int num = initEdgeSet(&graph, edges);//初始化边集数组
	printf("edgeSet num: %d\n", num);
	sortEdgeSet(edges, num);

    //结果(n-1条边)
	EdgeSet *result = (EdgeSet *) malloc(sizeof(EdgeSet) * (graph.nodeNum - 1));

    //总权值
	int sumW = KruskalMGraph(&graph, edges, num, result);
	printf("Kruskal sum of weight: %d\n", sumW);
    //打印输出边的情况
    //点---权值---点(A---12---B)
	for (int i = 0; i < graph.nodeNum - 1; ++i) {
		printf("edge %d: [%s] --- <%d> --- [%s]\n", i + 1,
			   graph.vex[result[i].begin].show, result[i].weight, graph.vex[result[i].end].show);
	}
	free(edges);
	free(result);
	return 0;
}

三、Prim算法

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

图:最小生成树 的相关文章

随机推荐

  • 南京大学《软件分析》笔记01 - 静态分析的基本概念

    Rice s Theorem Any non trivial property of the behavior of programs in a r e language is undecidable r e recursively enu
  • 顺序表初始化

    文章目录 1 顺序表 2 顺序表的初始化 1 顺序表 顺序表 顺序存储结构 存储数据时 会提前申请一整块足够大小的物理空间 然后将数据依次存储到一整块连续的存储空间内 存储时做到数据元素之间不留一丝缝隙 使用顺序表存储集合 1 2 3 4
  • Cheat Engine 教程( 1 - 9 通关 )

    工具包 https down 52pojie cn Tools Debuggers Cheat Engine 官网 https www cheatengine org Cheat Engine v7 5 汉化 https pan aoe t
  • FPGA project : inf_rcv

    module top input wire sys clk input wire sys rst n input wire inf in output wire led output wire ds output wire oe outpu
  • 获取数组的所有子序列

    一个包含n个元素的集合 获取其所有子集 可以采用按位对应法 例如 int array 1 3 2 5 这个集合可以看做1325四位 每一位在子集中要么存在要么不存在 是否的操作我们就考虑二进制的01 一位子序列的情况有 1000 0100
  • 大数据的分布式SQL查询引擎 -- Presto的详细使用

    Presto Distributed SQL Query Engine for Big Data 官网 项目源码 官方文档 目录 1 Presto 概述 2 概念 2 1 服务进程 2 2 数据源 2 3 查询执行模型 3 整体架构 4 P
  • 2.4 【LaTex】数论符号

    文章目录 同余 向下取整 向上取整 整除 进制 对数 数论的文章 写的人是蛮少的 因为数论好像已经成为民科专用数学 因为数论门槛低 上限高 研究成本低 很多问题至今未解决 所以成为了民科首选 在这篇文章 我不可能介绍所有数论使用的符号 所以
  • 查看gcc/g++默认include路径

    转自 http gcc gnu org ml gcc help 2007 09 msg00205 html gcc print prog name cc1plus v g print prog name cc1plus v 例如 CentO
  • 新安装Android Studio创建项目失败解决方法

    一 梗概 第一次安装Android Studio的时候 因为被墙等原因 Gradle总是出错导一直构建不了项目 Failed to open zip file Gradle s dependency cache may be corrupt
  • Delphi / C ++ Builder / Lazarus报表开发:如何直接从代码中保存BPM / JPEG / TIFF / GIF?

    报表生成器FastReport VCL是用于在软件中集成商务智能的现代解决方案 它提供了可视化模板设计器 可以访问最受欢迎的数据源 报告引擎 预览 将过滤器导出为30多种格式 并可以部署到云 Web 电子邮件和打印中 近日 FastRepo
  • Hexo + GitHub 搭建个人博客(三) Hexo配置

    Hexo 博客配置 你可以 在根目录下 config yml 中 修改大部分的配置 网站 参数 描述 title 网站标题 subtitle 网站副标题 description 网站描述 keywords 网站的关键词 支持多个关键词 au
  • TCP/UDP/Socket 通俗讲解

    1 封包和拆包 封包 就是发送数据前把自己本地需要发送的数据包装一下 即把要发送的原始数据附加上接受者可以辨识到自己身份等一些额外信息 有点像寄一封信 信封上填写的寄件人和收件人以及地址 拆包 是接收到对方封包后发送来的数据后 拆出原始信息
  • c++基础2:使用VS2010 创建最简单的MFC应用程序窗体

    1 添加 新建项目 选择 VISUAL C MFC应用程序 确定 下一步 2 在 应用程序类型 中选择 基于对话框 下一步 3 在 用户界面功能 只选择 粗框架 下一步 4 在 高级功能 取消所有选择 下一步 5 生成的类 点击 完成
  • 用Cmake生成opencv_contrib的python接口

    最近在看opencv的Fisherface Eigenface的部分 但具体实现时发现该库包含在opencv的contrib模块里 这个模块是opencv的扩展库 里面包括很多特征的算法 SIFT SURF Adaboost算法 ml还有神
  • Ubuntu 下命令行创建(删除)文件(夹)

    很多时候我们都会在终端进行文件 文件夹的创建与删除 使用快捷键ctrl alt t 打开终端 创建文件 touch a txt 创建文件夹 mkdir NewFolder 删除文件 rm a txt 删除文件夹 rmdir NewFolde
  • php 格式化 字符串

    private function setStringSubstr str len sublen len string strip tags str string preg replace n is string string preg re
  • CentOS使用 wget 命令报错Temporary failure in name resolution 解决方法

    在CentOS中安装Redis时使用wget下载一个文件出现了如下问题 wget http download redis io releases redis 3 0 7 tar gz failed Temporary failure in
  • 煤矿智能化相关50项团体标准征求意见

    智能化煤矿总体架构 原文地址 https chinacs scimall org cn a3651 html 由煤矿智能化创新联盟等单位提出 中国煤炭学会归口 中煤科工集团常州研究院有限公司等单位起草的 煤矿通信接口与协议通用技术要求 50
  • java中序列化与反序列化_Java中的序列化示例

    java中序列化与反序列化 Serialization in Java is the process of converting an object into bytes stream to save it in file Or we ca
  • 图:最小生成树

    一 最小生成树 1 1 生成树的定义 一个连通图的生成树是 个极小的连通子图 它包含图中全部的n个顶点 但只有构成 棵树的n 1条边 连通图和它相对应的 成树 可以 于解决实际生活中的问题 假设A B C 和 D 为 4 座城市 为了 便