算法基础:并查集详解

news2024/12/22 13:24:08

并查集

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中。其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

以上释义来自:百度百科

并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题


  • 根节点的编号就代表该集合的编号
  • 树中每一个节点存储着它的父亲节点
基本原理
  • 如何判断树根
if(p[x]==x)
  • 如何求X的集合编号
while(p[x]!=x) x = p[x];
  • 如何合并两个集合(本质上就是合并两棵树)

方法:将集合1或者2插入到集合1或者2的某个位置即可【根节点之下的某个位置】

** 设Px是x的集合编号,Py是y的集合编号,则合并操作**:p[x] = y;


画解合并/插入

初始时每个集合都是一个独立的集合且集合的编号等于自己下标数。
:)例:p[2]=2,p[6]=6将两个集合进行合并

  • 1.找到p[2]【该集合的根就是该集合的编号】集合的父节点
  • 2.找到p[6]集合父亲节点
  • 3.p[2]集合的父节点指向p[6]集合即可。
    合并后:p[2] = p[6] = 6;
    因为p[2]=2,p[6]=6
    所以合并后的集合{6,2},一个以6为祖宗节点的集合

插入

将以p[6]为根的集合{4,7,10}插入到以p[5]为根的集合{5,3,9}
显然p[3]=5,p[9]=5,p[5]=5p[4]=6,p[7]=6,p[10]=6,p[6]=6
那么只要找到集合p[6]中的祖宗节点【6】指向集合p[5]即可【不一定直接作为其第一个儿子,指向其孙子【比如指向p[3]】也是可以的,因为p[3]的父亲也是5】。
所以有:p[6] = find(3) or p[6] = find(9)【find(x)为寻找x节点的祖宗节点】


路径压缩

可以看到,在计算x的集合编号的时候还是比较耗时的,所以介入了一个优化算法 【路径压缩】

由于每次都要将x指向它的父亲节点,x = p[x]
因此只要搜索一次成功后将该路径上所有的节点x都指向其祖宗节点。这样再一次查询之后,之后的每一次都是近乎O(1)的操作了

这也是并查集NB的一个地方。

代码模板
 int p[N]; //存储每个点的祖宗节点
    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);
模板练习


// Problem: 合并集合
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/838/
// Memory Limit: 64 MB
// Time Limit: 1000 ms

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int p[N];
int find(int x)
{
	if(p[x]!=x) p[x] = find(p[x]);
	//返回祖宗节点
	return p[x];

}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		p[i] = i;
	}	
	while(m--)
	{
		char op[2];
		int a,b;
		scanf("%s%d%d",op,&a,&b);
		//合并集合
		if(*op=='M') p[find(a)]=find(b);
		else if(find(a)==find(b))
		printf("Yes\n");
		else
		printf("No\n");
	}
	return 0;
}

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

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

相关文章

【网络编程】网络编程概念 | TCP和UDP的区别 | UDP数据报套接字编程 | Socket

文章目录 网络编程一、什么是网络编程1.TCP和UDP的区别 二、UDP数据报套接字编程DatagramSocketDatagramPacket回显服务器&#xff08;echo server&#xff09; 网络编程 一、什么是网络编程 通过网络&#xff0c;让两个主机之间能够进行通信。基于通信来完成一定的功能。 ​…

Linux网络-DNS域名解析服务

目录 一.DNS相关介绍 1.DNS是什么 2.DNS系统的分布式数据结构 根域 顶级域 二级域 子域 主机 3.服务器类型 主域名服务器 从域名服务器 缓存域名服务器 转发域名服务器 二.DNS域名解析 1.DNS域名解析方式及功能 2.DNS域名解析查询方式 2.1.递归查询&#xff0…

Verilog基础语法——parameter、localparam与`define

Verilog基础语法——parameter、localparam与define 写在前面一、localparam二、parameter三、define写在最后 写在前面 在使用Verilog编写RTL代码时&#xff0c;如果需要定义一个常量&#xff0c;可以使用define、parameter和localparam三种进行定义与赋值。 一、localparam …

施耐德EOCR-2CT-300/5电流互感器 50HZ 5VA

EOCR主要产品有电子式电动机保护继电器&#xff0c;电子式过电流继电器&#xff0c;电子式欠电流继电器&#xff0c;电子式欠电压继电器&#xff0c;其它保护和监视装置&#xff0c;电流互感器。 施耐德EOCR-2CT-300/5电流互感器 EOCR-2CT系列型号&#xff1a; EOCR 2CT 100…

操作系统安全:Linux安全审计,Linux日志详解

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

18.Nacos配置管理-微服务读取Nacos中的配置

需要解决的问题 1.实现配置更改热更新&#xff0c;而不是改动了配置文件还要去重启服务才能生效。 2.对多个微服务的配置文件统一集中管理。而不是需要对每个微服务逐一去修改配置文件&#xff0c;特别是公共通用的配置。 配置管理服务中的配置发生改变后&#xff0c;回去立…

leetcode-二叉树的镜像-91

题目要求 思路1 1.遍历一遍二叉树&#xff0c;将左边的结点对应创建一个右边的结点 2.用此方法空间复杂度O(n)&#xff0c;并不是最优 思路2 1.将一个结点的左右子树进行交换&#xff0c;如果左子树还有左右结点&#xff0c;就再交换左子树的左右结点&#xff0c;以此递归下去…

vscode 创建代码模版

在vscode中快捷创建代码模版 1.在VSCode中&#xff0c;按下Ctrl Shift P&#xff08;Windows/Linux&#xff09;或Cmd Shift P&#xff08;Mac&#xff09;打开命令面板。 2.然后输入"Preferences: Configure User Snippets"并选择该选项。打开一个json文件用户…

Python函数小知识

目录 一、函数的定义和调用 二、函数参数 三、函数作用域 四、递归函数和匿名函数 一、函数的定义和调用 def 函数名(参数): 自定义函数可以分为有参函数和无参函数 。 函数的作用&#xff1a; 在Python中定义函数可以提高代码的复用率&#xff0c;避免重复的代码&#xff0c;…

matlab保存示波器数据

再重新运行一下示波器 然后就可以在工作区看见&#xff08;这里没有运行所以没有&#xff09; 将保存到文件夹中方便后续绘图

服务于金融新核心系统 星辰天合与中电金信完成产品兼容认证

近日&#xff0c;北京星辰天合科技股份有限公司&#xff08;简称&#xff1a;XSKY星辰天合&#xff09;与中电金信软件有限公司&#xff08;简称&#xff1a;中电金信&#xff09;完成产品兼容性认证&#xff0c;星辰天合的企业级分布式统一数据平台 XEDP 符合金融级数字底座&q…

“一个有趣的C语言代码”分析

“一个有趣的C语言代码” 一个有趣的C语言代码-流浪的海豚-ChinaUnix博客 #include <stdio.h> int print() {printf("hello world!\n");return 0; } int main(void) {long base[0];long* result base3;*(result1) *result;*result (long)print;return 0; …

Qt : 在QTreeWidget中添加自定义右键菜单

一、引言 如图&#xff0c;我们需要在一个QTreeWidget 控件中添加了自定义右键菜单。 二、思路 如何做到的呢&#xff0c;很简单。浅浅记录和分享一下。 继承QTreeWidget&#xff0c;定义一个子类CustomTreeWidget &#xff0c;在重写contextMenuEvent 事件即可。 三、代…

数据结构初阶——树和二叉树

数据结构初阶——树和二叉树 1. 树的概念和结构1.1 树的概念1.2 树的表示 2. 二叉树2.1 二叉树的概念和结构2.2 二叉树的存储结构2.2.1 顺序存储2.2.2 链式存储 3. 二叉树的顺序结构及实现——堆3.1 堆的概念和结构3.2 堆的实现3.2.1 堆的定义3.2.2 堆的向上调整3.2.3 堆的向下…

Pycharm/Dataspell中使用jupyter导入ros humble包

配置ros humble对应python包路径文件 首先在~/.local/lib/python3.10/site-packages目录下新建一个.pth文件&#xff0c;如下图所示。 将对应的ros humble的python包的路径配置在上述文件中&#xff0c;一行放置一个路径&#xff0c;对应的路径如下图所示。 完成上述操作后…

ESP32 IDF环境 连接WIFI

新建wifi.h #ifndef __WIFI_H_ #define __WIFI_H_#include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_wifi.h&…

IntelliJ IDEA - 10 款 IDEA 宝贝插件,YYDS!

好久没发这种实用贴了&#xff0c;最近用到了一些能提升工作效率的IDEA插件&#xff0c;给小伙伴们分享一下。相信我&#xff0c;我分享的这些插件&#xff0c;都是实实在在能解决实际开发场景中痛处的。 1、POJO to JSON 开发工作中&#xff0c;常常在设计完API后&#xff0c…

recat组件使用Antd

安装和初始化 项目中进行安装yarn add antd或者npm i antd安装 使用 在App.js文件中引入按钮并使用 需要引入自己的css文件 模块化 import React, { Component } from react import { Button } from antd; //引入按钮 import antd/dist/antd.css; //还需要引入css样式 …

javascript使用setTimeout函数来实现仅执行最后一次操作

在JavaScript中&#xff0c;setTimeout函数用于在指定的毫秒数后执行一个函数或计算表达式。它的主要用途是允许开发者延迟执行某些代码&#xff0c;而不是立即执行。 当我们想要确保仅最后一次更新UI时&#xff0c;我们可以使用setTimeout来合并多次连续的更新请求。具体做法…

Ansible自动化

Ansible自动化 自动化的需求&#xff1a; 1. 在什么样的场景下需要自动化&#xff1f; 批量化的工作&#xff1a; 装软件包、配置服务、升级、下发文件… 2. 为什么在自动化工具中选择ansible&#xff1f; 对比shell脚本&#xff1a; 相对于用shell的脚本来实现自动化&#x…