热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【经典数据结构】并查集

等价关系与等价类若对于每一对元素(a,b),a,b∈S,aRb或者为true或者为false,则称在集合S上定义关系R。如果aRb为true,那么我们说a与b有关系。等价关系(

等价关系与等价类

  若对于每一对元素(a,b),a,b∈S,a R b或者为true或者为false,则称在集合S上定义关系R。如果a R b为true,那么我们说a与b有关系。

  等价关系(equivalence relation)是满足下列三个性质的关系R:

  (1) 自反性:对于所有a∈S,a R a

  (2) 对称性:若a R b当且仅当b R a

  (3) 传递性:若a R b且b R c 则a R c

  关系“≤”不是等价关系。虽然它是自反的(即a≤a)、可传递的(即由a≤b和b≤c得出a≤c),但它不是对称的,因为a≤b不能得出b≤a。

  如果两个城市位于同一个国家,那么定义它们是由关系的。容易验证这是一个等价关系。如果能够通过公路从a旅行到b,则设a与b有关系。如果所有的道路都是双向行驶的,那么这种关系也是一个等价关系。

并查集: 

  在计算机科学中,并查集是一种树形的数据结构,其保持着用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示,进行快速规整。

  并查集保持一组不相交的动态集合S={S1,S2,...,Sk}。每个集合通过一个代表来识别,代表即集合中的某个成员。在某些应用中,哪一个成员被选作代表无所谓。在一些应用中,如何选择代表可能存在预先说明的规则,例如选择集合中的最小元素。

  并查集提供以下操作:

  1. Find:确定元素属于哪一个集合。不要求find操作返回任何特定的名字,而只要求当且仅当两个元素属于相同的集合时,作用在这两个元素上的find返回相同的名字。

  2. Union:将两个子集合并成同一个集合

  3. MakeSet: 用于建立单元素集合。

 

并查集的表示方法:

  1. 单链表表示

    要实现并查集数据结构,一种简单的方法是每一个集合都用一个链表来表示。每个链表的第一个对象作为它所在集合的代表。链表中的每一个对象都包含一个集合成员、一个指向包含下一个集合成员的对象的指针,以及指向代表的指针。每个链表都含head指针和tail指针,head指向链表的代表,tail指向链表中最后的对象。

  2. 并查集森林

    并查集的另一种更快的实现是用有根树表示集合树中每个节点包含一个成员,每棵树代表一个集合。在一个不相交森林中,每个成员仅指向指向它的父节点。每棵树的根包含集合的代表,并且是其自己的父节点。正如我们将要看到的那样,虽然使用这种表示的直接算法并不比使用链表表示的算法快,但通过引入两种启发式策略(“按秩合并”和“路径压缩"),我们能得到一个渐进最优的不相交集合数据结构。

    在这种表示方法中,MAKE-SET操作简单地创建一棵只有一个节点的树,Find操作通过沿着指向父节点的指针找到树的根。这一通过根节点的简单路径上所访问过的节点构成了查找路径。union操作使得一棵树指向另一棵树的根。下图所示为union操作:

    开始时每个集合含有一个元素。这些树不一定必须是二叉树,但是用二叉树表示起来要容易,因为我们需要的唯一信息就是父链(parent link)。集合的名字由根处的节点给出。由于只需要父节点的名字,因此可以假设这棵树是被非显式的存储在数组中(与二叉堆类似):数组的每个成员s[i]表示元素i的父亲。如果i是根,那么s[i]=-1.、

  

并查集森林的两种改进策略:

  1. 按秩合并(union by rank)。改进union操作的方法。这种方法的思想是在union操作中使较少节点的树的根指向具有较多节点的树的根,使得总是较小的树成为较大的树的子树。注意,这里并不显式地记录每个节点为根的子树的大小,而是采用一种易于分析的方法。对于每个节点,维护一个秩,它表示该节点高度的一个上界。在使用按秩合并策略的UNION操作中,我们可以让具有较小秩(高度)的根指向具有较大秩的根。我们初始化数组的所有项为-1。下图显示了一棵树及其对于按秩求并的实例。

  

  

 

  2. 路径压缩(path compression)。改进find操作的方法。它在查找过程中将查到节点到根的每个节点的父节点直接指向根。如下所示  

 

实现代码:

class DisjSets {
 public:
    explicit DisjSets(int numElements);

    int Find(int x);
    
    void Union(int element1, int element2); // union is key word in C++, use Union

 private:
     vector<int> s;
};

DisjSets::DisjSets(int numElements): s(numElements) {
    for (vector<int>::iterator iter = s.begin();
         iter != s.end(); ++iter) {
             *iter = -1;
    }
}

//路径压缩
int DisjSets::Find(int x)  { 
    if (s[x] <0)
        return x;
    else
        return s[x] = Find(s[x]);
}

void DisjSets::Union(int element1, int element2) {
    int root1 = Find(element1); //find root of element1
    int root2 = Find(element2); //find root of element2

    if (s[root2] < s[root1]) {
        s[root1] = root2;
    } else {
        if (s[root1] == s[root2])
            s[root1]--;
        s[root2] = root1;
    }
}

 

 

 

参考资料:

  1. 《算法导论》(第三版)

  2. 《数据结构与算法分析C++描述》(第三版)


推荐阅读
  • 本文深入解析了计算力扣平台上汉明距离问题的官方解法,并通过优化算法提高了计算效率。具体而言,我们详细探讨了如何利用位运算技巧来高效计算数组中所有数对之间的汉明距离,从而在时间和空间复杂度上实现了显著改进。通过实例代码演示,使读者能够更直观地理解这一优化方法。 ... [详细]
  • TypeScript 实战分享:Google 工程师深度解析 TypeScript 开发经验与心得
    TypeScript 实战分享:Google 工程师深度解析 TypeScript 开发经验与心得 ... [详细]
  • 在 Angular Google Maps 中实现图片嵌入信息窗口的功能,可以通过使用 `@agm/core` 库来实现。该库提供了丰富的 API 和组件,使得开发者可以轻松地在地图上的信息窗口中嵌入图片。本文将详细介绍如何配置和使用这些组件,以实现动态加载和显示图片的功能。此外,还将探讨一些常见的问题和解决方案,帮助开发者更好地集成这一功能。 ... [详细]
  • 本文介绍了如何利用Apache POI库高效读取Excel文件中的数据。通过实际测试,除了分数被转换为小数存储外,其他数据均能正确读取。若在使用过程中发现任何问题,请及时留言反馈,以便我们进行更新和改进。 ... [详细]
  • HDU1176:免费馅饼问题的动态规划解法分析
    题目“免费馅饼”通过动态规划方法进行了解析。该问题的时间限制为 Java 2000ms 和其他语言 1000ms,内存限制为 Java 65536K 和其他语言 32768K。本文详细探讨了如何利用动态规划算法高效求解此问题,并对算法的时间复杂度和空间复杂度进行了深入分析。此外,还提供了具体的实现步骤和代码示例,帮助读者更好地理解和应用这一方法。 ... [详细]
  • 如何高效启动大数据应用之旅?
    在前一篇文章中,我探讨了大数据的定义及其与数据挖掘的区别。本文将重点介绍如何高效启动大数据应用项目,涵盖关键步骤和最佳实践,帮助读者快速踏上大数据之旅。 ... [详细]
  • 技术分享:深入解析GestureDetector手势识别机制
    技术分享:深入解析GestureDetector手势识别机制 ... [详细]
  • 探索聚类分析中的K-Means与DBSCAN算法及其应用
    聚类分析是一种用于解决样本或特征分类问题的统计分析方法,也是数据挖掘领域的重要算法之一。本文主要探讨了K-Means和DBSCAN两种聚类算法的原理及其应用场景。K-Means算法通过迭代优化簇中心来实现数据点的划分,适用于球形分布的数据集;而DBSCAN算法则基于密度进行聚类,能够有效识别任意形状的簇,并且对噪声数据具有较好的鲁棒性。通过对这两种算法的对比分析,本文旨在为实际应用中选择合适的聚类方法提供参考。 ... [详细]
  • 在本文中,我们探讨了如何使用 NSArrays 来实现集合的交集与并集操作。通过两个示例数组 A 和 B,其中包含一些共同元素(例如 A: 1, 2, 3 和 B: 2, 3, 4),我们将详细介绍如何高效地进行这些集合操作。此外,我们还将讨论这些方法在实际应用中的性能优势和适用场景。 ... [详细]
  • 在使用 `useSelector` 选择器时,发现分派操作后状态未能实时更新。这可能是由于 React 组件的渲染机制或 Redux 的状态管理问题导致的。建议检查 `useSelector` 的依赖项和 `dispatch` 的调用时机,确保状态变化能够正确触发组件重新渲染。此外,可以考虑使用 `useEffect` 钩子来监听状态变化,以确保及时更新。 ... [详细]
  • 在处理大图片时,PHP 常常会遇到内存溢出的问题。为了避免这种情况,建议避免使用 `setImageBitmap`、`setImageResource` 或 `BitmapFactory.decodeResource` 等方法直接加载大图。这些函数在处理大图片时会消耗大量内存,导致应用崩溃。推荐采用分块处理、图像压缩和缓存机制等策略,以优化内存使用并提高处理效率。此外,可以考虑使用第三方库如 ImageMagick 或 GD 库来处理大图片,这些库提供了更高效的内存管理和图像处理功能。 ... [详细]
  • 揭秘腾讯云CynosDB计算层设计优化背后的不为人知的故事与技术细节
    揭秘腾讯云CynosDB计算层设计优化背后的不为人知的故事与技术细节 ... [详细]
  • 在HDU 1166敌军布阵问题中,通过运用线段树数据结构,可以高效地计算指定区间的敌军数量。该算法不仅能够在限定的时间和内存条件下快速求解,还能够灵活应对动态变化的战场局势,为实时决策提供支持。 ... [详细]
  • 深入浅出解读奇异值分解,助你轻松掌握核心概念 ... [详细]
  • 如果程序使用Go语言编写并涉及单向或双向TLS认证,可能会遭受CPU拒绝服务攻击(DoS)。本文深入分析了CVE-2018-16875漏洞,探讨其成因、影响及防范措施,为开发者提供全面的安全指导。 ... [详细]
author-avatar
lifetime8_797
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有