基于C++的带权无向图的实现 (五)- 连通图和连通分量

2023-11-19

该系列文章是本人整理的有关带权无向图的数据结构和算法的分析与实现,若要查看源码可以访问我的github仓库,如有问题或者建议欢迎各位指出。

目录

基于C++的带权无向图的实现 (一)- 数据结构
基于C++的带权无向图的实现 (二)- 遍历算法
基于C++的带权无向图的实现 (三)- Prim最小生成树算法
基于C++的带权无向图的实现 (四)- Dijkstra最短路径算法
基于C++的带权无向图的实现 (五)- 连通图和连通分量
基于C++的带权无向图的实现 (六)- 关节点算法

连通图

在图G = {V, {E} } 中,若对于任何两个顶点u, v 都存在从v到u的路径,则称G是连通图,如下所示:

在这里插入图片描述
如何测试一个图是否连通呢,很简单,只需要使用图的遍历(深度优先或广度优先)即可,如果从一个顶点出发可以访问到图中所有顶点,那么该图就是连通图。


连通分量

百度百科对连通分量的描述为:

无向图 G的一个极大连通子图称为 G的一个连通分量(或连通分支)。连通图只有一个连通分量,即其自身;非连通的无向图有多个连通分量。

极大连通子图的意思为:该子图是G的连通子图。如果将图G的任何不存在与该子图中的顶点放入,该子图将不再连通,也就是说就是新加入的这个顶点没有到达该子图中任何一个顶点的路径。

例如,对于下面的非连通图,存在极大连通子图g1和g2,他们都是图G的连通分量。
在这里插入图片描述

假如我移除顶点2与顶点5的边后再将其放到子图g1中,那么子图g1就不是图G的极大连通子图了,因为子图g1中的顶点1,3,4都没有能到达顶点2的路径,如下所示:

在这里插入图片描述

那么找到图中的连通分量的算法该如何实现呢?其实上文测试该图是否为连通图一样,只需要用到图的遍历算法即可:

  1. 当所有的顶点尚未全部访问时:
    1. 选择一个未访问的顶点使用遍历算法(深度优先或者广度优先)
    2. 将这个顶点能到达的所有顶点标记为已访问,并且把他们放到一个连通分量的数组中。

本文将通过深度优先遍历来求图中的连通分量。


代码实现

在Graph类中除了上节内容实现的功能外,额外添加了求连通图的算法,T为提前定义好的模板:


函数名 用途
vector<vector> get_connected_components() 求连通分量
void print_connected_components( const vector<vector>& connected_components ) 打印连通分量

  1. 边的定义(edge.hpp):
template <typename T>
class Edge {
public:
	T vertex;
	int weight;

	Edge(T neighbour_vertex) {
		this->vertex = neighbour_vertex;
		this->weight = 0;
	}

	Edge(T neighbour_vertex, int weight) {
		this->vertex = neighbour_vertex;
		this->weight = weight;
	}

	bool operator<(const Edge& obj) const {
		return obj.vertex > vertex;
	}

	bool operator==(const Edge& obj) const {
		return obj.vertex == vertex;
	}
};
  1. 图的定义(graph.hpp)
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<limits.h>
#include "edge.hpp"
using namespace std;

template <typename T>
class Graph {
public:
	map<T, set<Edge<T>>> adj;  /* 邻接表 */

	bool contains(const T& u); /* 判断顶点u是否在图中 */
	bool adjacent(const T& u, const T& v); /* 判断顶点u和v是否相邻 */

	void add_vertex(const T& u); /* 添加顶点 */
	void add_edge(const T& u, const T& v, int weight); /* 添加边和权重 */

	void change_weight(const T& u, const T& v, int weight); /* 修改权重 */

	void remove_weight(const T& u, const T& v); /* 移除权重 */
	void remove_vertex(const T& u); /* 移除顶点 */
	void remove_edge(const T& u, const T& v); /* 移除边 */

	int degree(const T& u); /* 求顶点的度数 */
	int num_vertices(); /* 求图中顶点的总数 */
	int num_edges(); /* 求图中边的总数*/
	int largest_degree(); /* 求图中的最大度数 */

	int get_weight(const T& u, const T& v); /* 得到某两个顶点之间边的权重 */
	vector<T> get_vertices(); /* 得到图中所有顶点 */
	map<T, int> get_neighbours(const T& u); /* 得到顶点u的所有边 */

	void show();

	void dft_recursion(const T& u, set<T>& visited, vector<T>& result); /* 深度优先遍历递归辅助函数 */
	vector<T> depth_first_rec(const T& u); /* 深度优先遍历递归法 */
	vector<T> depth_first_itr(const T& u); /* 深度优先遍历迭代法*/
	vector<T> breadth_first(const T& u); /* 广度优先遍历迭代法 */

	Graph<T> prim(T v); /* prim最小生成树算法 */

	map<T, int> dijkstra(T start); /*  dijkstra最短路径算法 */

	vector<vector<T>>  get_connected_components(); /* 获得图中的连通分量 */
	void  print_connected_components(const vector<vector<T>>& connected_components); /* 打印连通分量 */
};

由于图的函数声明除了最后两个函数其他的都在前四节中实现了,所以这里只放求连通分量和打印连通分量信息的代码(graph.hpp):

template <typename T> vector<vector<T>> Graph<T>::get_connected_components() {
	set<T> visited_vertices;
	vector<vector<T>> connected_components;

	for (auto vertex : adj) {

		// 对每一个未访问过的顶点进行深度优先遍历
		if (visited_vertices.find(vertex.first) == visited_vertices.end()) {
			stack<T> s;
			s.push(vertex.first);

			// 定义一个临时变量"connected_component"用来存储当前连通分量中的顶点
			vector<T> connected_component;
			
			// 深度优先遍历
			while (!s.empty()) {
				T u = s.top();
				s.pop();

				// 将未访问过的顶点放入连通分量"connected_component"中
				if (visited_vertices.find(u) == visited_vertices.end()) {
					connected_component.push_back(u);
					visited_vertices.insert(u);
				}

				// 当前顶点未访问过的邻居入栈
				for (auto neighbour : adj[u])
					if (visited_vertices.find(neighbour.vertex) == visited_vertices.end())
						s.push(neighbour.vertex);
			}
			// 将连通分量放到连通分量的集合"connected_components"中
			connected_components.push_back(connected_component);

		}
	}

	return connected_components;
}

template <typename T> void Graph<T>::print_connected_components(const vector<vector<T>>& connected_components ) {
	int number = connected_components.size();
	if(number == 1)  cout << "该图是连通图,只有一个连通分量,就是其自身" << endl;
	else if (number > 1){
		cout << "图中共有" << number << "个连通分量" << endl;
		for (unsigned i = 0; i < connected_components.size(); i++) {
			cout << "第" << i + 1 << "个连通分量中的顶点分别为:";
			for (unsigned j = 0; j < connected_components[i].size(); j++) {
				cout << " " << connected_components[i][j];
			}
			cout << endl;
		}
	}
}

测试

测试案例(graph_testing.cpp):

void test05(Graph<int> g) {
    vector<vector<int>> connected_components = g.get_connected_components();
    g.print_connected_components(connected_components);
}

int main()
{
    //Graph<char> g;
    //g.add_vertex('A');
    //g.add_vertex('B');
    //g.add_vertex('C');
    //g.add_vertex('D');
    //g.add_vertex('E');
    //g.add_vertex('F');
    //g.add_vertex('G');

    //g.add_edge('A', 'B', 7);
    //g.add_edge('A', 'D', 5);
    //g.add_edge('B', 'C', 8);
    //g.add_edge('B', 'D', 9);
    //g.add_edge('B', 'E', 7);
    //g.add_edge('C', 'E', 5);
    //g.add_edge('D', 'E', 15);
    //g.add_edge('D', 'F', 6);
    //g.add_edge('E', 'F', 8);
    //g.add_edge('E', 'G', 9);
    //g.add_edge('F', 'G', 11);

    //g.add_vertex('H');
    //g.add_edge('B', 'H', 9);
    //g.add_edge('A', 'H', 10);
    //g.add_edge('D', 'H', 11);
    //g.add_edge('A', 'H', 12);
    //g.remove_vertex('H');
    //cout << "打印图中顶点及其邻接表的详细信息如下" << endl;
    //g.show();
    //cout << endl;
    
    // test01(g);
    // test02(g);
    // test03(g);
    // test04(g);
  
    Graph<int> g;

    g.add_vertex(0);
    g.add_vertex(1);
    g.add_vertex(2);
    g.add_vertex(3);
    g.add_vertex(4);
    g.add_vertex(5);
    g.add_vertex(6);
    g.add_vertex(7);
    g.add_vertex(8);
    g.add_vertex(9);
    g.add_vertex(10);
    g.add_vertex(11);
    g.add_vertex(12);

    g.add_edge(0, 1 , 1);
    g.add_edge(0, 2, 1);
    g.add_edge(1, 2, 1);
    g.add_edge(1, 3, 1);
    g.add_edge(1, 4, 1);
    g.add_edge(3, 6, 1);
    g.add_edge(4, 5, 1);
    g.add_edge(7, 8, 1);
    g.add_edge(7, 9, 1);
    g.add_edge(7, 12, 1);
    g.add_edge(8, 9, 1);
    g.add_edge(8, 12, 1);
    g.add_edge(9, 10, 1);
    g.add_edge(9, 11, 1);
    g.add_edge(9, 12, 1);
    g.add_edge(10, 11, 1);
    cout << "打印图中顶点及其邻接表的详细信息如下" << endl;
    g.show();
    cout << endl;

    test05(g);
    return 0;
}

测试对应的图G如下,由于该图是无权无向图,所以main函数中的权重都设为1:
在这里插入图片描述


运行结果:
在这里插入图片描述

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

基于C++的带权无向图的实现 (五)- 连通图和连通分量 的相关文章

  • 我可以使用反射更改 C# 中的私有只读字段吗?

    我想知道 由于很多事情都可以使用反射完成 我可以在构造函数完成执行后更改私有只读字段吗 注 只是好奇 public class Foo private readonly int bar public Foo int num bar num
  • 从 SQL 数据库获取日期时间

    我的数据库表中有一个 DateTime 记录 我编写一个查询从数据库中获取它 string command2 select Last Modified from Company Data where Company Name Descrip
  • Web UI 中的 .Result 出现死锁

    我正在阅读以下主题http blog stephencleary com 2012 07 dont block on async code html http blog stephencleary com 2012 07 dont bloc
  • 如何知道并加载特定文件夹中的所有图像?

    我有一个应用程序 C Builder 6 0 需要知道特定文件夹中的图像总数 然后我必须加载它们 在 ImageList 或 ComboBoxEx 中 或任何其他控件中 我怎样才能做到这一点 我知道如何在控件中加载图像 或保存在 TList
  • 有没有比这更快的方法来查找目录和所有子目录中的所有文件?

    我正在编写一个程序 需要在目录及其所有子目录中搜索具有特定扩展名的文件 这将在本地驱动器和网络驱动器上使用 因此性能是一个问题 这是我现在使用的递归方法 private void GetFileList string fileSearchP
  • 在 Windows Phone 上启动 pdf 文件时出现 System.Runtime.InteropServices.COMException

    我正在尝试使用我之前在另一个应用程序上使用过的以下工作代码打开 pdf 文件 但这一次 当流程到达此行时 我收到 System Runtime InteropServices COMException Windows System Laun
  • Linq Where 本地计数器关闭在 VS watch 中的结果不同

    我尝试删除前 3 个元素array与 LinQWhere扩展功能 这是一个例子 var array new 1 2 3 4 5 6 7 8 9 var count 3 var deletedTest1 0 var test1 array W
  • 控制台应用程序 .net Core 2.0 的配置

    在 net Core 1 中我们可以这样做 IConfiguration config new ConfigurationBuilder AddJsonFile appsettings json true true Build 这样就可以使
  • 如何检测斑点并将其裁剪成 png 文件?

    我一直在开发一个网络应用程序 我陷入了一个有问题的问题 我会尝试解释我想要做什么 在这里您看到第一个大图像 其中有绿色形状 我想要做的是将这些形状裁剪成不同的 png 文件 并使它们的背景透明 就像大图像下面的示例裁剪图像一样 第一张图像将
  • 抽象类和接口之间的区别[重复]

    这个问题在这里已经有答案了 可能的重复 接口与基类 https stackoverflow com questions 56867 interface vs base class 我不明白抽象类和接口之间的区别 我什么时候需要使用哪种字体
  • C#:如何使用 SHOpenFolderAndSelectItems [重复]

    这个问题在这里已经有答案了 有人可以举例说明如何使用 shell 函数吗SH打开文件夹并选择项目 http msdn microsoft com en us library bb762232 VS 85 aspx来自 C 我不太明白如何使用
  • C++ Primer 5th Edition 错误 bool 值没有指定最小大小?

    bool 的最小大小不应该是 1 个字节吗 这有点学术性的东西 尽管它们会转换为数字 并且 与其他所有事物一样 它们最终将基本上由计算机内存中的数字表示 但布尔值不是数字 你的bool可以取值true 或值false 即使您确实需要至少 1
  • 编写专门用于类及其子类的函数模板

    我正在尝试编写一个函数模板 一个版本应该用于不满足另一版本标准的所有类型 当参数是给定类的基类或该类本身时 应使用另一个版本 我尝试过超载Base 但是当类派生自Base 他们使用通用的 而不是特定的 我也尝试过这种 SFINAE 方法 s
  • OpenMP C 程序运行速度比顺序代码慢

    我是 OpenMP 的新手 正在尝试并行化 Jarvis 的算法 然而事实证明 与顺序代码相比 并行程序花费的时间要长 2 3 倍 难道问题本身就不能并行化吗 或者我并行化它的方式有问题 这是我针对该问题的 openMP 程序 其中有 2
  • C# ToString("MM/dd/yy") 删除前导 0 [重复]

    这个问题在这里已经有答案了 可能的重复 格式化 NET DateTime Day 不带前导零 https stackoverflow com questions 988353 format net datetime day with no
  • 便携式终端

    有没有办法根据所使用的操作系统自动使用正确的 EOL 字符 我在想类似的事情std eol 我知道使用预处理器指令非常容易 但很好奇它是否已经可用 我感兴趣的是 我的应用程序中通常有一些消息 稍后我会将这些消息组合成一个字符串 并且我希望将
  • 在 MVVM 中,可以在视图后面的代码中访问 ViewModel 吗?

    在 MVVM 模式中 是否可以接受甚至可以访问视图代码后面的 ViewModel 属性 我有一个可观察的集合 它填充在 ViewModel 中 我需要在视图中使用它来绑定到带有链接列表的无限滚动条 IE private LinkedList
  • 多个同名内存数据库

    关系到这个答案 https stackoverflow com a 48446491 596758 我试图通过设置让多个上下文工作UseInMemoryDatabase以同名 下面的测试失败 第二个上下文为空 我还需要做什么才能在内存数据库
  • 如何使复选框不可选择?

    我想知道你是怎么做的CheckBox在c 中无法选择 我认为这会是类似 SetSelectable false 之类的东西 但我似乎看不到该方法 I found CanSelect但这似乎是只读属性 您可以设置自动检查 http msdn
  • Windows 上 libcurl 的静态库[重复]

    这个问题在这里已经有答案了 如何将此库 libcurl 静态链接到 exe 我努力了 disable share enable static 没有帮助 我使用的是MingW32 有没有一种简单的方法来静态链接这个库 这样我的应用程序就不再有

随机推荐

  • Linux网络连接出现问题

    报错截图 1 先查看NetworkManager是否启动 查看NetworkManager是否启动 systemctl status NetworkManager 在Linux系统中 可以通过以下命令启动NetworkManager服务 s
  • 小程序项目时间选择器用法

    项目需求是要实现这种形式 但是相信大家都试了各种插件 都不太合适 uView框架也不能满足自己的需要 推荐使用 uview ui plus 基本上小程序遇到的单选多选 日期 省市区 都可以完美的实现 可以通过插件市场安装使用 但是要实现ui
  • matplotlib画动态三维图

    从txt文本中读取数据并画动态三维点图 程序中实现动态三维图绘制 添加图标题 坐标轴标题 坐标轴数值范围 两种绘图模式 一种动态画图 所有点均保留 另一种每次仅显示一个点 三维坐标轴设置区间 需要通过Axes3D创建ax 否则其他方式无法设
  • Openface的安装和使用

    openface的安装与使用 环境 我的电脑是笔记本电脑 win10系统 用的是pycharm和annaconda 一 首先下载openface安装包 并且安装 1 下载地址 https codeload github com cmusat
  • FeignClient 在 oauth2 中与 hystrix 线程策略冲突问题造成的权限问题

    FeignClient 在 oauth2 中与 hystrix 线程策略冲突问题造成的权限问题 FeignClient 在 oauth2 中与 hystrix 线程策略冲突问题造成的权限问题 问题描述 问题原因 问题解决方法 方法1 直接禁
  • 关系型数据库的规范化

    规范化是通过修改表以减少冗余和矛盾的一系列动作 关系型数据库定义了3中范式 第一范式 列仅包含原子值 没有重复的组 第二范式 满足第一范式 非部分函数依赖 如果表中一些组合键的 但不是全部 值确定了一个非键列的值 则表包含部分函数依赖 第二
  • LeetCode Java 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。

    给定一个整数数组 nums 和一个目标值 target 请你在该数组中找出和为目标值的那两个整数 并返回他们的数组下标 你可以假设每种输入只会对应一个答案 但是 你不能重复利用这个数组中同样的元素 方法一 遍历 看到这个题便想到数组遍历 就
  • 商城项目 pc----商品详情页

    目录 vue路由滚动行为 排他思想 放大镜 加入购物车操作 项目实战 Promise 特点 用法 then 执行顺序 拓展 async await Promise优缺点 Promise方法 浏览器缓存 为什么需要本地存储呢 window s
  • 思科路由器IPv6各种路由协议配置

    一 基础配置 R1 Router gt ena Router conf t Router config host R1 R1 config int g0 0 R1 config if ipv add 2001 3 1 64 R1 confi
  • Java多线程(四):什么是死锁以及如何解决死锁

    目录 1 什么是死锁 2 死锁产生的原因 3 如何解决死锁问题 3 1 改变环路等待条件 3 2 破坏请求并持有条件 1 什么是死锁 死锁 是指两个或两个以上的进程在执行过程中 由于竞争资源或者由于彼此通信而造成的一种阻塞的现象 若无外力作
  • 【微信小程序】定时器超时处理设置方法【setInterval()和setTimeout()】

    2020年2月13日 0次阅读 共550个字 0条评论 0人点赞 QueenDekimZ Set Timeout Solution setTimeout和setInterval 函数都属于定时任务 一 settimeout延迟一段时间执行函
  • css中float用法

    float浮动 指将指定元素悬浮于所在整体之上 即将垂直排列的元素转换为水平同行显示 平时写出的HTML是具有先后顺序的 对于这个顺序我们称之为标准流 而浮动则是脱离标准流的另一个独立标准 下面给出float定义 float left 左浮
  • 阿里云大佬告诉你为什么学不会设计模式,归根到底还是方法不对

    最近总有读者在后台跟我说 工作几年 自己的代码质量似乎没有什么提升 我觉得他的情况非常典型 很多人应该或多或少都有过类似的经历 毕业几年 几乎一直在做复制黏贴的工作 偶尔会遇到原有业务扩展的需求 想简单应付一下完事的话 也不难 无非就是多加
  • 查询水果价格 (15分)

    给定四种水果 分别是苹果 apple 梨 pear 桔子 orange 葡萄 grape 单价分别对应为3 00元 公斤 2 50元 公斤 4 10元 公斤 10 20元 公斤 首先在屏幕上显示以下菜单 1 apple 2 pear 3 o
  • Hadoop学习——MapReduce的简单介绍及执行步骤

    MapReduce概述 MapReduce是一个分布式的计算框架 编程模型 最初由由谷歌的工程师开发 基于GFS的分布式计算框架 后来Cutting根据 Google Mapreduce 设计了基于HDFS的Mapreduce分布式计算框架
  • IDEA修改内存未生效原因和解决

    修改IDEA安装目录下的idea64 exe vmoptions server Xms1024m Xmx2048m XX ReservedCodeCacheSize 2048m 发现IDEA的内存修改并未生效 右下角显示依然是974M 原因
  • windows下进程间通信的(13种方法)

    Windows进程间的通信 迎风的祺 博客园 windows下进程间通信的 13种方法 phymat nico的专栏 CSDN博客 windows进程间通信
  • eclipse报错 parameterized types are only available if source level is 1.5 or greater

    preface 好久没有更新 blog 了 最近在 写新的项目 今天在用eclipse 出现了之前遇到的问题 这里记录下 问题 eclipse 控制台 报错 parameterized types are only available if
  • CUDA+OPENCV+PYTHON tensorflow 源码环境搭建

    CUDA OPENCV PYTHON tensorflow 源码环境搭建 接上文caffe环境安装 https blog csdn net u012350025 article details 104589982 主机环境ubuntu18
  • 基于C++的带权无向图的实现 (五)- 连通图和连通分量

    该系列文章是本人整理的有关带权无向图的数据结构和算法的分析与实现 若要查看源码可以访问我的github仓库 如有问题或者建议欢迎各位指出 目录 基于C 的带权无向图的实现 一 数据结构 基于C 的带权无向图的实现 二 遍历算法 基于C 的带