数据结构学习记录——集合及运算(集合的表示、并查集、树结构表示集合、集合运算、查找函数、并运算)

简介: 数据结构学习记录——集合及运算(集合的表示、并查集、树结构表示集合、集合运算、查找函数、并运算)

集合的表示

集合运算概述

交、并、补、差,判定一个元素是否属于某一个集合

并查集

集合并、查某元素属于什么集合

我们最主要关心的就是集合的两个运算,一个是把两个集合并起来;另一个是查找某一个元素是属于哪个集合的。


【例】有10台电脑{1,2,3,...,9,10},已知下列电脑之间已经实现了连接:


1和2、2和4、3和5、4和7、5和8、6和9、6和10


问:2和7之间,5和9之间是否是连通的?

解决思路

(1)将10台电脑看成10个集合{1},{2},{3},...,{9},{10};

(2)已知一种连接“x和y”,就将x和y对应的集合合并;

(3)查询“x和y是否是连通的”就是判别x和y是否属于同一集合。

已经清楚思路了,那么在这样的并查集问题中集合存储要如何实现呢?

  • 可以用树结构表示集合,树的每个结点代表一个集合元素

树结构表示集合

例如,有三个整数集合

S1 = {1,24,7}

S2 = {3,5,8}

S3 = {6,9,10}

存储形式采用数组

数组中每个元素的类型描述为:

typedef int ElementType;  
typedef struct
{
    ElementType Data;
    int Parent;
}SetType;

采用数组存储结点,可以通过下标访问结点,从而快速访问结点和结点信息。

双亲表示法只需要存储每个结点的父结点下标,相比于其他树形结构存储方式,双亲表示法占用的空间更小。

采用数组形式的树形结构双亲表示法存储集合结构的实现相对来说比较简单,易于理解和实现。

但是,

在使用时需要注意数组大小的限制和节点添加、删除的效率问题。此外,双亲表示法对于树形结构的高度有一定的限制,如果树的高度过高,会导致数组过大,空间利用率下降。

 

集合运算

查找函数

查找某个元素所在的集合(用根结点表示

#define MAXN 1000                  /* 集合最大元素个数 */
typedef int ElementType;           /* 默认元素可以用非负整数表示 */
typedef int SetName;               /* 默认用根结点的下标作为集合名称 */
typedef ElementType SetType[MAXN]; /* 假设集合元素下标从0开始 */
 
SetName Find( SetType S, ElementType X )
{    /* 默认集合元素全部初始化为-1 */
    if ( S[X] < 0 ) /* 找到集合的根 */
        return X;
    else
        return S[X] = Find( S, S[X] );  /* 路径压缩 */
}

S 数组用于存储每个元素的父结点,如果一个元素的父结点为负数,则说明该元素为集合的根。


在查找元素 X 所在的集合时,首先判断该元素是否为集合的根,如果是,则直接返回该元素的下标。


如果不是,则通过递归查找该元素的父结点,直到找到集合的根,并将路径上所有结点的父结点都设置为根结点。

在递归返回时,算法还进行了路径压缩的优化。也就是将路径上所有结点的父结点都设置为根结点,避免了后续查找时的重复递归。

路径压缩

路径压缩是一种优化技术,用于加速查找并查集中元素所在集合的过程。


在不进行路径压缩的情况下,每次查找一个元素的根结点时,需要从该元素一直向上遍历到根结点,并将路径上所有结点的父结点都设置为根结点,这样才能保证以后查找该元素所在集合时的效率。


而路径压缩技术的思想是:在递归返回的过程中,将路径上所有结点的父点都设置为根结点。假设要查找元素 X 所在的集合,从 X 开始向上查找其父结点,直到找到集合的根结点,然后将路径上所有节点的父结点都设置为根结点。这样就可以将路径上的所有结点都直接连接到根结点上,从而压缩树的高度,提高查找效率。

并运算

在进行并运算时,要注意

元素所在集合的根结点的 S 值为负数不仅仅单纯为-1,而是用负数大小来表示集合的大小。(例如-7则表示集合中有7个元素)。

void Union( SetType S, SetName Root1, SetName Root2 )
{     /* 这里默认Root1和Root2是不同集合的根结点 */
    /* 保证小集合并入大集合 */
    if ( S[Root2] < S[Root1] )  /* 如果集合2比较大 */
    { 
        S[Root2] += S[Root1];     /* 集合1并入集合2  */
        S[Root1] = Root2;
    }
    else                        /* 如果集合1比较大 */
    {                         
        S[Root1] += S[Root2];     /* 集合2并入集合1  */
        S[Root2] = Root1;
    }

在合并两个集合时,根据两个集合的大小来判断将哪个集合并入另一个集合。这样能使得并起来之后的树的高度尽可能小一点。


如果集合 1 的大小(S[Root1])比集合 2 的大小(S[Root2])小,就将集合 1 并入集合 2 中。


此时,需要将集合 1 的根结点的父结点设置为集合 2 的根结点,并将集合 2 的大小增加集合 1 的大小。



end



目录
相关文章
|
10月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
481 3
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
900 77
|
算法 数据可视化 开发者
为什么要学习数据结构与算法
今天,我向大家介绍一门非常重要的课程——《数据结构与算法》。这门课不仅是计算机学科的核心,更是每一位开发者从“小白”迈向“高手”的必经之路。
为什么要学习数据结构与算法
|
存储 人工智能 索引
Python数据结构:列表、元组、字典、集合
Python 中的列表、元组、字典和集合是常用数据结构。列表(List)是有序可变集合,支持增删改查操作;元组(Tuple)与列表类似但不可变,适合存储固定数据;字典(Dictionary)以键值对形式存储,无序可变,便于快速查找和修改;集合(Set)为无序不重复集合,支持高效集合运算如并集、交集等。根据需求选择合适的数据结构,可提升代码效率与可读性。
1025 1
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
532 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
485 12
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
325 9
|
存储 算法 测试技术
【C++数据结构——线性表】求集合的并、交和差运算(头歌实践教学平台习题)【合集】
本任务要求编写程序求两个集合的并集、交集和差集。主要内容包括: 1. **单链表表示集合**:使用单链表存储集合元素,确保元素唯一且无序。 2. **求并集**:遍历两个集合,将所有不同元素加入新链表。 3. **求交集**:遍历集合A,检查元素是否在集合B中存在,若存在则加入结果链表。 4. **求差集**:遍历集合A,检查元素是否不在集合B中,若满足条件则加入结果链表。 通过C++代码实现上述操作,并提供测试用例验证结果。测试输入为两个集合的元素,输出为有序集合A、B,以及它们的并集、交集和差集。 示例测试输入: ``` a c e f a b d e h i ``` 预期输出:
455 7
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
633 5