并查集

article/2025/8/27 2:56:13

参考
https://www.cnblogs.com/asdfknjhu/p/12515480.html
https://www.bilibili.com/video/BV13t411v7Fs?p=3
https://leetcode-cn.com/problems/number-of-provinces/solution/python-duo-tu-xiang-jie-bing-cha-ji-by-m-vjdr/

一、基本概念

并查集是一种数据结构
并查集这三个字,一个字代表一个意思。
(Union),代表合并
(Find),代表查找
(Set),代表这是一个以字典为基础的数据结构,它的基本功能是合并集合中的元素,查找集合中的元素
并查集的典型应用是有关连通分量的问题
并查集解决单个问题(添加,合并,查找)的时间复杂度都是 O(1) O(1) O(1)
因此,并查集可以应用到在线算法中

二、并查集的引入

假设有一个图如下图所示:
图片

问题:我们要判断该图有没有环?
如下图所示红线连接的就是一个环
在这里插入图片描述

第一步:将图中6个点平铺写开
在这里插入图片描述第二步:任意从图中的一个边开始,把所有的边都给遍历一遍,首先选择01之间的边,选择边后 我们把这条边所连接的两个顶点放到一个集合中,如下图示:
在这里插入图片描述选择12之间的边,选择边后 我们把这条边所连接的两个顶点放到一个集合中,1已经在集合中了,只需要把2放进去就可以了。集合的意义是该集合中的元素可以在图中相互走通的(即任意两个元素是直接或者间接相连的)如下图示:

在这里插入图片描述
选择34之间的边,选择边后 我们把这条边所连接的两个顶点放到一个集合中,3和4与上一个集合中的元素目前没有连接所以单独放在一个集合中。如下图示:
在这里插入图片描述
选择13之间的边,选择边后 我们把这条边所连接的两个顶点放到一个集合中,13分别在一个集合中,要让他们在一个集合中就要将13所在的集合合并。如下图示:
在这里插入图片描述
到目前为止我们使用过得边有(0,1)(1,2)(3,4)(1,3),由于我们选边的时候不会重复且边(0,1)和(1,0)是同一条边。如果接下来选边的时候(选边不重复),该边的两个端点出现在同一个集合中,则该集合元素可以构成一个环,即该图是有环的。如选择边24,24在集合中,所以可以构成环,该图有环。如图示:在这里插入图片描述
用代码实现的时候用树的结构来存储上面说的集合。在集合合并的时候如果一个边的两个结点所在的树(存储集合的)的根节点指向另一个树的根节点:

在这里插入图片描述在这里插入图片描述在这里插入图片描述
接下来找到边24 2的根和4的根相同都是4,这说明顶点2和4本身就在集合中(树上)。即有环。
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define VERTICES 6
int find_root(int x, int parent[])  // 找到根节点
{int x_root = x;while (parent[x_root] != -1){x_root = parent[x_root];}return x_root;
}
int union_vertices(int x, int y, int parent[])  // 让两个集合合并
{int x_root = find_root(x, parent);int y_root = find_root(y, parent);if (x_root == y_root)return 0;else{parent[x_root] = y_root;return 1;}
}
int main(void)
{int parent[VERTICES] = { 0 };memset(parent, -1, sizeof(parent));   // 初始化int edges[6][2] = { {0,1},{1,2},{1,3},{2,4},{3,4},{2,5} }; // 边集for (int i = 0; i < 6; i++){int x = edges[i][0];int y = edges[i][1];if (union_vertices(x, y, parent) == 0){printf("Cycle detected!\n");system("pause");exit(0);}}printf("No cycle found.\n");system("pause");return 0;
}

三、根据Rank的合并(压缩路径)

为了避免树的构建出现链条形状,影响根的查询,所以在合并的时候让矮树的根节点指向高树的根节点。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define VERTICES 6
int find_root(int x, int parent[])  // 找到根节点
{int x_root = x;while (parent[x_root] != -1){x_root = parent[x_root];}return x_root;
}
int union_vertices(int x, int y, int parent[],int rank[])  // 让两个集合合并
{int x_root = find_root(x, parent);int y_root = find_root(y, parent);if (x_root == y_root)return 0;else{if (rank[x_root] > rank[y_root])  // 让 少的指向多 的{parent[y_root] = x_root;}else if (rank[x_root] < rank[y_root])parent[x_root] = y_root;else{parent[x_root] = y_root;   // 这个随便可以rank[y_root]++;}return 1;}
}
int main(void)
{int parent[VERTICES] = { 0 };int rank[VERTICES] = { 0 };memset(rank, 0, sizeof(rank));memset(parent, -1, sizeof(parent));int edges[6][2] = { {0,1},{1,2},{1,3},{2,4},{3,4},{2,5} };for (int i = 0; i < 6; i++){int x = edges[i][0];int y = edges[i][1];if (union_vertices(x, y, parent,rank) == 0){printf("Cycle detected!\n");system("pause");exit(0);}}printf("No cycle found.\n");system("pause");return 0;
}

python并查集模板分析

一、并查集的实现

1、集合树用字典存储的定义

并查集跟树有些类似,只不过她跟树是相反的。在树这个数据结构里面,每个节点会记录它的子节点。在并查集里,每个节点会记录它的父节点。
并查集使用的是字典来存储树,字典的键是子节点,值是父节点。并且把一个并查集的所有操作和存储结构封装到一个类中:

class UnionFind:def __init__(self):"""记录每个节点的父节点"""self.father = {}# self.value = {}  如果边有权重 # key 本节点名称 ,value 为本节点 到 根点的权值

在这里插入图片描述可以看到,如果节点是相互连通的(从一个节点可以到达另一个节点),那么他们在同一棵树里,或者说在同一个集合里,或者说他们的祖先是相同的。

2、初始化

当把一个新节点添加到并查集中,它的父节点应该为空

 def add(self,x):"""添加新节点"""if x not in self.father:self.father[x] = None

在这里插入图片描述

3、合并两个节点

如果发现两个节点是连通的(两个节点被一个边连接起来),那么就要把他们合并。然后如果他们的根是相同的(同属一棵树),则任选一个节点作为另一个结点的父节点。这里究竟把谁当做父节点一般是没有区别的。如果他们的根是不同的,则让一个树的根节点作为另一个数的根节点的父节点。

def merge(self,x,y,val):"""合并两个节点"""root_x,root_y = self.find(x),self.find(y) # 查找结点x和y的根节点  if root_x != root_y:						# 根节点不相同 则合并self.father[root_x] = root_y

在这里插入图片描述

4、查找一个节点的根节点

查找一个节点的根节点方法是:如果节点的父节点不为空,那就不断迭代。

def find(self,x):"""查找根节点"""root = xwhile self.father[root] != None:root = self.father[root]return root

在这里插入图片描述

5、路径压缩

这里有一个优化的点:如果我们树很深,比如说退化成链表,那么每次查询的效率都会非常低。所以我们要做一下路径压缩。也就是把树的深度固定为二。

这么做可行的原因是,并查集只是记录了节点之间的连通关系,而节点相互连通只需要有一个相同的根节点(祖先)就可以了。

路径压缩可以用递归,也可以迭代。这里用迭代的方法。

    def find(self,x):"""查找根节点路径压缩"""root = xwhile self.father[root] != None:root = self.father[root]# 路径压缩while x != root:original_father = self.father[x]self.father[x] = rootx = original_fatherreturn root

路径压缩的时间复杂度为O(log⁡∗n)O(\log^*n)O(log∗n)
log⁡∗n\log^*nlog∗n 表示 n 取多少次log⁡2n\log_2nlog2​n并向下取整以后 变成 1
可以认为O(log⁡∗n)=O(1)O(\log^*n) = O(1)O(log∗n)=O(1),因为log⁡∗265536=5\log*2{65536} = 5log∗265536=5,而2655362^{65536}265536是一个天文数字。这个时间复杂度当成结论记下就可以。
在这里插入图片描述在这里插入图片描述

6、两节点是否连通

我们判断两个节点是否处于同一个连通分量的时候,就需要判断它们的根节点(祖先)是否相同

def is_connected(self,x,y):"""判断两节点是否相连"""return self.find(x) == self.find(y)

完整模板

class UnionFind:def __init__(self):"""记录每个节点的父节点"""self.father = {}def find(self,x):"""查找根节点路径压缩每一次查找节点x的根节点的时候都对该节点进行路径压缩"""root = xwhile self.father[root] != None:root = self.father[root]# 路径压缩while x != root:original_father = self.father[x]self.father[x] = rootx = original_fatherreturn rootdef merge(self,x,y,val):"""合并两个节点"""root_x,root_y = self.find(x),self.find(y)if root_x != root_y:self.father[root_x] = root_ydef is_connected(self,x,y):"""判断两节点是否相连"""return self.find(x) == self.find(y)def add(self,x):"""添加新节点"""if x not in self.father:self.father[x] = None

http://chatgpt.dhexx.cn/article/0E06kRU5.shtml

相关文章

并查集(详细解释+完整C语言代码)

目录 1概论 2.树的表现形式 3.代码实现 3.1创立集合 3.2合并 3.3查询 3.4路径压缩 第一个方法&#xff1a;查找时优化 第二个方法&#xff1a;合并时优化&#xff08;加权标记法&#xff09; 4.完整代码 4.1优化前 4.2优化后 1概论 并查集是一种十分精巧且实用的树形…

时间序列预测的一般步骤

一、ARIMA模型概述 ​​ ARMA模型就是AR和MA的简单结合&#xff0c;同时包含了历史数值项和错误项。由于AR和MA模型都对时间序列有平稳性要求&#xff0c;ARMA模型也存在这个限制&#xff0c;因此我们将其拓展到ARIMA模型&#xff0c;其可以解决非平稳性问题。引入的差分概念是…

时间序列预测算法总结

时间序列算法 time series data mining 主要包括decompose&#xff08;分析数据的各个成分&#xff0c;例如趋势&#xff0c;周期性&#xff09;&#xff0c;prediction&#xff08;预测未来的值&#xff09;&#xff0c;classification&#xff08;对有序数据序列的feature提…

基于深度学习的时间序列预测

# 技术黑板报 # 第十一期 推荐阅读时长&#xff1a;15min 前言 时间序列建模历来是学术和工业界的关键领域&#xff0c;比如用于气候建模、生物科学和医学等主题应用&#xff0c;零售业的商业决策和金融等。虽然传统的统计方法侧重于从领域专业知识层面提供参数模型&#xff0…

预测类问题中的时间序列预测模型

前言&#xff1a;时间序列预测模型适用于含有时间变量的模型预测&#xff0c;对中短期的预测有着较好的结果&#xff0c;对长期预测不适用。本文重点介绍ARIMA模型的原理及代码实现。 其他模型总结&#xff1a;​【Python】Python中的经典时间序列预测模型总结_风度78的博客-C…

Transformer 在时间序列预测中的应用

2017年&#xff0c;Google的一篇 Attention Is All You Need 为我们带来了Transformer&#xff0c;其在NLP领域的重大成功展示了它对时序数据的强大建模能力&#xff0c;自然有人想要把Transformer应用到时序数据预测上。在Transformer的基础上构建时序预测能力可以突破以往的诸…

【推荐收藏】11种比较常用的时间序列预测模型

时间序列及其预测是日常工作中建模&#xff0c;分析&#xff0c;预测的重要组成部分。 本文我们将从0开始介绍时间序列的含义&#xff0c;模型及其分析及其常用的预测模型。 文章目录 交流时间序列定义时间序列预测模型与方法原始数据朴素法简单平均法移动平均法指数平滑法一次…

时间序列预测的20个基本概念总结

1、时间序列 时间序列是一组按时间顺序排列的数据点 比如&#xff1a; 每小时的气压每年的医院急诊按分钟计算的股票价格 2、时间序列的组成部分 时间序列数据有三个主要组成部分。 趋势季节性残差或白噪声 3、趋势 在时间序列中记录的长期缓慢变化/方向。 4、季节性 …

时间序列-预测(Forcasting):时间序列预测算法总结

一、背景介绍 绝大部分行业场景,尤其是互联网、量化行业,每天都会产生大量的数据。金融领域股票价格随时间的走势;电商行业每日的销售额;旅游行业随着节假日周期变化的机票酒店价格等; 我们称这种不同时间收到的,描述一个或多种特征随着时间发生变化的数据,为时间序列…

时间序列预测 | ARMA应用指南

ARMA可谓是时间序列最为经典常用的预测方法&#xff0c;广泛应有于涉及时间序列的各个领域。ARMA模型自出道以来&#xff0c;出场次数不可胜数。想必大家也都不陌生&#xff0c;常学常新&#xff0c;我们今天不妨再来回顾一遍&#xff5e;。 ARMA全称Autoregressive moving ave…

时间序列预测的7种方法

import pandas as pd#取数 #dfpd.read_csv(jetrail.csv) #print(df.head()) ID Datetime Count 0 0 25-08-2012 00:00 8 1 1 25-08-2012 01:00 2 2 2 25-08-2012 02:00 6 3 3 25-08-2012 03:00 2 4 4 25-08-2012 04:00 2#pr…

Transformer时间序列预测

介绍&#xff1a; 提示&#xff1a;Transformer-decoder 总体介绍 本文将介绍一个 Transformer-decoder 架构&#xff0c;用于预测Woodsense提供的湿度时间序列数据集。该项目是先前项目的后续项目&#xff0c;该项目涉及在同一数据集上训练一个简单的 LSTM。人们认为 LSTM 在…

时间序列预测的8种常用方法简介

时间序列预测的7种方法 1. 朴素预测法&#xff08;Naive Forecast) 如果数据集在一段时间内都很稳定&#xff0c;我们想预测第二天的价格&#xff0c;可以取前面一天的价格&#xff0c;预测第二天的值。这种假设第一个预测点和上一个观察点相等的预测方法就叫朴素法&#xff…

常见的时间序列预测模型python实战汇总

最完整的时间序列分析和预测&#xff08;含实例及代码&#xff09;&#xff1a;https://mp.weixin.qq.com/s/D7v7tfSGnoAqJNvfqGpTQA 1 时间序列与时间序列分析 在生产和科学研究中&#xff0c;对某一个或者一组变量 x(t)x(t) ARIMA 模型对时间序列的要求是平稳型。因此&#…

时间序列预测模型

时间序列数据一般有以下几种特点&#xff1a;1.趋势(Trend) 2. 季节性(Seasonality)。 趋势描述的是时间序列的整体走势&#xff0c;比如总体上升或者总体下降。下图所示的时间序列是总体上升的&#xff1a; 季节性描述的是数据的周期性波动&#xff0c;比如以年或者周为周期&…

【数据分析】基于时间序列的预测方法

时间序列预测 目录 时间序列预测1.时间序列介绍2.原始数据集3.导入数据4.检测时间序列的平稳性5.如何使时间序列平稳5.1 估计和消除趋势5.1.1 对数转换5.1.2 移动平均 5.2 消除趋势和季节性5.2.1 差异化5.2.2 分解 6.预测时间序列6.1 AR Model6.2 MA Model6.3 Combined Model6.…

Matlab实现时间序列预测

文章目录 一、数据准备二、时间序列预测分类1、输入为xt&#xff0c;输出是yt2、有x值&#xff0c;有y值&#xff1a;NARX(1)选择模型类型(2)选择输出&#xff0c;只有y_t(3)选择70%用来作为训练数据&#xff0c;15%用来作为验证使用&#xff0c;15%用来测试(4)选择delay(5)开始…

【时序预测】Transformer模型在时间序列预测领域的应用

今天又是一篇Transformer梳理文章&#xff0c;这次应用场景是时间序列预测。Transformer的序列建模能力&#xff0c;让其天然就比较适合时间序列这种也是序列类型的数据结构。但是&#xff0c;时间序列相比文本序列也有很多特点&#xff0c;例如时间序列具有自相关性或周期性、…

11种常见的时间序列预测方法

参考内容&#xff1a;4大类11种常见的时间序列预测方法总结和代码示例 代码地址&#xff1a; https://github.com/SeafyLiang/machine_learning_study/blob/master/time_series 11种常见的时间序列预测方法 1、指数平滑Exponential Smoothing2、Holt-Winters 法3、自回归 (AR)…

时间序列预测方法最全总结!

时间序列预测就是利用过去一段时间的数据来预测未来一段时间内的信息&#xff0c;包括连续型预测&#xff08;数值预测&#xff0c;范围估计&#xff09;与离散型预测&#xff08;事件预测&#xff09;等&#xff0c;具有非常高的商业价值。 需要明确一点的是&#xff0c;与回归…