蓝桥杯高频考点——并查集(心血之作)

news2025/3/26 7:39:22

并查集

  • TA Can Do What & why learning
    • what
    • why
  • 原理和结构
  • 路径压缩
  • 例题讲解
    • 题解
      • solution 1(50分)
      • solution 2(100分)
  • 按秩(树高)合并
  • 按大小合并

TA Can Do What & why learning

从字面意思上来理解就是,合并(merge)查询 (isCon?)我们需要的集合(set)的一种小算法,其实并没有多难 let’s go!!!

what

在这里插入图片描述

并查集主要是解决连通块的问题,例如对于上面的这个由5个村子若干条路构成的简易地图,如果问你1----->5是否是连通的,显然是的,那如果问你3——>5是否连通,显然是false,因为没有任何一条路能从3指向5,这不是很简单吗

why

那我们需要并查集干嘛?

好问题 如果这个图如果需要你自己构造而不是直接给你(动态构造集合关系),你很难或者说没法直接通过给的数据去画出每一个图的时候,并查集就能帮助我们迅速判断是否连通,而往往算法竞赛中的题目都是动态构造的(后面会附上例题),所以学习并查集是很有必要的,不然的话很大概率会超时
传统方式:
假设你有 100 万个村庄,每次新增一条路(动态添加边),如果每次都用 DFS/BFS 重新遍历整个图来判断两个村庄是否连通,时间复杂度会高达 O (N),效率极低。而并查集的find和union操作经过路径压缩和按秩合并优化后,时间复杂度几乎是O(1)

原理和结构

我们需要知道两个概念父节点和祖先,有一点像二叉树章节里的但又不太一样,尤其是对于祖先这个概念,
祖先代表的是:某一个节点不断去寻找他的父节点(递归),直到某一个节点的父节点是他本身(出口)

题外话:我在学习的时候感觉有点像 二叉树章节里面的 求最大深度问题,其实后来我想了一下是很正常的
毕竟树从某种意义上来说是特殊的图

言归正传:我们用一个pre数组来保存每个节点的父亲,我们的数组如下图所示,这里特意需要说的就是 1的父亲是他本身,所以1对应pre数组里头存储的父节点就是1

这句话怎么来理解呢:
其实可以把1看做是上面这个集合(1,2,4,5)的代表元素,也就是根节点,打个比方来说,这个1就是这个集合(家族)里面最年长的,就像家族的 “掌门人” 一样,没有比他更年长的父亲了,所以他的父亲就是他自己。
成员 1 作为这个家族的代表元素(根节点),就像是整个家族的源头,其他成员都是从他这里 “衍生” 出来的,就像一棵大树,成员 1 是树干最顶端的那个起始点,其他成员是从树干上长出来的树枝和树叶。

在这里插入图片描述

通过上面的例子大家应该会有一个比较基础的了解,但是在一开始每一个节点都是跟自己是一个连通关系(即指向自身)
在这里插入图片描述
就比如在添加1-2这条边的时候,我们就会让2的祖先指向1,在添加2-4这条边的时候,我们就需要注意,打个比方来说
由于2的“钱”已经交给1了,4想要找2要钱是找不到的,只能去找1了,体现在图中就是让4的祖先指向1,
总结来说就是:对于任何非根节点的相连都必须转换成它们各自的根节点相连

那现在我们已经可以用个构建的这个表来判断节点之间是否连通了,就比如1一直找,找到他的祖先是4,这个时间复杂度是O(N)的,但是这只是一次查询的情况如果有n次查询,那么时间复杂度就是O(n方),
那么有没有办法去优化它呢?
其实是有的——路径压缩(没学之前听着恐怖 其实是纸老虎)
在这里插入图片描述

路径压缩

我对于路径压缩的理解(可能不完全准确哈):
就是好比你是一个失忆的人,现在知道四个地方,A B C D,你通过不断探索知道了A是经过B C 能走到D的,也就是说你现在知道A到D是连通的,但是每过一段时间 如果一个人问你,你都需要重新走一遍,路径压缩好比就是,你用笔记本记下来了,A到D是通的并且D是终点,(这就引出了路径压缩的核心思想:通过直接记录最终结果(根节点),避免重复计算路径)
同理B,C到D也是通的,我们这里并不关心,A怎么到D,只关心从A能不能到D

压缩完成就是这个情况:
在这里插入图片描述

本质:
牺牲空间(存储父指针)换取时间(快速查询)
将树的高度 “压扁”,使得后续的find操作时间复杂度接近 O (1)

这里压扁的是啥?不就是我们寻找的路径path吗

例题讲解

在这里插入图片描述

题解

题目说的很直白就是让你用并查集的思路,其中有一个flag Z当它变化的时候,分别对应两种操作:1.合并(merge)2.判断(isCon)

solution 1(50分)

还有50%的测试点,数据非常大,即便关闭同步流,还是超时了吗,还是做不到吗,哈基霜你这家伙…
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 9;
int pre[maxn];//存储父节点

int root(int x)
{
    return pre[x] == x ? x : pre[x] = root(pre[x]);
}
//一切操作都在根上
void Merge(int x, int y)
{
    pre[root(x)] = root(y);
}

bool isCon(int x, int y)
{
    return root(x) == root(y);
}

void init(int N)
{
    for (int i = 1; i <= N; ++i) pre[i] = i;
}

signed main()
{
    ios::sync_with_stdio(0);
    int N, M;
    cin >> N >> M;
    init(N);
    while (M--)
    {
        int Z, X, Y;
        cin >> Z >> X >> Y;
        if (Z == 1)
            Merge(X, Y);
        else if (Z == 2)
            printf("%c\n", isCon(X, Y) ? 'Y' : 'N');
    }
    return 0;
}

solution 2(100分)

之前代码仅实现路径压缩,未结合按树高或者大小合并,在数据量大 时,树可能因合并顺序不当变得高度很高,导致find操作时间复杂度退化为接近O(n),最终超时。而当前代码通过两种优化,将单次操作的时间复杂度优化至几乎常数级

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 9;
int pre[maxn];// 存储父节点
int siz[maxn];// 存储集合大小(模拟秩)

// 初始化并查集
void init(int N) {
    for (int i = 1; i <= N; ++i) {
        pre[i] = i;
        siz[i] = 1; // 初始时每个集合大小为 1
    }
}

// 查找根节点并路径压缩
int root(int x) {
    return pre[x] == x ? x : pre[x] = root(pre[x]);
}

// 合并两个集合,按集合大小(秩)优化
void Merge(int x, int y) {
    int rx = root(x);
    int ry = root(y);
    if (rx == ry) return; // 已在同一集合,无需合并
    
    // 保证将较小集合合并到较大集合
    if (siz[rx] > siz[ry]) swap(rx, ry); 
    pre[rx] = ry;
    if (siz[rx] == siz[ry]) siz[ry]++; // 若大小相等,合并后新集合秩+1
}

// 判断两个元素是否连通
bool isCon(int x, int y) {
    return root(x) == root(y);
}

signed main() {
    ios::sync_with_stdio(0);
    int N, M;
    cin >> N >> M;
    init(N);
    while (M--) {
        int Z, X, Y;
        cin >> Z >> X >> Y;
        if (Z == 1) {
            Merge(X, Y);
        } else if (Z == 2) {
            printf("%c\n", isCon(X, Y) ? 'Y' : 'N');
        }
    }
    return 0;
}

这就引出了下面的优化方法按秩合并和启发式合并

按秩(树高)合并

在这里插入图片描述
在这之前啊,我们是不是通过解决 斜树查找退化成链表的问题 学习过平衡二叉树的概念,
这个其实跟这里的按秩合并非常像,解决的问题也很像

比如说上面这张图,如果新增的边是4->3,
那拿3这个节点举例,那他走到根只需要两步,那如果是3->4,那就需要走3步,根节点向右倾斜了

对于一棵树 我们更希望它变成 矮墩墩 而不是长竹竿,所以就需要我们添加边的时候就需要进行比较 矮的指向高的
在这里插入图片描述

按大小合并

跟上面跟类似用小的集合指向大的集合 也就是比较少的点会多走一步

void Merge(int x, int y) {
    int rx = root(x);
    int ry = root(y);
    if (rx == ry) return; // 已在同一集合,无需合并
    
    // 保证将较小集合合并到较大集合
    if (siz[rx] > siz[ry]) swap(rx, ry); 
    pre[rx] = ry;
    if (siz[rx] == siz[ry]) siz[ry]++; // 若大小相等,合并后新集合秩+1
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2320828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter 学习之旅 之 flutter 使用 connectivity_plus 进行网路状态监听(断网/网络恢复事件监听)

Flutter 学习之旅 之 flutter 使用 connectivity_plus 进行网路状态监听&#xff08;断网/网络恢复事件监听&#xff09; 目录 Flutter 学习之旅 之 flutter 使用 connectivity_plus 进行网路状态监听&#xff08;断网/网络恢复事件监听&#xff09; 一、简单介绍 二、conne…

Redisson 分布式锁原理

加锁原理 # 如果锁不存在 if (redis.call(exists, KEYS[1]) 0) then# hash结构,锁名称为key,线程唯一标识为itemKey&#xff0c;itemValue为一个计数器。支持相同客户端线程可重入,每次加锁计数器1.redis.call(hincrby, KEYS[1], ARGV[2], 1);# 设置过期时间redis.call(pexpi…

【STM32】SPI通信外设硬件SPI读写W25Q64

SPI通信协议和W25Q64存储器芯片解读笔记&#xff1a; 【STM32】SPI通信协议&W25Q64Flash存储器芯片&#xff08;学习笔记&#xff09;-CSDN博客 SPI通信外设 SPI外设简介 STM32内部集成了硬件SPI收发电路&#xff0c;可以由硬件自动执行时钟生成、数据收发等功能&…

Spring JDBC Template与事务管理:基于XML与注解的实战指南

摘要 本文深入解析Spring JDBC Template与事务管理的核心技术&#xff0c;结合XML配置与注解方式两种主流方案&#xff0c;通过转账案例完整演示数据库操作与事务管理的最佳实践。文章涵盖JDBC Template的核心用法、事务配置语法、常见问题及性能优化建议&#xff0c;帮助开发…

【Keil5-开发技巧】

Keil5-开发技巧 ■ Keil5利用AStyle插件格式化代码第一步:下载AStyle插件第二步:添加AStyle插件第三步:AStyle插件介绍■ 一键转UTF-8编码■ Keil5利用AStyle插件格式化代码 第一步:下载AStyle插件 AStyle下载 第二步:添加AStyle插件 解压后 astyle-3.6.7-x64 在重命…

电脑上不了网普通用户排除方法

1&#xff1a;首先通过电脑的运行/CMD/ipconfig /all 命令查看电脑的ip地址是否正常如图&#xff1a; 2&#xff1a;在命令行中运行&#xff1a;ping 127.0.0.1 如图则正常&#xff0c;否则要重新安装网卡驱动 程序。 3&#xff1a;用ping命令&#xff0c;ping一下同网段的电…

【C#】WinForm自定义控件及窗体

前言 WinForm&#xff08;Windows Forms&#xff09;是Microsoft.NET框架中的技术&#xff0c;用于开发Windows桌面应用程序。它提供了一套丰富的控件和组件。通过拖放控件、编写事件处理程序等方式快速构建用户界面。 通过属性窗口定制这些控件的外观和行为。 通过数据绑定&am…

基于虚拟知识图谱的语义化决策引擎

在数字化转型浪潮中&#xff0c;企业数据资产的价值释放面临两大挑战&#xff1a;海量异构数据的整合困局与业务-技术语义鸿沟。本文解析飞速创软灵燕智能体平台的创新解决方案——通过构建业务语义驱动的虚拟知识图谱系统&#xff0c;实现企业数据的智能认知与决策赋能。 一、…

HarmonyOS:@AnimatableExtend 装饰器自学指南

在最近的项目开发中&#xff0c;我遇到了需要实现复杂动画效果的需求。在探索解决方案的过程中&#xff0c;我发现了 AnimatableExtend 装饰器&#xff0c;它为实现动画效果提供了一种非常灵活且强大的方式。然而&#xff0c;在学习这个装饰器的过程中&#xff0c;我发现相关的…

kubernetes|云原生|kubeadm-1.25.7集群单master+外部etcd集群+kubeadm-init+cri-docker文件形式快速部署

一、 前言和写作原因 本文做一个kubernetes集群部署记录&#xff0c;实在是部署的东西太多了&#xff0c;害怕忘记&#xff0c;kubernetes集群的部署又细节比较多&#xff0c;因此&#xff0c;在这里做一个尽量详细的记录 三个VMware虚拟机&#xff0c;IP分别为192.168.123.…

Qt 导入TagLib库

文章目录 0. 前言和环境介绍1. 下载TagLib2. 下载zlib3. 修改.pro文件4. 测试代码 0. 前言和环境介绍 最近在使用Qt写一个播放器&#xff0c;需要解析mp3文件&#xff0c;于是研究了一下如何导入TagLib库 Qt构建套件:Desktop Qt6.8.2 MinGW64-bit Qt Creator安装目录: D:\bit…

新能源汽车充换站如何实现光储充一体化管理?

长三角某换电站光伏板晒到发烫&#xff0c;却因电网限电被迫切机&#xff1b;北京五环充电站每月多缴6万超容费&#xff1b;深圳物流车充电高峰排队3小时...当95%的充换站深陷“用不起绿电、扛不住扩容、算不清碳账”困局&#xff0c;安科瑞用一组真实数据撕开行业潜规则&#…

【数据分享】2000—2024年我国省市县三级逐年归一化植被指数(NDVI)数据(年平均值/Shp/Excel格式)

之前我们分享过2000-2024年我国逐年的归一化植被指数&#xff08;NDVI&#xff09;栅格数据&#xff0c;该逐年数据是取的当年月归一化植被指数&#xff08;NDVI&#xff09;的年平均值。&#xff01;该数据来源于NASA定期发布的MOD13A3数据集&#xff01;很多小伙伴拿到数据后…

【leetcode题解】链表

目录 链表 两数相加 两两交换链表中的节点 重排链表 合并 K 个升序链表&#xff08;困难&#xff09; K 个一组翻转链表 链表 1. 常用技巧 画图&#xff01;&#xff01;&#xff01;&#xff08;直观形象&#xff0c;便于我们理解&#xff09;引入虚拟“头”节点&#xf…

Windows打开ftp局域网共享

前提是windows已经设置好开机账号密码了&#xff0c;否则教程不适用 第一先打开电脑ftp共享配置 点击保存即可 2.设置要共享到其他电脑的文件路径&#xff08;如果你要共享整个盘你就设置整个盘&#xff0c;如果是共享盘中某文件就设置某文件&#xff0c;这里是某文件&#x…

我爱学算法之——滑动窗口攻克子数组和子串难题(中)

学习算法&#xff0c;继续加油&#xff01;&#xff01;&#xff01; 一、将 x 减到 0 的最小操作数 题目解析 来看这一道题&#xff0c;题目给定一个数组nums和一个整数x&#xff1b;我们可以在数组nums的左边或者右边进行操作&#xff08;x减去该位置的值&#xff09;&#…

从零开始上手huggingface

1. 环境配置 # git 安装&#xff1a;https://git-scm.com/ # git lfs安装&#xff1a;https://git-lfs.com git lfs install # huggingface-cli 安装&#xff1a;https://huggingface.co/docs/hub/index pip install huggingface_hub2. 网站直接下载模型 可能会中断&#xff…

用 pytorch 从零开始创建大语言模型(六):对分类进行微调

用 pytorch 从零开始创建大语言模型&#xff08;六&#xff09;&#xff1a;对分类进行微调 6 微调用于分类6.1 微调的不同类别6.2 准备数据集6.3 创建数据加载器6.4 使用预训练权重初始化模型6.5 添加分类头部6.6 计算分类损失和准确率6.7 在监督数据上微调模型6.8 使用LLM进…

Netty——BIO、NIO 与 Netty

文章目录 1. 介绍1.1 BIO1.1.1 概念1.1.2 工作原理1.1.3 优缺点 1.2 NIO1.2.1 概念1.2.2 工作原理1.2.3 优缺点 1.3 Netty1.3.1 概念1.3.2 工作原理1.3.3 优点 2. Netty 与 Java NIO 的区别2.1 抽象层次2.2 API 易用性2.3 性能优化2.4 功能扩展性2.5 线程模型2.6 适用场景 3. 总…

【Linux】信号:信号保存和处理

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;linux笔记仓 目录 01.阻塞信号信号集 02.捕捉信号sigaction可重入函数volatileSIGCHLD 01.阻塞信号 实际执行信号的处理动作称为信号递达&#xff1a;每个信号都有一个默认行为&#xff0c;例如终…