数据结构 -- 并查集与图

目录

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 << "* ";elsecout << _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->dstiEdge* eg = new Edge(dsti, w);eg->_next = _table[srci];_table[srci] = eg;//无向图 dsti->srciif (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遍历

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spark on yarn 模式的安装与部署

任务描述 本关任务&#xff1a; Spark on YARN 模式的安装与部署。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; Spark 部署模式的种类&#xff1b;Spark on YARN 模式的安装。 Spark 部署模式 Spark 部署模式主要分为以下几种&#xff0c;Spark Stand…

2021年2月1日 Go生态洞察:VS Code Go扩展中默认启用Gopls

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

rss服务搭建记录

layout: post title: RSS subtitle: vps搭建RSS服务 date: 2023-11-27 author: Sprint#51264 header-img: img/post-bg-universe.jpg catalog: true tags: - 折腾 文章目录 引言RSShub-dockerRSS-radarFreshrssFluent reader获取fever api配置Fluent Reader同步 结语 引言 一个…

IDEA设置方法注释模板

目录 一.打开设置&#xff1a;File—>Settings... 二.选择Live Templates—>点击右侧 "" 号—>选择Template Group... 三.输入组名称&#xff0c;建议取容易理解的名字&#xff0c;点击OK 四.选中创建好的组&#xff0c;再次点击 "" 号&#…

“大型”基础模型中幻觉的调查

Abstract 基础模型 (FM) 中的幻觉是指生成偏离事实或包含捏造信息的内容。这篇调查论文广泛概述了近期旨在识别、阐明和解决幻觉问题的努力&#xff0c;特别关注“大型”基础模型&#xff08;LFM&#xff09;。该论文对LFM特有的各种类型的幻觉现象进行了分类&#xff0c;并建…

手撕A*算法(详解A*算法)

A*算法原理 全局路径规划算法&#xff0c;根据给定的起点和终点在全局地图上进行总体路径规划。 导航中使用A*算法计算出机器人到目标位置的最优路线&#xff0c;一般作为规划的参考路线 // 定义地图上的点 struct Point {int x,y; // 栅格行列Point(int x, int y):x(x),y(y){…

java学习part18抽象类

Java抽象类 详解-CSDN博客 111-面向对象(高级)-抽象类与抽象方法的使用_哔哩哔哩_bilibili 1.概念 2.抽象类 抽象类不能实例化&#xff0c;可以有属性&#xff0c;也可以有方法。 方法可以实现或者只声明不实现&#xff0c;要加一个abstract abstract class A{//定义一个抽…

springboot整合redis+自定义注解+反射+aop实现分布式锁

1.定义注解 import java.lang.annotation.*; import java.util.concurrent.TimeUnit;/** Author: best_liu* Description:* Date: 16:13 2023/9/4* Param * return **/ Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD}) Documented public interface RedisLo…

Go语言基础:包、函数、语句和注释解析

一个 Go 文件包含以下几个部分&#xff1a; 包声明导入包函数语句和表达式 看下面的代码&#xff0c;更好地理解它&#xff1a; 例子 package mainimport "fmt"func main() { fmt.Println("Hello World!") }例子解释 第 1 行&#xff1a; 在 Go 中&am…

基于SSM的仓库管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

15个超级实用的Python操作,肯定有你意想不到的!

文章目录 1&#xff09;映射代理&#xff08;不可变字典&#xff09;2&#xff09;dict 对于类和对象是不同的3) any() 和 all()4) divmod()5) 使用格式化字符串轻松检查变量6) 我们可以将浮点数转换为比率7) 用globals()和locals()显示现有的全局/本地变量8) import() 函数9) …

【LeetCode刷题】--90.子集II

90.子集II class Solution {public List<List<Integer>> subsetsWithDup(int[] nums) {List<List<Integer>> ans new ArrayList<>();List<Integer> list new ArrayList<>();//排序后便于去重Arrays.sort(nums);dfs(0,nums,ans,lis…

fastReID论文总结

fastReID论文总结 fastReIDReID所面临的挑战提出的背景概念&#xff1a;所谓ReID就是从视频中找出感兴趣的物体&#xff08;人脸、人体、车辆等&#xff09;应用场景&#xff1a;存在的问题&#xff1a;当前的很多ReID任务可复用性差&#xff0c;无法快速落地使用解决方式&…

基于SpringBoot实现的教务查询系统

一、系统架构 前端&#xff1a;html | js | css | jquery | bootstrap 后端&#xff1a;springboot | springdata-jpa 环境&#xff1a;jdk1.7 | mysql | maven 二、代码及数据库 三、功能介绍 01. 登录页 02. 管理员端-课程管理 03. 管理员端-学生管理 04. 管理员端-教师管理…

史上最全接单平台集锦,程序员不容错过!

非典型程序员不是每天都累成狗&#xff0c;天天”996"甚至”007“。可能&#xff0c;面临着上班摸鱼没事干&#xff0c;下班躺尸打游戏的无聊境况。那么&#xff0c;如果你也是这样的程序员&#xff0c;有没有什么安排可以打发时间&#xff1f; 闲着还不如挣钱~心情好的时…

【QML】qml+gstreamer显示的同时录像,避免卡顿

1. 问题 使用QML的CameravideoRecorder(Camera)VideoOutput实现显示加录像功能。在Ubuntu上运行正常&#xff0c;视频流畅。但是在开发板上&#xff08;RK3568&#xff09;上出现明显卡顿&#xff0c;无法正常录像。 2. 解决方案 将摄像头数据通过gstreamer共享内存到某个位…

cddd 安装指南(pip install cddd)

pip install cddd 这个命令可能会报错&#xff0c;因为要求是TensorFlow1.10.0 TensorFlow1.10.0对应的Python版本是3.6&#xff0c;所以如果你的Python版本是3.6以上是不行的.....

OpenCV实现手势音量控制

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 今天来学习一下如何使用OpenCV实现手势音量控制&#xff0c;欢迎大家一起前来探讨学习~ 一、需要的库及功能介绍 本次实验需要使用OpenCV和mediapipe库进行手势识别&#xff0c;并利用手势距离控制电脑音量。 导入库&am…

Python内置函数与标准库函数的详细解读

一、内置函数与标准库函数的区分 Python 解释器自带的函数叫做内置函数&#xff0c;这些函数可以直接使用&#xff0c;不需要导入某个模块。 Python 解释器也是一个程序&#xff0c;它给用户提供了一些常用功能&#xff0c;并给它们起了独一无二的名字&#xff0c;这些常用功能…

C++初阶 | [五] 内存管理

摘要&#xff1a;new and delete&#xff0c;定位new&#xff0c;&#xff08;C内存管理的方式&#xff09;&#xff0c;malloc/free和new/delete的区别&#xff0c;内存泄漏 关于内存&#xff1a; 栈又叫堆栈——非静态局部变量/函数参数/返回值等等&#xff0c;栈是向下增长…