高阶数据结构之-并查集

简介: 高阶数据结构之-并查集

并查集原理

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题,常常在使用中以森林来表示

适合于描述这类问题的抽象数据类型称为并查集(union-findset)


小例子-编号和人

如果我们想让编号和人构成关系, 即可以通过编号找到对应的人,也可以通过人找到对应的编号怎么做?

template<classT>  

classUnionFindSet

{

public:

   UnionFindSet(constT*a, size_tn)  

   {

       for (inti=0; i<n; i++)

       {

           _a.push_back(a[i]);

           _indexMap[a[i]] =i;

       }

   }

private:

   vector<T>_a;  //编号找人  下标对应的就是人名

   map<T, int>_indexMap;//人找编号  key:人名   value:编号

};

#include"UnionFind.h"

intmain()

{

   stringa[] = { "张三","李四","王五" };

   UnionFindSet<string>ufs(a, 3);

   return0;

}


并查集的实现

#include<iostream>

#include<vector>

classUnionFindSet

{

public:

   UnionFindSet(size_tn);

   size_tFindRoot(intx);

   voidUnion(intx1, intx2);

   boolInset(intx1,intx2);

   size_tSetCount();

private:

   std::vector<int>_set;

};


首先我们要知道如下规则:

  1. 数组的下标对应集合中元素的编号
  2. 数组中的值如果为负数,负号代表根,数字代表该集合中元素个数
  3. 数组中的值如果为非负数,代表该元素双亲在数组中的下标

例如:


基本结构

classUnionFindSet

{

private:

   std::vector<int>_set;

};

构造函数

最初每个元素各自构成一个集合, 初始状态就是-1,表示size个集合

UnionFindSet(intsize)

   :_set(size, -1)//size个元素都最初初始化为-1

   {}


查找根节点

返回根节点所在下标

//如果_set[x]是负数,那么x就是某个集合的根

//如果_set[x]不是负数,那么x的父亲节点就是_set[x]值对应的位置

size_tFindRoot(intx)

{

   while (_set[x] >=0)

   {

       x=_set[x];

   }

   //来到这,_set[x] <0, 此时x就是根节点

   returnx;

}


路径压缩技巧

路径压缩实际上是在数据量太大的时候,访问一些数据可能在位于叶子位置,导致访问的效率不高

本质上我们查找的代表节点的时候,可以对该路径上的节点进行路径压缩

注意:最好不要用递归的时候进行路径压缩,因为路径深可能导致栈溢出


做法:

1)先找到当前路径节点的代表节点

2)然后从当前位置_set[x]开始的节点,沿途的节点的父亲位置都改为代表节点的位置

  只需要根据下标关系往上迭代即可x节点的父亲位置就是_set[x]位置

注意:要提前保存x节点的父亲节点位置,因为更改当前位置的父亲节点, 会丢失以前的父亲节点

size_tFindRoot(intx)

{

   introot=x;

   while (_set[root] >=0)

   {

       root=_set[root];

   }

   //此时root就是x这条路径的头节点(代表节点)

   //把沿途的节点的父亲都改为root

   while (_set[x] >=0)

   {

       intparent=_set[x];//先保存x节点的父节点位置

       _set[x] =root;//将x节点的父亲位置改为root位置

       x=parent;//往上迭代

   }

   returnroot;

}


合并集合


这里选择合并到x2所在的集合合并到x1所在的集合

voidUnion(intx1, intx2)

{

   //找到x1和x2的代表节点(根节点)

   size_troot1=FindRoot(x1);

   size_troot2=FindRoot(x2);

   //两个节点不在一个集合才合并

   //把root2合并到root1所在集合

   if (root1!=root2)

   {

       _set[root1] +=_set[root2];//root2所在集合的元素累加到root1所在集合

       _set[root2] =root1; //root2的父亲变为root1

   }

}

如果我们希望是小集合合并到大集合呢?  即元素少的集合合并到元素多的集合

路径压缩技巧

两颗树合并的时候,节点少的数往节点多的树合并

目的:为了使节点层数增多的节点相对减少


做法:

1)判断哪颗树的节点更多, 让root1变为是较大的集合, root2往root1合并

如何判断呢? 因为root1和root2是根,_set[root]的值是负数,代表该集合的元素个数, _set[root]值越大,集合元素个数越少

//x1和x2合并 ->本质是代表节点(根节点)合并

voidUnion(intx1, intx2)

{

   //找到x1和x2的代表节点(根节点)

   size_troot1=FindRoot(x1);

   size_troot2=FindRoot(x2);

   //我们让root1的集合是大集合,小集合合并到大集合中

   //因为root1和root2是根,所以_set[root]的值为负数,代表集合的元素个数,值越小,元素越多

   if (_set[root1] >_set[root2]) //说明root2集合元素多

   {

       ::swap(root1, root2);

   }

   //两个节点不在一个集合才合并

   if (root1!=root2)

   {

       _set[root1] +=_set[root2];//root2所在集合的元素累加到root1所在集合

       _set[root2] =root1; //root2的父亲变为root1

   }

}


求集合个数

遍历_set,查看有多少元素是负数,就代表有多少个根节点, 即有多少个集合

size_tSetCount()

{

   size_tcount=0;

   for (autoe : _set)

   {

       if (e<0) count++;

   }

   returncount;

}


判断两个元素是否在同一个集合

只需要判断两个元素的代表节点的位置是否相同即可

boolInset(intx1,intx2)

{

   returnFindRoot(x1) ==FindRoot(x2);

}



相关文章
|
人工智能 自然语言处理 算法
谷歌推出”自我发现“框架,极大增强GPT-4等大模型推理能力
【4月更文挑战第20天】谷歌DeepMind团队推出了SELF-DISCOVER框架,让大型语言模型能自我发现并构建推理结构,提升在复杂任务中的性能。该框架模仿人类解决问题方式,分两阶段选择和适应原子推理模块,以解决挑战。在多任务测试中,SELF-DISCOVER相比传统方法表现出色,性能提升42%,计算量减少10至40倍。它具有跨模型应用的普适性,并与人类思维方式相通。然而,它在某些任务类型上仍有优化空间,且需解决计算成本问题。论文链接:https://arxiv.org/abs/2402.03620
264 1
|
算法 计算机视觉 Python
OpenCV中Canny边缘检测和霍夫变换的讲解与实战应用(附Python源码)
OpenCV中Canny边缘检测和霍夫变换的讲解与实战应用(附Python源码)
1059 0
|
11月前
|
人工智能 Python
【02】做一个精美的打飞机小游戏,python开发小游戏-鹰击长空—优雅草央千澈-持续更新-分享源代码和游戏包供游玩-记录完整开发过程-用做好的素材来完善鹰击长空1.0.1版本
【02】做一个精美的打飞机小游戏,python开发小游戏-鹰击长空—优雅草央千澈-持续更新-分享源代码和游戏包供游玩-记录完整开发过程-用做好的素材来完善鹰击长空1.0.1版本
472 7
|
开发者
深入了解HTTP状态码
深入了解HTTP状态码
508 64
|
关系型数据库 MySQL 数据库
Python处理数据库:MySQL与SQLite详解 | python小知识
本文详细介绍了如何使用Python操作MySQL和SQLite数据库,包括安装必要的库、连接数据库、执行增删改查等基本操作,适合初学者快速上手。
1145 15
|
运维 监控 Devops
DevOps实践:持续集成与部署的自动化之旅
【10月更文挑战第7天】在软件开发领域,DevOps已成为提升效率、加速交付和确保质量的关键策略。本文将深入探讨如何通过实施持续集成(CI)和持续部署(CD)来自动化开发流程,从而优化运维工作。我们将从基础概念入手,逐步过渡到实际操作,包括工具选择、流程设计以及监控和反馈机制的建立。最终,我们不仅会展示如何实现这一自动化流程,还会讨论如何克服常见的挑战,以确保成功实施。
223 9
|
消息中间件 网络协议 NoSQL
1000W长连接,如何建立和维护?千万用户IM 架构设计
最近有小伙伴在面试 美团,又遇到了 IM 架构问题。小伙伴支支吾吾的说了几句,面试挂了。 所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,**让面试官爱到 “不能自已、口水直流”**,然后实现”offer直提”
|
Kubernetes Cloud Native 开发者
阿里云网络发布 alibaba-load-balancer-controller v1.2.0:开启云原生网关开源新篇章!敬请探索!
**阿里云发布开源版ALB控制器v1.2.0,对齐商业版ALB Ingress Controller v2.10.0。新版本增强了功能特性,提升了用户体验,并提供了最佳实践。功能更新包括自定义标签、QUIC协议支持、转发规则和安全策略等。此外,还引入了ReadinessGate实现滚动升级时的平滑上线和Prestop钩子确保平滑下线。用户可从GitHub获取开源代码,通过Docker Hub拉取镜像,开始使用alibaba-load-balancer-controller v1.2.0。**
753 3
阿里云网络发布 alibaba-load-balancer-controller v1.2.0:开启云原生网关开源新篇章!敬请探索!
|
设计模式 开发框架 前端开发
在Winform界面中使用DevExpress的TreeList实现节点过滤查询的两种方式
在Winform界面中使用DevExpress的TreeList实现节点过滤查询的两种方式
|
安全 网络协议 前端开发
Windows下nmap命令及Zenmap工具的使用方法
【7月更文挑战第28天】zenmap是一个开放源代码的网络探测和安全审核的工具,它是nmap安全扫描工具的图形界面前端,它可以支持跨平台。使用zenmap工具可以快速地扫描大型网络或单个主机的信息。如扫描主机提供了哪些服务,使用的操作系统等。
1806 8