并查集专题1_图篇

news2024/11/15 15:45:57

1.并查集介绍

并查集支持查询和合并操作,只回答两个节点是不是在一个连通分量中,并不回答路径问题。

如果一个问题具有传递性,可以考虑用并查集。并查集最常见的一种设计思想是把在同一个连通分量中的节点组织成一个树形结构。

2.并查集的核心函数

先搞明白模板里的函数——

首先在并查集类里常见的函数,初始函数、find()函数,union()函数。

def init(self, n):

初始函数里面参数是节点个数n,初始化节点的父节点集合self.father,初始时每个节点都是一个连通域,数据类型可用列表或字典;

在这里插入图片描述

根据题目要求可能还有联通区域的数量(初始有n个);

在合并的时候如果是小树挂大树,那就用哈希表self.size表示每个联通区域的节点个数(用列表也行),key是根,value是节点个数,初始时每个联通区域大小都是1。主要是用在合并两个连通区域的时候判断哪个区域节点少,节点少的挂在节点多的根上。

在合并的时候如果是按秩合并,初始化都是0。在合并的时候秩小的指向秩大的。

def __init__(self, n):
    self.father = {i: i for i in range(n)}
    self.rank = [0]*n
    #self.size = {i: 1 for i in range(n)}
    self.cnt = n

def find(self, x):

find()函数是查找节点x的根节点。代码上可以用 father[x] = y 表示 x 的 father是 y,通过不断沿着搜索father找到x的 root。

def find(self, x):
    while x != self.fatehr[x]:
        x = self.father[x]
    return x

重点请注意,重点请注意,重点请注意。

并查集里有一个比较有意思的一点,就是能把查找的时间复杂度降低到接近常数级别,该操作是——路径压缩和按秩合并

为什么这么说呢?

因为find是从当前节点往上搜索直到找到根节点,所以时间复杂度约等于树的高度,最差的情况为节点个数,find的时间复杂度降低到O(n)。如果进行路径压缩,树的高度不会超过logn。再结合按秩合并可以将时间复杂度降到趋近于O(1)。

路径压缩就是让节点直接指向根节点。
在这里插入图片描述

def find(self, x):
    if x == self.father[x]:
        return x
    # 路径压缩:让查找路径的每一个节点都指向根节点
    self.father[x] = self.find(self.father[x])
    return self.father[x]

def union(self, x,y):

union()函数是合并两个节点,先判断两个节点的根节点是不是同一个,不是的话,就把两个节点相连。

按秩合并就是在合并两个连通区域时使得合并后的树高度尽可能低,所以要将高度低的树指向高度高的树,也就是秩小的指向秩大的。这个秩我理解的就是树的高度。

def union(self, x, y):
   root_x, root_y = self.find(x), self.find(y)
   if root_x != root_y:
	   if self.rank[root_x] < self.rank[root_y]:
	       self.father[root_x] = root_y
	   else:
	       self.father[root_y] = root_x
	       # 两个秩相等因root_y指向root_x,故root_x的秩+1
	       if self.rank[root_x] == self.rank[root_y]:
	           self.rank[root_x] += 1

如果合并的时候是小树挂大树,代码就是:

def union(self, x: int, y: int):
    x, y = self.find(x), self.find(y)
    if x == y:
        return
    if self.size[x] < self.size[y]:
        x, y = y, x
    self.fathert[y] = x
    self.size[x] += self.size[y]

3.并查集模板

无权值模板

class Unionfind:
    """
    初始化函数
    self.father 哈希表 key是当前节点 value是该节点的根节点
    self.rank 是该节点所在联通区域的秩(高度)
    self.size 哈希表 key是联通区域的根节点 value是该联通区域节点个数
    self.cnt 是联通区域的个数
    """
    def __init__(self, n):
        self.father = {i: i for i in range(n)}
        self.rank = [0]*n
        # self.size = {i: 1 for i in range(n)}
        self.cnt = n
        
    """
    查找x节点的根节点
    路径压缩:经过的节点均指向根节点
    """
    def find(self, x):
        if x == self.father[x]:
            return x
        self.father[x] = self.find(self.father[x])
        return self.father[x]
    
    """
    合并两个连通区域
    按秩合并 小秩指向大秩
    """
    def union(self, x, y):
        root_x, root_y = self.find(x), self.find(y)
        if root_x != root_y:
            if self.rank[root_x] < self.rank[root_y]:
                self.father[root_x] = root_y
            else:
                self.father[root_y] = root_x
                if self.rank[root_x] == self.rank[root_y]:
                    self.rank[root_x] += 1

带权值模板

在初始化的时候再用一个哈希表存放权重。在路径压缩以及合并的时候都需要处理一下权重。

class Unionfindweight:
    """
    初始化函数
    self.father 哈希表 key是当前节点 value是该节点的根节点
    self.rank 是该节点所在联通区域的秩(高度)
    self.size 哈希表 key是联通区域的根节点 value是该联通区域节点个数
    self.cnt 是联通区域的个数
    """
    def __init__(self, n):
        self.father = {i: i for i in range(n)}
        self.rank = [0] * n
        self.weight = {i: 0 for i in range(n)}
        # self.size = {i: 1 for i in range(n)}
        self.cnt = n

    """
    查找x节点的根节点
    路径压缩:经过的节点均指向根节点
    """

    def find(self, x):
        if x != self.father[x]:
            root, w = self.find(self.father[x])
            self.father[x] = root
            self.weight[x] += w
        return self.father[x], self.weight[x]

    """
    合并两个连通区域
    """

    def union(self, x, y, w_xy):
        root_x, w_x = self.find(x)
        root_y, w_y = self.find(y)
        self.father[root_x] = root_y
        # 若x的根是a,y的根是b,则有w(xa) + w(ab) = w(xy) + w(yb)
        self.weight[root_x] = w_xy + w_y - w_x

4.实战实战

1319. 连通网络的操作次数

题目

用以太网线缆将 n 台计算机连接成一个网络,计算机的编号从 0 到 n-1。线缆用 connections 表示,其中 connections[i] = [a, b] 连接了计算机 a 和 b。

网络中的任何一台计算机都可以通过网络直接或者间接访问同一个网络中其他任意一台计算机。

给你这个计算机网络的初始布线 connections,你可以拔开任意两台直连计算机之间的线缆,并用它连接一对未直连的计算机。请你计算并返回使所有计算机都连通所需的最少操作次数。如果不可能,则返回 -1 。

在这里插入图片描述

输入:n = 4, connections = [[0,1],[0,2],[1,2]]
输出:1
解释:拔下计算机 1 和 2 之间的线缆,并将它插到计算机 1 和 3 上。

思路和代码

用并查集,得到连通区域的个数,如果边的个数少于n-1,那么返回-1.否则返回的结果就是连通区域个数-1。

代码:

class Unionfind:
    def __init__(self, n):
        self.father = list(range(n))
        self.cnt = n
        self.rank = [0]*n

    def find(self, x):
        if x == self.father[x]:
            return x
        self.father[x] = self.find(self.father[x])
        return self.father[x]

    def union(self, x, y):
        root_x, root_y = self.find(x), self.find(y)
        if root_x == root_y:
            return 
        if self.rank[root_x] < self.rank[root_y]:
            self.father[root_x] = root_y
        else:
            self.father[root_y] = root_x
            if self.rank[root_x] == self.rank[root_y]:
                self.rank[root_x] += 1
        self.cnt -= 1

class Solution:
    def makeConnected(self, n: int, connections: List[List[int]]) -> int:
        if len(connections) < n-1:
            return -1
        uf = Unionfind(n)
        for node1,node2 in connections:
            uf.union(node1,node2)
        return uf.cnt -1

leetcode1971. 寻找图中是否存在路径

题目

有一个具有 n 个顶点的 双向 图,其中每个顶点标记从 0 到 n - 1(包含 0 和 n - 1)。图中的边用一个二维整数数组 edges 表示,其中 edges[i] = [ui, vi] 表示顶点 ui 和顶点 vi 之间的双向边。 每个顶点对由 最多一条 边连接,并且没有顶点存在与自身相连的边。

请你确定是否存在从顶点 source 开始,到顶点 destination 结束的 有效路径 。

给你数组 edges 和整数 n、source 和 destination,如果从 source 到 destination 存在 有效路径 ,则返回 true,否则返回 false 。

示例 1:

在这里插入图片描述

输入:n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2
输出:true
解释:存在由顶点 0 到顶点 2 的路径:

  • 0 → 1 → 2
  • 0 → 2

思路和代码

就判断source和destination在不在一个连通区域里嘛。

class Unionfind:
    def __init__(self, n):
        self.father = [i for i in range(n)]
        self.rank = [0] * n
    
    def find(self, x):
        if x == self.father[x]:
            return x
        self.father[x] = self.find(self.father[x])
        return self.father[x]
    
    def union(self,x,y):
        root_x, root_y = self.find(x), self.find(y)
        if root_x != root_y:
            if self.rank[root_x] < self.rank[root_y]:
                self.father[root_x] = root_y
            else:
                self.father[root_y] = root_x
                if self.rank[root_x] == self.rank[root_y]:
                    self.rank[root_x] += 1
    
    def connected(self,x,y):
        return self.find(x) == self.find(y)

            
class Solution:
    def validPath(self, n: int, edges: List[List[int]], source: int, destination: int) -> bool:
        uf = Unionfind(n)
        for node1, node2 in edges:
            uf.union(node1,node2)
        return uf.connected(source, destination)

547. 省份数量

题目
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。
在这里插入图片描述

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2

思路和代码

直接套模板。

class Unionfind:
    def __init__(self, n):
        self.father = [i for i in range(n)]
        self.rank = [0] * n
        self.cnt = n
    
    def find(self, x):
        if x == self.father[x]:
            return x
        self.father[x] = self.find(self.father[x])
        return self.father[x]
    
    def union(self,x,y):
        root_x, root_y = self.find(x), self.find(y)
        if root_x != root_y:
            if self.rank[root_x] < self.rank[root_y]:
                self.father[root_x] = root_y
            else:
                self.father[root_y] = root_x
                if self.rank[root_x] == self.rank[root_y]:
                    self.rank[root_x] += 1
            self.cnt -= 1

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n = len(isConnected)
        uf = Unionfind(n)
        for node1 in range(n):
            for node2 in range(n):
                if isConnected[node1][node2]==1:
                    uf.union(node1,node2)
        return uf.cnt

684. 冗余连接

题目
树可以看成是一个连通且 无环 的 无向 图。

给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。

请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。

示例 1:

在这里插入图片描述
输入: edges = [[1,2], [1,3], [2,3]]
输出: [2,3]

思路和代码

判断图里是否有环,就是在union两个节点之前先判断是否已经连通,如果是的话则说明存在环。

代码:

class Unionfind:
    def __init__(self,n):
        self.father = [i for i in range(n+1)]
        self.rank = [0]*(n+1)
    
    def find(self,x):
        if x == self.father[x]:
            return x
        self.father[x] = self.find(self.father[x])
        return self.father[x]
    
    def union(self,x,y):
        x,y = self.find(x), self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                self.father[x] = y
            else:
                self.father[y] = x
                if self.rank[x] == self.rank[y]:
                    self.rank[x] += 1
    
    def connected(self, x, y):
        return self.find(x) == self.find(y)

class Solution:
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        n = len(edges)
        uf = Unionfind(n)
        for node1, node2 in edges:
            if uf.connected(node1,node2):
                return [node1,node2]
            uf.union(node1,node2)

参考文献:
1.https://github.com/azl397985856/leetcode/blob/master/thinkings/union-find.md(这个写的很好)
2.leetcode官网
3.https://blog.csdn.net/wait_nothing_alone/article/details/79254879
【算法】并查集—带路径压缩的按秩合并法

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

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

相关文章

动作捕捉技术应用于地面移动机器人协同

《一千零一夜》故事集中收录的《阿拉丁神灯》深受读者们的喜爱&#xff0c;其中阿拉丁拥有一块神奇的魔毯&#xff0c;它具有运载功能&#xff0c;可以将物体轻松便捷的从一个地方转移到另一个地方。在现实生活中&#xff0c;可变形布作为一种轻量便携且具有良好适应性的载体&a…

redhat9中mysql常用命令(持续更新)

目录 1、查看当前用户 2、查看选择的数据库 3、创建数据库 4、创建数据表 5、插入数据 6、查看表所以字段的内容 7、查看数据库当前密码策略 8、查看密码插件 9、更改密码策略为LOW&#xff0c;改为LOW或0 10、 更改密码长度 11、设置大小写、数字和特殊字符均不要求 …

微服务系列专栏介绍

文章目录一 专栏介绍1.1 微服务行业背景不同行业IT系统更新频率IT系统存在的问题微服务架构在企业中应用情况1.2 什么是微服务1.3 微服务的特点1.4 微服务诞生背景1.5 微服务架构的优势二 专栏目标三 专栏涉及技术四 专栏架构1.微服务架构&#xff1a;2.Go语言3.go-micro架构4.…

入侵无线WiFi的主要方式及防护要点

从攻击形态上看&#xff0c;无线网络攻击主要可以分为三个大类&#xff1a;被动型攻击、主动型攻击以及针对网络组件的攻击&#xff1a; 被动攻击一般发生在攻击者处于无线网络范围内并可以监视无线通信内容时&#xff0c;最常见的被动攻击是数据包嗅探。由于被动攻击者只是监…

HTTPS 的通信加解密过程,证书为什么更安全?

经典面试题 HTTPS 的通信加解密过程&#xff0c;证书为什么更安全&#xff1f; 考察点 《计算机网络》相关知识 了解 HTTPS 协议加解密的过程 了解数字证书认证的过程 技术点 对称加密和非对称加密 HTTPS 协议的加解密过程 数字证书认证过程 对称加密和非对称加密 对称加…

Mysql 小Tips

Mysql 小Tips 目录Mysql 小Tips1.group_concat2.char_length3.locate4.replace5.now6.insert into ... select7.insert into ... ignore8.select ... for update9.on duplicate key update10.show create table11.create table ... select12.explain13.show processlist14.mysq…

企业电子招投标采购系统源码之首页设计

​ 功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

字符设备驱动(二)

1. IO模型 &#xff08;1&#xff09;阻塞式IO&#xff1a;不能操作就睡觉 &#xff08;2&#xff09;非阻塞式IO&#xff1a;不能操作就返回错误 &#xff08;3&#xff09;IO复用 &#xff08;4&#xff09;信号驱动式IO &#xff08;5&#xff09;异步IO 2.阻塞与…

c++算法基础必刷题目——按位贪心

文章目录按位贪心1、毒瘤xor2、兔子的区间密码3、起床困难综合症按位贪心 1、毒瘤xor NC18979 毒瘤xor 题目描述 小a有N个数a1, a2, …, aN&#xff0c;给出q个询问&#xff0c;每次询问给出区间[L, R]&#xff0c;现在请你找到一个数X&#xff0c;使得 1、0⩽X<231 2、∑…

Vue(ref和$refs属性介绍与使用)

一、 Vue(ref和$refs属性介绍与使用) 在Vue中一般很少会用到直接操作DOM&#xff0c;但不可避免有时候需要用到&#xff0c;这时我们可以通过ref和$refs这两个来实现&#xff0c;本文我们就来详细的介绍下这个内容 除了自定义属性外&#xff0c;Vue实例还暴露一些有用的实例属性…

Educational Codeforces Round 140 (Rated for Div. 2)

A. Cut the Triangle 题目链接&#xff1a;Problem - A - Codeforces 样例输入&#xff1a; 44 7 6 8 3 54 5 4 7 6 85 8 1 8 2 53 6 6 6 6 3 样例输出&#xff1a; YES YES YES NO题意&#xff1a;在二维平面上给定一个非退化三角形的三个顶点&#xff0c;问我们能不能用…

NVIDA CUDA和cuDNN安装教程

文章目录一. 查看自己的GPU版本是否支持cuda二 .安装CUDA三. 安装cuDNN一. 查看自己的GPU版本是否支持cuda 打开显卡的控制面板&#xff0c;查看显卡是否支持cuda 二 .安装CUDA 去官网查看cuda版本与所需系统固件的对应关系 从官网下载所需的CUDA Toolkit Archive,这里下载…

04-Css+Nginx

前端入门——CSS 1 CSS入门 1.1 初识CSS 1.1.1 概述 在学习了基本HTML标签和样式后&#xff0c;接下来学习前端开发的第二门技术。 我们发现&#xff0c;虽然标签的属性可以调整一些样式&#xff0c;但是效果不够理想&#xff0c;而我们更愿意把样式编写在<style> 标…

CSS 实现七彩圆环loading动画

前言 &#x1f44f;CSS 实现七彩圆环loading动画&#xff0c;速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现步骤 定义父容器宽度为–w&#xff0c;每个圆环之间的gap间距为–gap&#xff0c;圆环的border边框宽为–border&am…

7个从Windows计算机上恢复Word文档的方法

你是否有受到Windows上损坏或丢失的Word文件的困扰&#xff1f;好吧&#xff0c;你并不孤单&#xff01; 文件损坏是 Word 文档的常见问题。此外&#xff0c;人们不小心删除了文件或忘记保存最新版本。 由于Word文件通常包含您在个人或职业生涯中所需的重要数据&#xff0c;因…

JavaWeb:Maven创建Web项目

1.1 Web项目结构 Web项目的结构分为&#xff1a;开发中的项目和开发完可以部署的Web项目&#xff0c;这两种项目的结构是不一样的&#xff0c;我们一个个来介绍下&#xff1a; Maven Web项目结构&#xff1a;开发中的项目 开发完成部署的Web项目 开发项目通过执行Maven打包…

python为你画虎迎新年,结束旧年迎新景

前言 2022年是农历壬寅虎年&#xff0c;虎作为十二生肖是家喻户晓的 据学者考证&#xff0c;其应当起源于楚文化中对虎的图腾崇拜&#xff0c;象征力量与威严。 虎者&#xff0c;百兽之王&#xff0c;是威猛的象征&#xff0c;为镇宅辟邪之灵物。 虎在民间习俗中被尊崇为瑞兽…

高斯分布下的线性判别函数简介

协方差矩阵的性质 实对称矩阵&#xff08;第iii元素和第jjj元素的耦合与第jjj元素和第iii元素的耦合相等&#xff09;Eigenvalues & eigenvecters (本征值, 本征向量) ΣϕiλiϕiΦ[ϕ1ϕ2⋯ϕd]Λdiag⁡[λ1,λ2,⋯,λd]\Sigma \phi_i\lambda_i \phi_i \quad \Phi\left[…

oracle安装完成后,只能在本地链接

Oracle搭建好了之后&#xff0c;发现只能在服务器本机链接oracle&#xff0c;其他客户端无法链接&#xff0c;这是因为监听地址是本地localhost&#xff0c;重建监听即可 lsnrctl status 查看监听的名称 lsnrctl stop 停止监听&#xff0c;并关闭数据库 sqlplus / as sysdba…

基于人工神经网络的空压机负荷预测(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…