数据结构 -- 并查集与图

目录

1.并查集

1.结构

2.原理

3.代码实现

1.存储

2.寻找根节点

3.是否为同一集合

4.求集合个数

5.合并为同一集合中

整体代码

2.图

1.基本知识

1.各个属性

2.特殊名词

3.图的解释

2.图的表示

1.邻接矩阵

2.邻接表

3.图的遍历

1.BFS--广度优先遍历

2.DFS--深度优先遍历


1.并查集

定义:n个元素被划分在不相交的集合中,一旦出现某几个元素可以形成一个相同的集合内,那么此时就会被该结构放入同一个集合内.该集合的好处就是当需要查询就能得到某个元素是属于哪个集合中.

1.结构

其实无非就是用于记载是否存在在哪个集合中,那么只需要有一个能存储某个位置对应的集合,而位置是唯一的,那么每一个位置其实就已经代表特定的元素.

1.为了表示存储元素的位置,那么就需要一个vector

2.每个位置对应的元素,那么只需要hash表即可表示特定的元素的特定下标

2.原理

1.其实我们可以把并查集表示为森林

2.如果下标对应的元素为正数,则表示和元素对应下标的元素为同一集合;如果下标对应的元素为负数,表示当前为森林的顶端

3.如果我们已经知道每个下标代表的意义,那么不需要哈希表进行表示.如果需要对下标进行解析,可以使用hash对元素和下标进行映射

3.代码实现

1.存储

class UnionFindSet
{
private:
    vector<int> _ufs;
public:
    UnionFindSet(size_t n)
	    :_ufs(n, -1)
    {}
}

1.先设置一个vector

2.构造时,需要多少元素就设置多少,并且初始化为-1,表示当前的元素独立

2.寻找根节点

int FindRoot(int x)
{
	int root = x;
	while (_ufs[root] >= 0)
		root = _ufs[root];
	while (_ufs[x] >= 0)
	{
		int parent = _ufs[x];
		_ufs[x] = root;
		x = parent;
	}
	return root;
}

我们知道循着元素下标往上找就能找到我们需要的根节点并且实现压缩数据

3.是否为同一集合

bool InSet(int x1, int x2)
{
	return FindRoot(x1)== FindRoot(x2);
}

对比二者的根节点是否相同即可

4.求集合个数

size_t SetSize()
{
	size_t size = 0;
	for (size_t i = 0; i < _ufs.size(); i++)
	{
		if (_ufs[i] < 0) size++;
	}
	return size;
}

只需要根节点就可以了,根节点的特点就是元素为负数,那么只需要遍历vector,计算负数的个数就可以了.

5.合并为同一集合中

void Uinon(int x1, int x2)
{
	int root1 = FindRoot(x1);
	int root2 = FindRoot(x2);
	if (root1 == root2) return;
	if (abs(_ufs[root1]) < abs(_ufs[root2])) swap(root1, root2);
	_ufs[root1] += _ufs[root2];
	_ufs[root2] = root1;
}

1.找到两个元素对应的根节点,如果根节点相同则不需要结合

2.如果不相同,我们需要将数据量小的合并到数据量大的去

3.那么当前root1就是小的下标._ufs[root1] += _ufs[root2]表示当前的root1为根节点时的所有元素个数,_ufs[root2] = root1将root2的下标记作root1

整体代码

class UnionFindSet
{
private:
	vector<int> _ufs;
public:
	UnionFindSet(size_t n)
		:_ufs(n, -1)
	{}

	void Uinon(int x1, int x2)
	{
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		if (root1 == root2) return;
		if (abs(_ufs[root1]) < abs(_ufs[root2])) swap(root1, root2);
		_ufs[root1] += _ufs[root2];
		_ufs[root2] = root1;
	}

	int FindRoot(int x)
	{
		int root = x;
		while (_ufs[root] >= 0)
			root = _ufs[root];
		while (_ufs[x] >= 0)
		{
			int parent = _ufs[x];
			_ufs[x] = root;
			x = parent;
		}
		return root;
	}

	bool InSet(int x1, int x2)
	{
		return FindRoot(x1)== FindRoot(x2);
	}

	size_t SetSize()
	{
		size_t size = 0;
		for (size_t i = 0; i < _ufs.size(); i++)
		{
			if (_ufs[i] < 0) size++;
		}
		return size;
	}
};

2.图

1.基本知识

1.各个属性

图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V,E)

1.顶点集合:V = {x|x属于某个数据对象集}是有穷非空集合
2.边集合:E = {(x,y)|x,y属于V}或者E={<x, y>|x,y属于V&&Path(x, y)}是顶点间关系的有穷集合.

3.图的种类可分为:有向图和无向图

2.特殊名词

1.完全图:每一个顶点都互相有一个边联通的叫完全图。[其中在有n个顶点的无向图中,若有n * (n-1)/2条边,即任意两个顶点之间有且仅有一条边,则称此图为无向完全图;在n个顶点的有向图中,若有n * (n-1)条边,即任意两个顶点之间有且仅有方向相反的边,则称此图为有向完全图]

2.邻接顶点:两个顶点通过一个边可互相抵达的。[在无向图中G中,若(u, v)是E(G)中的一条边,则称u和v互为邻接顶点,并称边(u,v)依附于顶点u和v;在有向图G中,若<u, v>是E(G)中的一条边,则称顶点u邻接到v,顶点v邻接自顶点u,并称边<u, v>与顶点u和顶点v相关联]

3.顶点的度:顶点v的度是指与它相关联的边的条数,记作deg(v)。[在有向图中,顶点的度等于该顶点的入度与出度之和,其中顶点v的入度是以v为终点的有向边的条数,记作indev(v);顶点v的出度是以v为起始点的有向边的条数,记作outdev(v)。因此:dev(v) = indev(v) + outdev(v)。注意:对于无向图,顶点的度等于该顶点的入度和出度,即dev(v) = indev(v) = outdev(v)]

4.路径:在图G = (V, E)中,两顶点可经过若干边抵达的,就叫路劲。若从顶点vi出发有一组边使其可到达顶点vj,则称顶点vi到顶点vj的顶点序列为从顶点vi到顶点vj的路径。

5.路径长度:对于不带权的图,一条路径的路径长度是指该路径上的边的条数;对于带权的图,一条路径的路径长度是指该路径上各个边权值的总和

3.图的解释

1.简单路径与回路:若路径上各顶点v1,v2,v3,…,vm均不重复,则称这样的路径为简单路径;若路径上第一个顶点v1和最后一个顶点vm重合,则称这样的路径为回路或环。
2.子图:设图G = {V, E}和图G1 = {V1,E1},若V1属于V且E1属于E,则称G1是G的子图
3.连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图

4.强连通图:在有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj到vi的路径,则称此图是强连通图

5.生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边。

2.图的表示

1.邻接矩阵

1.适合存储非常稠密的图

2.邻接矩阵可以O(1)判断两个顶点之间的连接关系

3.一个点的所有边不方便找

namespace Matrix
{
	template<class V, class W, W MAX_W = INT_MAX, bool Direction = false>
	class Graph
	{
	public:
		//图的创建 -- 1.IO  2.关系放在文件中  3.手动添加边
		Graph(const V* a, size_t n)
		{
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(a[i]);
				_indexMap[a[i]] = i;
			}
			_matrix.resize(n);
			for (size_t i = 0; i < n; ++i)
			{
				for (size_t j = 0; j < n; ++j)
				{
					_matrix.resize(n, MAX_W);
				}
			}
		}

		size_t GetVertexIndex(const V& v)
		{
			auto it = _indexMap.find(v);
			if (it == _indexMap.end())
			{
				throw invalid_argument("顶点不存在");
				return -1;
			}
			else
			{
				return it->second;
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
			_matrix[srci][dsti] = w;
			if (Direction == false)
			{
				_matrix[dsti][srci] = w;
			}
		}

		void Print()
		{
			// 打印顶点和下标映射关系
			for (size_t i = 0; i < _vertexs.size(); ++i)
			{
				cout << _vertexs[i] << "-" << i << " ";
			}
			cout << endl;

			cout << "  ";
			for (size_t i = 0; i < _matrix.size(); i++)
			{
				cout << i << " ";
				for (size_t j = 0; j < _matrix[0].size(); j++)
				{
					if (_matrix[i][j] == MAX_W)
						cout << "* ";
					else
						cout << _matrix[i][j] << " ";
				}
				cout << endl;
			}
			cout << endl;
		}

	private:
		vector<V> _vertexs; //保存顶点
		map<V, int> _indexMap; //顶点与下标映射
		vector<vector<W>> _matrix; //邻接矩阵
	};
}

2.邻接表

1.适合比较稀疏的图

2.适合找到一个顶点的所有边

3.不适合判断两个顶点之间的连接关系

namespace link_table
{
	template<class W>
	struct Edge
	{
		int _dsti; //目标点下标
		W _w; //权值
		Edge<W>* _next;
		Edge(int dsti,const int w)
			:_dsti(dsti),
			_w(w),
			_next(nullptr)
		{}
	};

	template<class V, class W, bool Direction = false>
	class Graph
	{
		typedef Edge<W> Edge;
	public:
		Graph(const V* a, size_t n)
		{
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(a[i]);
				_indexMap[a[i]] = i;
			}

			_table.resize(n, nullptr);
		}

		size_t GetVertexIndex(const V& v)
		{
			auto it = _indexMap.find(v);
			if (it == _indexMap.end())
			{
				throw invalid_argument("顶点不存在");
				return -1;
			}
			else
			{
				return it->second;
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
			//srci->dsti
			Edge* eg = new Edge(dsti, w);
			eg->_next = _table[srci];
			_table[srci] = eg;

			//无向图 dsti->srci
			if (Direction == false)
			{
				Edge* eg = new Edge(srci, w);
				eg->_next = _table[dsti];
				_table[dsti] = eg;
			}
		}

		void Print()
		{
			// 打印顶点和下标映射关系
			for (size_t i = 0; i < _vertexs.size(); ++i)
			{
				cout << _vertexs[i] << "-" << i << " ";
			}
			cout << endl;

			for (size_t i = 0; i < _table.size(); i++)
			{
				cout << _vertexs[i] << "[" << i << "]->";
				Edge* cur = _table[i];
				while (cur)
				{
					cout << "{" << _vertexs[cur->_dsti] << "[" << cur->_dsti << "]" << cur->_w << "}->";
					cur = cur->_next;
				}
				cout << "{nullptr}" << endl;
			}
		}
	private:
		vector<V> _vertexs; //保存顶点
		map<V, int> _indexMap; //顶点与下标映射
		vector<Edge*> _table; //邻接表
	};
}

3.图的遍历

1.BFS--广度优先遍历

1.bfs的实现其实就是基于队列实现的

2.先将第一个顶点push到队列中,那么我们就可以基于该顶点进行遍历.需要注意的是,我们需要一个表来查看走到的点是否已经遍历过了

3.之后的循环,每次都查看每一层的顶点,并且将下一层的相邻顶点连接.遍历过的点pop掉并且将其访问的点进行标记已经访问

		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;

			size_t n = _vertexs.size();
			while (!q.empty())
			{
				// 一层一层出
				for (int i = 0; i < levelSize; ++i)
				{
					int front = q.front();
					q.pop();
					cout << front << ":" << _vertexs[front] << " ";
					// 把front顶点的邻接顶点入队列
					for (size_t i = 0; i < n; ++i)
					{
						if (_matrix[front][i] != MAX_W)
						{
							if (visited[i] == false)
							{
								q.push(i);
								visited[i] = true;
							}
						}
					}
				}
				cout << endl;

				levelSize = q.size();
			}

			cout << endl;
		}

2.DFS--深度优先遍历

将当前访问的点进行遍历,随后标记为访问过了,循环该点的其他相邻点,同样的逻辑

		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[srci][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);
		}

这样的代码其实有弊端,就是只能遍历相邻的点.一旦该图不是连通图就会出现只能访问一部分.那么就需要在DFS处遍历每一个点,如果没有被_DFS遍历标记过就需要再一次进行_DFS遍历

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

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

相关文章

LabVIEWL实现鸟巢等大型结构健康监测

LabVIEWL实现鸟巢等大型结构健康监测 管理国家地震防备和减灾的政府机构中国地震局(CEA)选择了七座新建的巨型结构作为结构健康监测(SHM)技术的测试台。这些标志性建筑包括北京2008年夏季奥运会场馆&#xff08;包括北京国家体育场和北京国家游泳中心&#xff09;、上海104层的…

Http协议(Hyper Text Transfer Protocol)

Http协议(Hyper Text Transfer Protocol) 这是一种超文本传输协议&#xff0c;规定了浏览器与服务器中间数据传输的规则 特点&#xff1a; 基于TCP协议&#xff1a;面向连接&#xff0c;安全基于请求-响应模型&#xff1a;一次请求对应一次响应http协议是无状态的协议&#…

通过网易的API完成一个简易的音乐播放器

效果图 工程环境 1、使用node在本地部署网易云音乐API接口 下载解压 链接:https://pan.baidu.com/s/1YQiMJoUMEYlMz14FH5xxRA?pwd36o5 提取码:36o5 工程目录概览 &#xff08;js文件夹里面放了music.html和main.js和vue.js&#xff09; 工程目录)&#xff08;有点重复…

每日一题:LeetCode-103/107.二叉树的(层序/锯齿形层序)遍历

每日一题系列&#xff08;day 04&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

微服务学习|初识elasticsearch、操作索引库、文档操作、RestClient操作索引库、RestClient操作文档

初识elasticsearch 什么是elasticsearch&#xff1f; elasticsearch是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。 elasticsearch结合kibana、Logstash、Beats&#xff0c;也就是elastic stack (ELK)。被广泛应用在日志数据分析、实…

Oracle 11g安装过程

文章目录 前言1.下载安装包2.安装2.1本地安装文件2.2 安装过程 3.查看是否安装成功3.1 查看oracle是否安装成功3.2 查看oracle服务 前言 本文仅用于记录亲自安装oracle的过程 1.下载安装包 官网地址&#xff1a; Oracle Database 11g Release 2 (11.2.0.1.0) 注意&#xff…

函数的极值与最值

函数的最值 1.闭区间上连续函数的最值 1.求驻点或不可导点&#xff08;可能的极值点&#xff09; 2.求函数在驻点&#xff0c;不可导点&#xff0c;端点的函数值 3.比较大小 例题&#xff1a; 例题思想&#xff1a;分段函数分段点必须验证导数的存在性 几种常见的最值类型 1.…

不同类型的开源许可证

不同类型的开源许可证 什么是开源许可证 最简单的解释是&#xff0c;开源许可证是计算机软件和其他产品的许可证&#xff0c;允许在定义的条款和条件下使用、修改或共享源代码、蓝图或设计。开源并不意味着该软件可以根据需要使用、复制、修改和分发。根据开源许可证的类型&a…

群晖安装portainer

一、下载镜像 打开【Container Manager】 ,搜索portainer&#xff0c;双击【6053537/portainer-ce】下载汉化版本 二、创建映射文件夹 打开【File Station】&#xff0c;在docker目录下创建【portainer】文件夹 三、开启SSH 群晖 - 【控制面板】-【终端机和SNMP】 勾选【启动…

36.JavaScript补完计划:typescript

点赞收藏加关注&#xff0c;你也能住大别墅&#xff01; 一、什么是typescript 二、应用场景 我认为JavaScript的特点就是在于它强大的延展性&#xff0c;不仅蔓延到了后端&#xff0c;而且也逐渐成为代码世界无法被忽视的存在。那么&#xff0c;编写js代码时我们都会经常遇到…

Echarts tooltip配置项的属性 图表悬浮框

这个小图标就是tooltip的配置项 tooltip:{} //默认样式 自定义显示数据 如果没有自定义的属性可以 只是写data [1254,1551,574,10]… series: {//图表配置项 如大小&#xff0c;图表类型name: 图表名字,type: bar,//图表类型data: [{value: 454,time: 2012-11-12},{value: 898…

easyrecovery 16数据恢复软件2024最新免费下载地址

EasyRecovery 16是一款操作简单、功能强大数据恢复软件,通过easyrecovery可以从硬盘、光盘、U盘、数码相机、手机等各种设备中恢复被删除或丢失的文件、图片、音频、视频等数据文件。 EasyRecovery Pro 16安装步骤 一、首先需要在该页找到下载地址处选任意地址将EasyRecovery软…

小间距LED屏幕需要解决的五大芯片问题

随着微距LED电子显示屏的像素间距逐渐缩小&#xff0c;对封装技术提出了更高的要求&#xff0c;LED灯珠和芯片尺寸也需要进一步减小。由此引发的显示性能、产品品质、一次性通过率、亮度和灰度等问题都需要通过先进芯片技术来解决。那么&#xff0c;什么是微距LED显示屏&#x…

JavaScript基础知识总结

1.前提 Html是一种标记语言&#xff0c;用来结构化我们的网页内容并赋予内容含义&#xff0c;例如定义段落、标题和数据表&#xff0c;或在页面中嵌入图片和视频 Css是一种样式规则语言&#xff0c;可将样式应用于 HTML 内容&#xff0c;例如设置背景颜色和字体&#xff0c;在多…

BUUCTF-pwn-ciscn_2019_ne_51

简单查看保护&#xff1a; 32为程序没有canary没有PIE&#xff0c;应该是简单的栈溢出。我们照着这个思路去找溢出点在哪&#xff0c;运行下程序看看什么情况&#xff1a; 程序上来是输入一个密码验证。随便输入下错误直接退出。因此我们需要到IDA中看看怎么回事&#xff1a; 主…

tcp/ip协议 error=10022 Winsock.reg Winsock2.reg

tcp/ip协议 error10022 这2个注册表选项千万不能删除&#xff0c;否则上不了网。 按下windows键R键&#xff0c;输入regedit&#xff0c;打开注册表&#xff0c;在文件目录里找到如下两个文件夹&#xff0c;删除这两个文件夹。 路径&#xff1a;HKEY_LOCAL_MACHINE\System\C…

微信小程序获取手机号上限,怎么处理比较省钱

微信新规 微信2023年改了规则&#xff0c;原本免费的小程序获取手机号&#xff0c;现在如果要获取要1分钱一条。 有些小程序的用户非常恐怖&#xff0c; 比如一些工具类的&#xff0c; 群发类的。如果进入小程序就必须要获取小程序&#xff0c;就像是无底洞&#xff0c;让运营…

enote笔记法之附录1——“语法词”(即“关联词”)(ver0.24)

enote笔记法之附录1——“语法词”&#xff08;即“关联词”&#xff09;&#xff08;ver0.24&#xff09; 最上面的是截屏的完整版&#xff0c;分割线下面的是纯文字版本&#xff1a; 作者姓名&#xff08;本人的真实姓名&#xff09;&#xff1a;胡佳吉 居住地&#xff1…

HCIE 01:基于前缀列表的BGP ORF功能

当运行BGP协议的某台设备上&#xff0c;针对入方向配置了基于ip-prefix的路由过滤&#xff0c;过滤了邻居发送的路由&#xff1b; 目前想&#xff0c;通过在peer关系的两端设备上都配置ORF功能&#xff0c;实现路由发送端只能送路由接收端过滤后的路由&#xff1b; ORF功能的说…

Java实现通过经纬度求两个任意地点在球面上的距离

我们在实际开发中会获取对应的经纬度&#xff0c;可以使用ES大数据搜索引擎进行计算对应区域的数据&#xff0c;那我们在如何根据两个经纬度获取对应的球面距离&#xff0c;就是在地球上从一个地点到另一个地点的直线距离 工具类如下: public class GeoUtils {// 地球半径&am…