[路飞]_数据结构-并查集

简介: 数据结构-并查集

网络异常,图片无法展示
|


B站地址


概念


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


应用场景


举两个栗子🌰:


  1. 朋友圈中,不一定其中的人都互相直接认识。
    如果小张的朋友是小王,小王的朋友是小李,则他们属于一个朋友圈。
    给定若干朋友关系,判断某两人是否在一个朋友圈。
  2. 如果小张有一个亲戚小王,小王有一个亲戚小李,则小张和小李是亲戚关系。
    给定若干亲戚关系,判断某两人是否是亲戚关系。


所以并查集主要应用于一些连通性问题,处理集合是否相交及查询元素所属集合的问题。


动画演示


网络异常,图片无法展示
|


主要操作


初始化


把每个点所在的集合初始化为其自身。


通常来说,这个步骤是在该数据结构初始化时执行一次,无论何种实现方式,时间复杂度均为 O(n)。


查找


查找元素所在的集合,即根节点。


合并


将两个元素所在的集合合并为一个集合。


代码实现


染色法


顾名思义,这种方式合并集合的时候,采用将一个集合的元素染成另外一个集合的颜色的方法,颜色相同的元素,即为同一个集合的元素。


代码如下:


class UnionSet {
  constructor(n){
    // 初始化节点数组
    this.colors = Array(n);
    // 将每个元素染色为其下标
    for(let i = 0;i<n;i++){
      this.colors[i] = i;
    }
  }
  // 获取当前元素色值
  find(x){
    return this.colors[x]
  }
  merge(a,b){
    // 获取元素 a 的颜色,b 的颜色
    const ca = this.colors[a],cb = this.colors[b];
    // 将颜色为和 b 相同的元素染为 a 的颜色
    for(let i =0;i<this.colors.length;i++){
      if(this.colors[i]===cb){
        this.colors[i] = ca
      }
    }
  }
}
复制代码


树型结构


染色法的 merge 操作时间复杂度是 O(n) 的,同行并查集使用树型结构来实现,这样我们只需要将一个元素所在的树挂载到另一个元素所在的树即可,合并操作时间复杂度是 O(1) 的。


虽然这种是方式的查找操作(时间复杂度 O(logn))相比染色法(时间复杂度 O(1)) 效率会低一些,但是综合下来还是会更高效一些,而且对于属性结构的并查集,我们还可以进行更多的优化。


代码如下:


class UnionSet {
  constructor(n){
    // 初始化节点集合数组
    this.list = Array(n);
    // 把每个元素的集合初始化为其自身
    for(let i = 0;i<n;i++){
      this.list[i] = i
    }
  }
  // 获取元素所在集合根节点
  find(x){
    // 如果当前元素为根节点,返回
    if(this.list[x]===x) return x;
    // 否则递归获取根节点并返回
    return this.find(this.list[x])
  }
  // 集合合并
  merge(a,b){
    // 获取元素所在集合根节点
    const rootA = this.find(a),rootB = this.find(b);
    // 如果两元素在同一个集合,取消合并
    if(rootA===rootB) return;
    // 将b所在集合合并到a所在集合
    this.list[rootB] = rootA;
  }
}
复制代码


优化树高


上面这一版代码我们只是很随意的把 b 所在集合合并到 a 所在集合,这种做法,在极端情况下,是可能把树连成一个链表的。为了防止这种情况发生,我们可以判断一下两棵树的节点数量更多,为了优化后续的查找效率,应该把节点更少的树挂载到节点更多的树下面。


代码如下:


class UnionSet {
  constructor(n){
    // 初始化节点数量数组
    this.size = Array(n).fill(1);
    // 初始化节点集合数组
    this.list = Array(n);
    // 把每个元素的集合初始化为其自身
    for(let i = 0;i<n;i++){
      this.list[i] = i
    }
  }
  // 获取元素所在集合根节点
  find(x){
    // 如果当前元素为根节点,返回
    if(this.list[x]===x) return x;
    // 否则递归获取根节点并返回
    return this.find(this.list[x])
  }
  // 集合合并
  merge(a,b){
    // 获取元素所在集合根节点
    const rootA = this.find(a),rootB = this.find(b);
    // 如果两元素在同一个集合,取消合并
    if(rootA===rootB) return;
    // 将节点更少的树挂载到节点更多的树下,并更新该树节点数量
    if(this.size[rootA]<this.size[rootB]){
      this.list[rootA] = rootB;
      this.size[rootB] += this.size[rootA]
    }else{
      this.list[rootB] = rootA;
      this.size[rootA] += this.size[rootB]
    }
  }
}
复制代码


路径压缩


经过上面的优化后,我们的并查集已经很高效了,但是对于 find 操作,还可以做进一步优化。


方法是在 find 操作中,获取元素所在集合根节点,然后将当前元素直接挂载到根节点上,这样下一次获取该元素根节点,就只需要进行两次查找,将 O(logn) 的时间复杂度优化到了 O(1)


class UnionSet {
  constructor(n){
    // 初始化节点数量数组
    this.size = Array(n).fill(1);
    // 初始化节点集合数组
    this.list = Array(n);
    // 把每个元素的集合初始化为其自身
    for(let i = 0;i<n;i++){
      this.list[i] = i
    }
  }
  // 获取元素所在集合根节点
  find(x){
    // 如果当前元素为根节点,返回
    if(this.list[x]===x) return x;
    // 否则递归获取根节点
    const root = this.find(this.list[x])
    // 将当前节点挂载为根节点子节点,实现路径压缩优化
    this.list[x] = root;
    // 返回根节点
    return root;
  }
  // 集合合并
  merge(a,b){
    // 获取元素所在集合根节点
    const rootA = this.find(a),rootB = this.find(b);
    // 如果两元素在同一个集合,取消合并
    if(rootA===rootB) return;
    // 将节点更少的树挂载到节点更多的树下,并更新该树节点数量
    if(this.size[rootA]<this.size[rootB]){
      this.list[rootA] = rootB;
      this.size[rootB] += this.size[rootA]
    }else{
      this.list[rootB] = rootA;
      this.size[rootA] += this.size[rootB]
    }
  }
}
复制代码


至此,我们就完成了并查集的概念、应用场景以及手写实现的全过程。


如有任何问题或建议,欢迎留言讨论!

相关文章
|
8月前
|
容器
数据结构:并查集
数据结构:并查集
78 0
数据结构:并查集
|
3月前
|
算法 开发者 计算机视觉
燃爆全场!Python并查集:数据结构界的网红,让你的代码炫酷无比!
在编程的世界里,总有一些数据结构以其独特的魅力和高效的性能脱颖而出,成为众多开发者追捧的“网红”。今天,我们要介绍的这位明星,就是Python中的并查集(Union-Find)——它不仅在解决特定问题上大放异彩,更以其优雅的设计和强大的功能,让你的代码炫酷无比,燃爆全场!
47 0
|
8月前
|
机器学习/深度学习 存储
数据结构(九)---并查集
数据结构(九)---并查集
117 5
|
4月前
|
Python
逆天改命!掌握Python并查集,数据结构难题从此不再是你的痛!
在编程旅程中,遇到棘手的数据结构难题是否让你苦恼?别担心,Python并查集(Union-Find)是你的得力助手。这是一种高效处理不相交集合合并及查询的数据结构,广泛应用于网络连通性、社交网络圈子划分等场景。通过维护每个集合的根节点,它实现了快速合并与查询。本文将介绍并查集的基本概念、应用场景以及如何在Python中轻松实现并查集,帮助你轻松应对各种数据结构挑战。
43 3
|
4月前
|
存储 C语言
数据结构基础详解(C语言): 树与二叉树的应用_哈夫曼树与哈夫曼曼编码_并查集_二叉排序树_平衡二叉树
本文详细介绍了树与二叉树的应用,涵盖哈夫曼树与哈夫曼编码、并查集以及二叉排序树等内容。首先讲解了哈夫曼树的构造方法及其在数据压缩中的应用;接着介绍了并查集的基本概念、存储结构及优化方法;随后探讨了二叉排序树的定义、查找、插入和删除操作;最后阐述了平衡二叉树的概念及其在保证树平衡状态下的插入和删除操作。通过本文,读者可以全面了解树与二叉树在实际问题中的应用技巧和优化策略。
|
4月前
|
Python
告别低效!Python并查集:数据结构界的超级英雄,拯救你的编程人生!
告别低效!Python并查集:数据结构界的超级英雄,拯救你的编程人生!
36 0
|
4月前
|
算法 开发者 计算机视觉
Python并查集:数据结构界的肌肉男,让你在编程路上无所畏惧!
在编程的浩瀚宇宙中,数据结构如同基石,构建了解决问题的坚实框架。而并查集(Union-Find),这位数据结构界的“肌肉男”,以其独特的魅力和强大的功能,让无数开发者在面对复杂关系处理时,都能感受到前所未有的从容与自信。今天,就让我们一同揭开并查集的神秘面纱,看看它是如何成为你编程路上的得力助手的。
39 0
|
4月前
|
算法 程序员 计算机视觉
Python并查集:数据结构界的肌肉男,让你在编程路上无所畏惧!
并查集,一种处理不相交集合合并与查询的数据结构,被誉为编程的“肌肉男”。它提供Find(找根节点)和Union(合并集合)操作,常用于好友关系判断、图像处理、集合合并等。Python实现中,路径压缩和按秩合并优化效率。并查集的高效性能使其成为解决问题的强大工具,助力程序员应对复杂挑战。
40 0
|
6月前
|
算法 程序员 图形学
脑洞大开!Python并查集:用最简单的方式,解决最复杂的数据结构问题!
【7月更文挑战第17天】并查集,数据结构明星,处理不相交集合合并与查询。Python实现核心操作:查找与合并。路径压缩优化查找,按秩合并保持平衡。实战应用如图连通性判断,算法竞赛利器。掌握并查集,解锁复杂问题简单解法,照亮编程之旅!
66 10
|
6月前
|
Python
告别低效!Python并查集:数据结构界的超级英雄,拯救你的编程人生!
【7月更文挑战第18天】并查集,数据结构超级英雄,用于不相交集合的合并与查询。Python实现包括初始化、查找根节点和合并操作。应用广泛,如社交网络分析、图论问题、集合划分等。示例代码展示了解决岛屿数量问题,统计连通的“1”单元格数。掌握并查集,提升编程效率,解决复杂问题。
60 6