[C++][数据结构][图][中][图的遍历][最小生成树]详细讲解

目录

  • 1.图的遍历
    • 1.广度优先遍历
    • 2.深度优先遍历
  • 2.最小生成树
    • 1.Kruskal算法
    • 2.Prim算法


1.图的遍历

  • 给定一个图G和其中任意一个顶点 v 0 v_0 v0,从 v 0 v_0 v0出发,沿着图中各边访问图中的所有顶点,且每个顶 点仅被遍历一次
    • “遍历”:对结点进行某种操作的意思

1.广度优先遍历

请添加图片描述

  • **例如:**现在要找东西,假设有三个抽屉,东西在哪个抽屉不清楚,现在要将其找到,广度优先遍历的做法是:

    1. 先将三个抽屉打开,在最外层找一遍
    2. 将每个抽屉中红色的盒子打开,再找一遍
    3. 将红色盒子中绿色盒子打开,再找一遍
    4. 直到找完所有的盒子
      • 注意:每个盒子只能找一次,不能重复找
        请添加图片描述
  • 思考:如何防止节点被重复遍历?

    • 增加一个数组,用于标记是否入过队列,这样可以防止重复遍历
void BFS(const V& src)
{
	size_t srci = GetVertexIndex(src);

	queue<int> q;
	vector<bool> visited(_vertexs.size(), false); // 标记数组

	q.push(srci);
	visited[srci] = true;
	int levelSize = 1; // 控制每层出的数量

	while (!q.empty())
	{
		// 一层一层出
		for (size_t i = 0; i < levelSize; i++)
		{
			int front = q.front();
			q.pop();
			cout << front << ":" << _vertexs[front] << " ";

			// 把front的邻接顶点入队列
			for (size_t j = 0; j < _vertexs.size(); j++)
			{
				if (_matrix[front][j] != MAX_W && visited[j] == false)
				{
					q.push(j);
					visited[j] = true;
				}
			}
		}
		cout << endl;

		levelSize = q.size();
	}
}

2.深度优先遍历

请添加图片描述

  • **例如:**现在要找东西,假设有三个抽屉,东西在哪个抽屉不清楚,现在要将其找到,深度优先遍历的做法是:
    1. 先将第一个抽屉打开,在最外层找一遍
    2. 将第一个抽屉中红盒子打开,在红盒子中找一遍
    3. 将红盒子中绿盒子打开,在绿盒子中找一遍
    4. 递归查找剩余的两个盒子
    • **深度优先遍历:**将一个抽屉一次性遍历完(包括该抽屉中包含的小盒子),再去递归遍历其他盒子
  • 如果给的图不是连通图,以某个顶点为起点没有遍历完成,怎么保证遍历完剩下的顶点
    • 在visited数组中找没有遍历过的顶点,再次进行遍历
void _DFS(size_t srci, vector<bool>& visited)
{
	cout << srci << ":" << _vertexs[srci] << endl;
	visited[srci] = true;

	for (size_t i = 0; i < _vertexs.size(); i++)
	{
		if (_matrix[i] != MAX_W && visited[i] == false)
		{
			_DFS(i, visited);
		}
	}
}

void DFS(const V& src)
{
	size_t srci = GetVertexIndex(src);
	vector<bool> visited(_vertexs.size(), false);
	_DFS(srci, visited);

	// 处理存在不连通的情况
	for (size_t i = 0; i < _vertexs.size(); i++)
	{
		if (!visited[i])
		{
			_DFS(i, visited);
		}
	}
}

2.最小生成树

  • 连通图中的每一棵生成树,都是原图的一个极大无环子图,即:
    • 从其中删去任何一条边,生成树就不在连通
    • 反之,在其中引入任何一条新边,都会形成一条回路
  • 若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边,因此构造最小生成树的准则有三条:
    • 只能使用图中权值最小的边来构造最小生成树
      • 最小的成本让着N个顶点连通
    • 只能使用恰好n-1条边来连接图中的n个顶点
    • 选用的n-1条边不能构成回路
  • 构造最小生成树的方法:Kruskal算法和Prim算法,这两个算法都采用了逐步求解的贪心策略
  • 贪心算法:
    • 指在问题求解时,总是做出当前看起来最好的选择
      • 即:贪心算法做出的不是整体最优的的选择,而是某种意义上的局部最优解
    • 贪心算法不是对所有的问题都能得到整体最优解

1.Kruskal算法

  • 任给一个有n个顶点的连通网络 N = { V , E } N=\{V,E\} N={V,E}
    • 首先构造一个由这n个顶点组成、不含任何边的图 G = { V , N U L L } G=\{V,NULL\} G={V,NULL},其中每个顶点自成一个连通分量
    • 其次不断从E中取出权值最小的一条边(若有多条任取其一),若该边的两个顶点来自不同的连通分量,则将此边加入到G中
      • 如此重复,直到所有顶点在同一个连通分量上为止
    • 核心每次迭代时,选出一条具有最小权值,且两端点不在同一连通分量上的边,加入生成树
      • Kruskal算法是一种全局贪心的算法
  • 如何判断是否形成环?
    • 并查集
  • 在下图执行Kruskal算法的过程
    • 加了阴影的边属于不断增长的森林A
    • 该算法按照边的权重大小依次进行考虑,箭头指向的边是算法每一步考察的边
      • 如果该条边将两颗不同的树连接起来,它就被加入到森林里,从而完成对两棵树的合并
        请添加图片描述
W Kruskal(Self& minTree)
{
	size_t n = _vertexs.size();

	// 初始化minTree
	minTree._vertexs = _vertexs;
	minTree._indexMap = _indexMap;
	minTree._matrix.resize(n);
	for (size_t i = 0; i < n; i++)
	{
		minTree._matrix[i].resize(n, MAX_W);
	}

	priority_queue<Edge, vector<Edge>, greater<Edge>> minQueue;

	// 建堆排序
	for (size_t i = 0; i < n; i++)
	{
		for (size_t j = 0; j < n; j++)
		{
			if (i < j && _matrix[i][j] != MAX_W)
			{
				minQueue.push(Edge(i, j, _matrix[i][j]));
			}
		}
	}

	// 选出n-1条边
	size_t size = 0;
	W totalW = W();
	UnionFindSet ufs(n);
	while (!minQueue.empty())
	{
		Edge min = minQueue.top();
		minQueue.pop();

		// 判环 -> 并查集
		if (!ufs.InSameSet(min._srci, min._dsti))
		{
			cout << _vertexs[min._srci] << "->" \
				<< _vertexs[min._dsti] << ":" << min._w << endl;
			
			minTree._AddEdge(min._srci, min._dsti, min._w);
			ufs.Union(min._srci, min._dsti); // 入集
			
			size++;
			totalW += min._w;
		}
		else
		{
			cout << "Forming Ring: ";
			cout << _vertexs[min._srci] << "->" \
				<< _vertexs[min._dsti] << ":" << min._w << endl;
		}
	}

	if (size == n - 1)
	{
		return totalW;
	}
	else
	{
		return W();
	}
}

2.Prim算法

  • Prim算法的一个性质集合A中的边总是构成一棵树,这棵树从一个任意的根节点r开始,一直长大到覆盖V中的所有结点时为止
    • Prim算法思路天然避环
    • 算法每一步在连续集合A和A之外的结点的所有边中,选择一条轻量级边加入到A中
    • 本策略也属于贪心策略,因为每一步所加入的边都必须是使树的总权重增加量最小的边
      • Prim算法是一种局部贪心算法
  • 在下图执行Prim算法的过程
    • 初始的根节点为a,加阴影的边和黑色的结点都属于树A
    • 在算法的每一步,树中的结点就决定了图的一个切割,横跨该切割的一条轻量级边被加入到树中
    • **例如:**在途中第二步,该算法可以选择将边 ( b , c ) (b, c) (b,c)加入到树中,也可以将边 ( a , h ) (a, h) (a,h)加入到树中,因为这两条边都是横跨该切割的轻量级边
      请添加图片描述
W Prim(Self& minTree, const W& src)
{
    size_t srci = GetVertexIndex(src);
    size_t n = _vertexs.size();

    // 初始化minTree
    minTree._vertexs = _vertexs;
    minTree._indexMap = _indexMap;
    minTree._matrix.resize(n);
    for (size_t i = 0; i < n; i++)
    {
        minTree._matrix[i].resize(n, MAX_W);
    }

    // true & false表示该元素是否在该集合内
    vector<bool> X(n, false);
    vector<bool> Y(n, true);
    X[srci] = true;
    Y[srci] = false;

    // 从X->Y集合中连接的边里面选出最小的边
    priority_queue<Edge, vector<Edge>, greater<Edge>> minQueue;

    // 先把srci连接的边添加到队列中
    for (size_t i = 0; i < n; i++)
    {
        if (_matrix[srci][i] != MAX_W)
        {
            minQueue.push(Edge(srci, i, _matrix[srci][i]));
        }
    }

    size_t size = 0;
    W totalW = W();
    while (!minQueue.empty())
    {
        Edge min = minQueue.top();
        minQueue.pop();

        // 最小边的目标也在X集合,则构成环
        if (X[min._dsti])
        {
            cout << "Forming Ring:";
            cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;
        }
        else
        {
            cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;

            minTree._AddEdge(min._srci, min._dsti, min._w);
            X[min._dsti] = true;
            Y[min._dsti] = false;

            size++;
            totalW += min._w;

            // 可能最小生成树已经生成,但是多了很多成环边,无须继续遍历
            if (size == n - 1)
            {
                break;
            }

            // 将目标顶点连接的边加入到队列中
            for (size_t i = 0; i < n; i++)
            {
                if (_matrix[min._dsti][i] != MAX_W && Y[i])
                {
                    minQueue.push(Edge(min._dsti, i, _matrix[min._dsti][i]));
                }
            }
        }
    }

    // 实际不一定存在最小生成树
    if (size == n - 1)
    {
        return totalW;
    }
    else
    {
        return W();
    }
}

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

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

相关文章

# [0619] Task01 绪论、马尔可夫过程、动态规划

easy-rl PDF版本 笔记整理 P1 - P2 joyrl 比对 补充 P1 - P3 相关 代码 整理 最新版PDF下载 地址&#xff1a;https://github.com/datawhalechina/easy-rl/releases 国内地址(推荐国内读者使用)&#xff1a; 链接: https://pan.baidu.com/s/1isqQnpVRWbb3yh83Vs0kbw 提取码: us…

lib9-03 配置基于时间的 ACL

实验&#xff1a;配置基于时间的 ACL 1、实验目的 通过本实验可以掌握定义 time-range 的方法基于时间 ACL 的配置和调试方法 2、实验拓扑 实验拓扑如下图所示。本实验要求只允许主机 PC1 在周一到周五每天的 8&#xff1a;00-17&#xff1a;00 访问路由器 R3 的Telnet 服务…

Python的三种方式显示图片

from PIL import Image import numpy as np im Image.open("img.png") #方法一&#xff1a;使用PIL库显示图片 a np.array(im) imImage.fromarray(a) im.show() import matplotlib.pyplot as plt #方法二&#xff1a;使用matplotlib库显示图片 plt.imshow(a) plt.s…

Covalent实现对1000亿笔链上交易解析,支持AI长期数据可用性

在区块链与人工智能&#xff08;AI&#xff09;交汇处&#xff0c;讨论往往集中于去中心化推理和去中心化训练等方面。然而&#xff0c;这一数据的关键组成部分却一直未得到足够的重视。一个主要问题是&#xff1a;我们如何保护 AI 模型中的数据不受偏见和操纵的影响&#xff1…

linux环境编程基础学习

Shell编程&#xff1a; 相对的chmod -x xx.sh可以移除权限 想获取变量的值要掏点dollar&#xff08;&#xff04;&#xff09; 多位的话要加个花括号 运算&#xff1a;expr 运算时左右两边必须要加空格 *号多个含义必须加转义符 双引号可以加反单&#xff0c;但是发过来就不行 …

企业微信,机器人定时提醒

场景&#xff1a; 每天定时发送文字&#xff0c;提醒群成员事情&#xff0c;可以用机器人代替 人工提醒。 1&#xff09;在企业微信&#xff0c;创建机器人 2&#xff09;在腾讯轻联&#xff0c;创建流程&#xff0c;选择定时任务&#xff0c;执行操作&#xff08;企业微信机…

秋招突击——6/19——新作{括号生成、合并K个排序链表}

文章目录 引言新作括号生成个人实现实现时遇到的问题实现代码 参考思路实现代码 合并K个有序链表个人实现实现代码 参考实现实现代码 总结 引言 今天把第二篇论文投了&#xff0c;后续有审稿意见再说&#xff0c;然后在进行修改的。后续的生活要步入正轨了&#xff0c;每天刷题…

FreeRTOS源码分析

目录 1、FreeRTOS目录结构 2、核心文件 3、移植时涉及的文件 4、头文件相关 4.1 头文件目录 4.2 头文件 5、内存管理 6、入口函数 7、数据类型和编程规范 7.1 数据类型 7.2 变量名 7.3 函数名 7.4 宏的名 1、FreeRTOS目录结构 使用 STM32CubeMX 创建的 FreeRTOS 工…

Ubuntu服务器搭建Git远程仓库

本文所述方法适用于小型团队在局域网环境中使用Git进行代码版本管理。 1. 安装Git 打开终端(Ctrl + Alt + T) ,输入以下命令: sudo apt update #更新软件包列表信息 sudo apt install git #安装Git 验证Git是否安装成功,可以查看Git版本: git --version 也需…

shell中的流程控制

条件判断在流程控制中的重要性 有了条件判断才能进行if判断即分支流程&#xff0c;才能进行case的多分支流程&#xff0c;才能进行for循环和while循环。 单分支流程判断 如上图所示&#xff0c;在shell编程中常使用英文状态下的分号来在Linux控制台一次性执行多条命令&#x…

无线领夹麦克风哪个牌子好用?一文揭秘哪种领夹麦性价比最高!

​无线领夹麦克风&#xff0c;无疑是现代音频技术的杰出代表。它摆脱了传统有线麦克风的束缚&#xff0c;让声音的传播更加自由、灵活。无论是追求极致音质的音乐爱好者&#xff0c;还是需要高效沟通的商务人士&#xff0c;无线领夹麦克风都能满足你的需求&#xff0c;让你的声…

513、找二叉树左下角的值

题解&#xff1a;层序遍历简单&#xff0c;此篇记录递归法&#xff0c;要注意左下角的值并不一定是左叶子节点&#xff0c;遍历思路形象化就是按先左后右的顺序遍历每一条分支&#xff0c;若遍历到叶子结点&#xff0c;看此时深度有没有超过之前的值&#xff0c;超过了就记录下…

Jlink下载固件到RAM区

Jlink下载固件到RAM区 准备批处理搜索exe批处理调用jlink批处理准备jlink脚本 调用执行 环境&#xff1a;J-Flash V7.96g 平台&#xff1a;arm cortex-m3 准备批处理 搜索exe批处理 find_file.bat echo off:: 自动识别脚本名和路径 set "SCRIPT_DIR%~dp0" set &qu…

TIME_WAIT的危害

前言 该文章主要讨论下TIME_WAIT的存在意义和潜在危害&#xff0c;以及解决措施。 具体内容 首先看一下下面这幅图 这幅图来自《TCP IP详解卷1&#xff1a;协议 原书第2版中文》TCP状态变迁图。 TIME_WAIT存在意义 可靠的终止TCP连接。 保证让迟来的TCP报文有足够的时间被…

数据库 | 试卷四

1.数据库系统的特点是 数据共享、减少数据冗余、数据独立、避免了数据不一致和加强了数据保护 2.关系模型的数据结构是二维表结构 3.聚簇索引 cluster index 4. 这里B&#xff0c;C都是主属性&#xff0c;所以B->C不是非主属性对码的部分函数依赖 候选键&#xff08;AC&a…

光电液位传感器在净水器领域的应用优势有哪些?

光电液位传感器作为一种先进的液位检测技术&#xff0c;在净水器领域有着显著的应用优势。具有高精度的特点&#xff0c;能够精确地检测水位变化&#xff0c;保证水处理过程的稳定性和效率。 传统的浮球式传感器可能存在精度偏差或者在长期使用中需要维护和更换的问题&#xf…

nginx+tomcat负载均衡、动静分离群集【☆☆☆☆☆】

Nginx是一款非常优秀的HTTP服务器软件&#xff0c;性能比tomcat更优秀&#xff0c;它支持高达50 000个并发连接数&#xff0c;拥有强大的静态资源处理能力&#xff0c;运行稳定&#xff0c;内存、CPU等系统资源消耗非常低。目前很多大型网站都应用Nginx服务器作为后端网站程序的…

机器学习课程复习——隐马尔可夫

不考计算题 Q:概率图有几种结构? 条件独立性的公式? 顺序结构发散结构汇总结构Q:隐马尔可夫模型理解? 概念 集合:状态集合、观测集合 序列:状态序列、观测序列

你不知道的MySQL备份和还原技巧,速来学习!

01、mysql备份数据库 1、mysql备份单个数据库 #mysql备份某个库格式&#xff1a; mysqldump -h主机名 -P端口 -u用户名 -p"密码" --database 数据库名 > 文件名.sql#实例&#xff1a;mysql备份某个库&#xff1a; mysqldump -h10.*.*.9 -P3306 -uroot -p"密…

闹大了!高考作文“人工智能与AI”引发争议,专家喊话,部分考生家长无奈,直呼:“太不公平了!这哪里是考作文,分明是在考城乡差距啊!”

闹大了&#xff01;高考作文“人工智能与AI”引发争议&#xff0c;专家喊话&#xff0c;部分考生家长无奈&#xff0c;直呼&#xff1a;“太不公平了&#xff01;这哪里是考作文&#xff0c;分明是在考城乡差距啊&#xff01;” ​高考&#xff0c;本该是最公平的战场&#xff…