并查集 + Tarjan算法

并查集 + Tarjan 算法

并查集是一种用于找出一个森林(图)中树(连通分支)的个数的算法,也可用于判断两个节点是否在同一棵树上。它在每一棵树(连通分支)上选择一个节点作为本棵树(连通分支)的代表。对于给定两个节点,如果他们具有相同的代表节点,则说明两个节点在同一个节点上。

一、并查集的简单应用

例题 1:城市群的数量

题目描述:

魔法大陆上有 n 个城市,编号为 1 到 n。城市与城市之间的道路均为双向道路,共有 m 条双向道路,并非任意两个城市之间都有双向道路。问,魔法大陆上有多少个城市群?
若两个城市之间存在一条双向道路,则两个城市属于同一个城市群。任意两个城市之间最多只有一条双向道路。

输入格式:

第一行包含两个整数 n,m,含义与问题描述中相同。接下来 m 行,每行包含两个整数 u,v,表示城市 u 和城市 v 之间存在一条双向道路。

输出格式:

输出共一行,包含一个整数,表示城市群的数量。

代码示例:

# DFS 暴力做法
from sys import setrecursionlimit
setrecursionlimit(1000000)
def dfs(cur):# traversal from the city whose index is curglobal n,g,visitedvisited[cur] = True# search next cityfor i in g[cur]:if visited[i]:continuedfs(i) def counter():# traversal all if the city,everytime it uses the dfs() function,add one to the answer global n,visitedans = 0for i in range(1,n+1):if visited[i]:continue# hasn't been visited yetans += 1# mark all of the cities that belong to the same group dfs(i)return ans# main part
n,m = map(int,input().split())
# create a list to record the roads
g = [[] for i in range(n+1)]
for _ in range(m):u,v = map(int,input().split())g[u].append(v)g[v].append(u)
# create a visited list to record whether the city has been visited or not 
visited = [False for i in range(n+1)]
visited[0] = True
print(counter())
# 并查集模板题
# 找出x所在树的根节点
def find(x):if pre[x] != x:return find(pre[x])return x
# 判断两个节点是否在同一个树(城市群)上,如果不在,则合并两个城市群并计数
def join(x,y):global nx_root = find(x) # 找出x所在树的根节点y_root = find(y) # 找出y所在树的根节点if x_root != y_root:# 两个节点不在同一个树上(城市群),将两个城市群合并,以后再碰到这两个树的节点就不会重复计数了,保证每一颗树只计数一次pre[x_root] = y_root # 将x_root变成y_root的子节点,合并两树# 初始时有n个节点,彼此没有路径关系,视为n个城市群# 随着道路关系的引入,城市群不断合并,n就是城市群的数量n -= 1  # 主程序
n,m = map(int, input().split())
# 注意序号从1开始
pre = [i for i in range(n+1)]
for _ in range(m):u,v = map(int,input().split())join(u,v)
print(n)
# 优化后的并查集,在找到x的根节点后直接将x的前驱节点改为根节点,缩短x的子节点查找根节点的路径长度
n, m = map(int, input().split())
p = list(range(n + 1))
def find_root(x):if p[x] == x:return xp[x] = find_root(p[x])return pre[x]
for i in range(m):u, v = map(int, input().split())u_root = find_root(u)v_root = find_root(v)if u_root != v_root:p[u_root] = v_rootn-=1
print(n)

例题 2:修改数组(第10届蓝桥杯省赛真题)

题目描述:

给定一个长度为 N N N 的数组 A = [ A 1 , A 2 , ⋅ ⋅ ⋅ , A N ] A = [A_1, A_2, · · ·, A_N] A=[A1,A2,⋅⋅⋅,AN],数组中有可能有重复出现的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改 A 2 , A 3 , ⋅ ⋅ ⋅ , A N A_2, A_3, · · ·, A_N A2,A3,⋅⋅⋅,AN
当修改 A i A_i Ai 时,小明会检查 A i A_i Ai 是否在 A 1 ∼ A i − 1 A_1 ∼ A_{i−1} A1Ai1 中出现过。如果出现过,则小明会给 A i A_i Ai 加上 1 ;如果新的 A i A_i Ai 仍在之前出现过,小明会持续给 A i A_i Ai 加 1 ,直到 A i A_i Ai 没有在 A 1 ∼ A i − 1 A_1 ∼ A_{i-1} A1Ai1 中出现过。
A N A_N AN 也经过上述修改之后,显然 A A A 数组中就没有重复的整数了。现在给定初始的 A A A 数组,请你计算出最终的 A A A 数组。

输入格式:

第一行包含一个整数 N。
第二行包含 N 个整数 A 1 , A 2 , ⋅ ⋅ ⋅ , A N A_1, A_2, · · ·, A_N A1,A2,⋅⋅⋅,AN

输出格式:

输出 N 个整数,依次是最终的 A 1 , A 2 , ⋅ ⋅ ⋅ , A N A_1, A_2, · · ·, A_N A1,A2,⋅⋅⋅,AN

代码示例:

# 并查集
def find(x):if x == f[x]:# 找到还没有出现过的元素return xp = xwhile p != f[p]:p = f[p]f[x] = preturn pn = int(input())
a = [int(i) for i in input().split()]
f = [i for i in range(1000001)]  # 使用a[]中元素的最大值作为并查集数组容量
for i in range(n):# 更新a[]a[i] = find(a[i])# 更新并查集f[a[i]] = find(a[i]+1) 
print(' '.join(list(map(str, a))))

二、Tarjan 算法

(1)算法作用

T a r j a n Tarjan Tarjan 算法是DFS序和并查集的结合应用,可以高效地求出树上两点的最近公共祖先( L C A LCA LCA),求出的 L C A LCA LCA 可以用于求树上两点之间的最短距离、树上差分等问题。

(2)算法思路

  1. 由于 T a r j a n Tarjan Tarjan 算法是一种离线算法,所以要先将所有的查询操作存储起来,等待一并处理。
  2. 以树的根节点作为入口进行DFS遍历,同时利用并查集维护当前节点的父节点
  3. 在遍历当前节点时,标记当前节点
  4. 先遍历当前节点的所有孩子节点,如果未被访问,DFS这个孩子,然后调用并查集将这个孩子的父节点标记为当前节点
  5. 遍历以当前节点为主节点的所有询问请求,如果当前节点的询问请求的另一个节点已经有标记了,那么这个询问的答案就是另一个节点此时的父节点,记录这个答案

(3)算法模板

def find(x):if x == fa[x]:return xp = xwhile p != fa[p]:p = fa[p]# 合并路径版的并查集fa[x] = preturn pdef tarjan(x):visited[x] = True# 遍历所有子节点for i in e[x]:if visited[i]:continuetarjan(i)fa[i] = x# 检查以x为主元素的查询for t in query[x]:if visited[t[0]]:ans[t[1]] = find(t[0])n,m,s = map(int,input().split())
e = [[] for _ in range(n+1)] # 存储边的关系
visited = [False]*(n+1)
# 并查集
fa = [i for i in range(n+1)]
# 存储查询,元素类型是二元组
query = [[] for _ in range(n+1)]
# 存储结果
ans = [-1]*m
# 接收输入,构造树
for _ in range(n-1):x,y = map(int,input().split())e[x].append(y)e[y].append(x)
# 接收查询信息
for i in range(m):x,y = map(int,input().split())# 在记录每一组查询的同时记录查询的次序query[x].append((y,i))query[y].append((x,i))tarjan(s)
# 输出结果
for i in ans:if i == -1:continueprint(i)

扩展应用:计算树上两点之间的最短距离

L C A LCA LCA 的最基本应用是求树上两点之间的最短距离,公式如下:

d i s t ( x , y ) = d e e p [ x ] + d e e p [ y ] − 2 ∗ d e e p [ L C A ( x , y ) ] dist(x,y) = deep[x] + deep[y] - 2*deep[LCA(x,y)] dist(x,y)=deep[x]+deep[y]2deep[LCA(x,y)]

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

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

相关文章

leetcode代码记录(买卖股票的最佳时机

目录 1. 题目:2. 我的代码:小结: 1. 题目: 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股…

蓝桥-时间显示

目录 题目链接 代码 题目链接 1.时间显示 - 蓝桥云课 (lanqiao.cn) 代码 #include <bits/stdc.h> using namespace std;int main() {long long x;cin>>x;int h,m,s;x x / 1000 % (3600*24); // 毫秒化秒&#xff0c;并且保留最后一天的时间h x / 3600; //求得…

python中OrderedDict有序字典类

OrderedDict 是 Python 标准库 collections 模块中的一个类&#xff0c;它实现了有序字典。与普通字典&#xff08;dict&#xff09;不同&#xff0c;OrderedDict 记住了元素被插入时的顺序&#xff0c;因此你可以按照元素被添加的顺序来迭代遍历字典的元素。 OrderedDict 的主…

第四百四十三回

文章目录 1. 概念介绍2. 思路与方法2.1 整体思路2.2 使用方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"自定义Action菜单"相关的内容&#xff0c;本章回中将介绍如何获取屏幕相关参数.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本…

Qtxlsx第三方库的安装和使用

本文仅作为一个记录&#xff0c;安装QtXlsx方便操作excel&#xff0c;主要参考了这篇博文&#xff1a;https://blog.csdn.net/u014779536/article/details/111769792 1&#xff0c;下载安装Perl脚本Strawberry Perl for Windows&#xff0c;默认安装strawberry-perl-5.30.0.1-…

MySQL Payload

常见Payload Version SELECT version Comment SELECT 1 -- commentSELECT 1 # commentSELECT /*comment*/1 Space 0x9 0xa-0xd 0x20 0xa0 Current User SELECT user()SELECT system_user()SELECT current_role() List User SELECT user FROM mysql.user Current Database SELEC…

Vue中的ref与reactive

在 Vue 中&#xff0c;ref 和 reactive 是用于处理响应式数据的两个不同的 API。 1. ref ref 是 Vue 3 中引入的 API&#xff0c;用于创建一个响应式的引用对象。它接收一个初始值作为参数&#xff0c;并返回一个包含了一个 .value 属性的对象。这个 .value 属性持有着实际的值…

程序·人生

诡异之极 2024.03.12 清新环境&#xff08;股票代码002573&#xff09;委托卖出 20000股&#xff0c;委托价4.58&#xff0c;当日最高价4.57 2024.03.11 清新环境&#xff08;股票代码002573&#xff09;委托卖出 20000股&#xff0c;委托价4.55&#xff0c;当日最高价4.54 …

汉明码检错与纠错的结论(hamming code)

假如一组二进制数据为101&#xff0c;另外一组为111&#xff0c;那么显然把第一组的第二位数据0改成1就可以变成第二组数据111&#xff0c;所以两组数据的汉明距离就为1 简单点说&#xff0c;汉明距离就是一组二进制数据变成另一组数据所需的步骤数&#xff08;它表示两个相同长…

策略模式图

策略模式 小小的图解 主要的三个角色 Strategy—抽象策略角色ConcreateStrategy—具体策略角色Context—上下文角色 封装了对具体策略的调用可以使用set的依赖注入也可以使用构造方法 核心是上下文角色 只要调用上下文角色就行&#xff0c;实现解耦 策略 工厂 将上下文角…

深入理解MP4视频文件裁剪原理[下下]

深入理解MP4视频文件裁剪原理[上] 深入理解MP4视频文件裁剪原理[中] 深入理解MP4视频文件裁剪原理[下] 深入理解MP4视频文件裁剪原理[下下] 3.22 stco box* stco box记录了每个chunk在MP4文件中的偏移量。如下图24: 图24 这里的offset指的是chunk的起始位置相对于整个MP4文…

Pytoch安装记录

使用pycharm 1、CUDA的安装 官网&#xff1a;CUDA Toolkit Archive | NVIDIA Developer 选择对应的版本 选择对应的版本进行下载&#xff1a; 有3个多G cuda的安装需要注意&#xff0c;如果没有安装vs&#xff0c;则需要选择自定义安装&#xff0c;在自定义的安装中取消 安…

【系统架构师】-系统可靠性分析与设计

1、可靠性与可用性区别 1、系统可靠性&#xff1a;系统在规定时间内及规定的环境下&#xff0c;完成规定功能的能力&#xff0c;即系统无故障运行的概率 2、系统可用性&#xff1a;在某个给定时间点上系统能够按照需求执行的概率。 可靠性分为软件、硬件可靠性 2、可靠性指标…

观察者模式 C++

&#x1f442; Honey Honey - 孙燕姿 - 单曲 - 网易云音乐 目录 &#x1f33c;前言 &#x1f33c;描述 &#x1f382;问题 &#x1f4aa;解决方案 &#x1f232;现实场景 代码 场景1 -- 报纸发行 场景 解释 代码 场景2 -- 气象资料发布 场景3 -- 过红绿灯 &#x…

【200】Java8 调用 Gson2.10 读取 JSON 数组的六种方法。

准备工作 我们先看一下本文例子中的 JSON 文件&#xff1a;cars.json [{"no": "鲁B0001","color": "红色","code": 1},{"no": "鲁B0002","color": "黑色","code": 2},…

视频分块上传Vue3+SpringBoot3+Minio

文章目录 一、简化演示分块上传、合并分块断点续传秒传 二、更详细的逻辑和细节问题可能存在的隐患 三、代码示例前端代码后端代码 一、简化演示 分块上传、合并分块 前端将完整的视频文件分割成多份文件块&#xff0c;依次上传到后端&#xff0c;后端将其保存到文件系统。前…

C++读取.bin二进制文件

C读取.bin二进制文件 在C中&#xff0c;可以使用文件输入/输出流来进行二进制文件的读写操作&#xff0c;方便数据的保存和读写。 //C读取bin二进制文件 int read_bin() {std::ifstream file("data_100.bin", std::ios::in | std::ios::binary);if (file) {// 按照…

【拓扑空间】示例及详解1

例1 度量空间的任意两球形邻域的交集是若干球形邻域的并集 Proof&#xff1a; 任取空间的两个球形邻域、&#xff0c;令 任取,令 球形领域 例2 规定X的子集族,证明是X上的一个拓扑 Proof&#xff1a; 1. 2., &#xff08;若干个球形邻域的并集都是的元素&#xff0c;元素…

SSM 项目学习(Vue3+ElementPlus+Axios+SSM)

文章目录 1 项目介绍1.1 项目功能/界面 2 项目基础环境搭建2.1 创建项目2.2 项目全局配置 web.xml2.3 SpringMVC 配置2.4 配置 Spring 和 MyBatis , 并完成整合2.5 创建表&#xff0c;使用逆向工程生成 Bean、XxxMapper 和 XxxMapper.xml2.6 注意事项和细节说明 3 实现功能 01-…

Java 开发者必备:JDK 版本详解与选择策略(含安装与验证)

1. JDK 版本 (Oracle Java SE 支持路线图) 数据来源&#xff1a;Oracle Java SE 支持路线图 | 甲骨文中国: https://www.oracle.com/cn/java/technologies/java-se-support-roadmap.html 版本GA DatePremier Support UntilExtended Support Until&#xff08;限 LTS&#xff09…