当前位置: 首页 > article >正文

“树”据结构:并查集从入门到AC

“树”据结构:并查集

    • 前言
    • 算法设计
    • 代码示例
    • 优化
    • 相关文章

前言

在一组数据中,数据被分为了不同的集合,那么其中的集合往往可以用树形来表示。而区分集合,与查找集合的元素,就会成为核心的问题。并查集主要就是解决这类问题,因此并查集算法的核心也就是查找与区分。
并查集通过一个一维数组来实现,因此,给我们提供了更大的时间和空间上的便利。通过一维数组来维护一个森林,也就是维护由不同树为子集构成的集合。
问题示例:
有10个学生,1号与2号同班,3号和5号同班,4号和6号同班,7号和3号同班,8号和10号同班,9号和2号同班,8号和4号同班。
然后一般是要求解不同的班级数or输入序号查询同班同学
这类问题都是经典的并查集模板。

算法设计

先动动笔算下我们需要的结果:
1,2,9一个班级
3,5,7一个班级
4,6,8,10一个班级
首先先将一个一维数组初始化,设数组的值为其班级序号,假设每个学生都来自不同的班级,即f[i]=i。
由此f[1]=1,f[2]=2,f[3]=3,…f[10]=10。
然后我们再把结点合并成树,树内部再做处理。
由结点合并为树:以靠左优先进行同班合并,由于输入的条目是1 2,所以2号的2班消失合并到1班
在这里插入图片描述
接下来我们要准备的函数是,搜索同班同学的归属,我们先写一个深度搜索:

int dfs(int v)
{
    if(a[v]==v)
        return v;
    else
    {
        dfs(a[v]);
    }
}

于是我们可以搜索到同班同学的最终归属。
而当我们读到输入条目:9号与2号同班的时候,由于2号班级已经消失成为了1号班级(形成了树,而不是单一结点),这时候我们将整个结点归到9号之下:
在这里插入图片描述
但这时候我们的算法没有完成,因为在最后的数组下标里,1、2、9明明同属于9班2号学生却属于1班,2向1的指针就多余了,那么我们需要略微处理下搜索算法来处优化冗余数据,也称路径压缩:
在这里插入图片描述
如此处理,当最终搜索完成的时候,只要每次存在f[i]=i,就代表了有一个独立的班级,这棵树的最终父节点为i。

代码示例

初始化数据

scanf("%d %d",&n,&m);//输入学生数与数据条目数
    for(i=0;i<=n;i++)
    {
        f[i]=i;//初始化
    }
    for(i=1;i<=m;i++)
    {
        scanf("%d %d",&x,&y);//输入每组同班条目
        Merge_(x,y);//合并函数
    }

合并函数:
进行同班合并
t1与t2代表了v和u的祖先结点,如果t1与t2不相等,t2从下至上一整支需要归并到t1下

void Merge_(int v,int u)
{
    int t1=0,t2=0;
    t1=getf(v);//查找根部归属
    t2=getf(u);
    if(t1!=t2)
    {
        f[t2]=t1;
    }

}

搜索函数只需要在上文的基础上稍稍加强一下

int getf(int v)
{
    if(f[v]==v)
        return v;
    else
    {
        f[v]=getf(f[v]);//路径压缩
        return f[v];
    }
}

到这里,我们拿上文的题目来做输入输出示例:
有10个学生,1号与2号同班,3号和5号同班,4号和6号同班,7号和3号同班,8号和10号同班,9号和2号同班,8号和4号同班。
求学生来自多少个不同班级
那么用变量 j 来扫描搜索结果:

for(i=1;i<=n;i++)
    {

        if(f[i]==i)
        {
        j++;
        }
    }

在这里插入图片描述
得解分为3个班级。

优化

并查集的优化思路不少,但核心都在于,如何高效的来合并树,也就是谁向谁合并。
通俗的说,既然合并的过程是修改被合并的树的祖先认知,由于修改的过程是把树回溯,那么显然,被合并的树越小,速度就会越快,反之越慢。
前文中我们使用的是向左边的树合并,那么现在我们可以始终保持小树向大树合并:
先去声明一个size数组,来表示树的结点数量,并全部初始化为1

void Merge_(int v,int u)
{
    int t1=0,t2=0;
    t1=getf(v);
    t2=getf(u);
    if(t1!=t2)
    {
        if (size[t1] > size[t2]) {//比较大小,然后由小并大
		f[t2] = t1;
		size[t1] += size[t2];
	} else {
		f[t1] = t2;
		size[t2] += size[t1];
	}
    }

}

相关文章

二叉树从入门到AC(1)构建和前中后序遍历
二叉树从入门到AC(2)深度与层次遍历
二叉树从入门到AC(3)完全二叉树与堆


http://www.kler.cn/a/306139.html

相关文章:

  • oracle: create new database
  • JAVA:组合模式(Composite Pattern)的技术指南
  • 【从零开始入门unity游戏开发之——C#篇21】C#面向对象的封装——`this`扩展方法、运算符重载、内部类、`partial` 定义分部类
  • 服务器数据恢复—V7000存储中多块磁盘出现故障导致业务中断的数据恢复案例
  • 秒优科技-供应链管理系统 login/doAction SQL注入漏洞复现
  • 【定理证明工具调研】Coq, Isabelle and Lean.
  • MATLAB基础语法知识
  • MySQL指令
  • linux 操作系统下的cut命令介绍和使用案例
  • JavaScript控制语句和函数的使用
  • Python Numpy布尔数组在数据分析中的应用
  • 思维商业篇(3)—三大竞争战略
  • 【安全系列--处理挖矿】
  • Centos 执行yum安装 出现Failed connect to mirrors.163.com:80; 拒绝连接
  • Golang | Leetcode Golang题解之第409题最长回文串
  • Java中的服务端点响应缓存:Spring Cache抽象
  • ★ C++基础篇 ★ string类的实现
  • Python实现pdf转图片、转文字、去水印
  • 房产销售系统开发:SpringBoot技术要点
  • 避免 PyCharm 将该 Python 脚本作为测试运行
  • 串口数据波形显示工具对比
  • k8s service如何实现流量转发
  • Python 课程10-单元测试
  • 基于 TDMQ for Apache Pulsar 的跨地域复制实践
  • 2024.9.14 Python与图像处理新国大EE5731课程大作业,马尔可夫随机场和二值图割,校正立体图像的深度
  • 攻击者如何在日常网络资源中隐藏恶意软件