并查集-应用方向以及衍生汇总+代码实现(c++)-学习一个数据结构就会做三类大题!

news2025/1/5 10:07:31

并查集的核心功能,合并集合,查找元素,这两个最基本的功能相关题目本文不列举了,主要是一些和图相关的:

并查集的核心母题

  • 一、连通性检测:
    • 问题:判断在一个图中,任意两点是否连通。
    • 应用:这是并查集最基本的功能,通过 find 操作可以判断两个节点是否属于同一个连通分量。
    • 衍生问题:
      • 判断两点是否在同一个连通分量中。
      • 找出图中有多少个连通分量(即独立的子图)。
      • 在图中动态地添加边,并检查连通性。
无向图,任意两点是否连通 问题代码

(注释中输入输出很清晰了)

  • 关键!如果在一个连通分量中~根节点会是一样的 ~find值相同
#include<iostream>
using namespace std;

const int MAXN = 100010;
int fa[MAXN];  // 并查集的父节点数组

// 初始化,每个节点都是自己的父节点
void init(int n) {
    for(int i = 1; i <= n; i++)
        fa[i] = i;
}

// 查找根节点,并进行路径压缩
int find(int x) {
    if (fa[x] != x) {
        fa[x] = find(fa[x]);  
    }
    return fa[x];
}


// 合并两个节点所在的集合
void join(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    if(fx != fy)
        fa[fx] = fy;  // 将其中一个根节点指向另一个
}

// 判断两点是否连通

bool isConnected(int x, int y) {
    return find(x) == find(y);
}

int main() {
    int n, m;
    cin >> n >> m;  // n为节点数,m为边数

    init(n);  // 初始化并查集

    // 读取每条边并进行合并
    for(int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        join(u, v);
    }

    // 查询两点是否连通
    int a, b;
    cin >> a >> b;
    if(isConnected(a, b))
        cout << "Yes, they are connected." << endl;
    else
        cout << "No, they are not connected." << endl;

    return 0;
}

  • 二、连通分量的管理:

    • 问题:管理图中的多个连通分量,支持动态的合并与查询操作。
    • 应用:这是并查集的核心应用,使用 union 操作可以将两个连通分量合并,通过 find 操作查询某个节点所属的连通分量。
    • 衍生问题:
      • 求解有多少个连通分量。
      • 在合并过程中检测图中是否形成了环。
      • 处理动态连通性问题(如动态添加或删除边)。
      • 判断图是否为树(只有一个连通分量且无环)。
求解连通分量个数 问题代码:
  • 关键:计算有多少个根节点就可以了,所以遍历所有的点,看有多少个点满足fa[i]==i;
#include<iostream>
using namespace std;

const int MAXN = 100010;
int fa[MAXN];  // 并查集的父节点数组

// 初始化,每个节点都是自己的父节点
void init(int n) {
    for(int i = 1; i <= n; i++)
        fa[i] = i;
}

// 查找根节点,并进行路径压缩
int find(int x) {
    if (fa[x] != x) {
        fa[x] = find(fa[x]);  
    }
    return fa[x];
}

// 合并两个节点所在的集合
void join(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    if(fx != fy)
        fa[fx] = fy;  // 将其中一个根节点指向另一个
}

int main() {
    int n, m;
    cin >> n >> m;  // n为节点数,m为边数

    init(n);  // 初始化并查集

    // 读取每条边并进行合并
    for(int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        join(u, v);
    }

    // 计算连通分量的数量
    int components = 0;
    for(int i = 1; i <= n; i++) {
        if(find(i) == i)  // 如果节点是它自己的父节点,说明它是一个连通分量的根
            components++;
    }

    cout << "Number of connected components: " << components << endl;

    return 0;
}

  • 三、环检测:
    • 问题:判断在无向图中是否存在环。
    • 应用:在合并过程中,如果发现两个节点已经在同一个连通分量中,那么在添加这条边之后会形成环。
    • 衍生问题:
      • 在无向图中检测是否有环。
      • 在最小生成树算法中(Kruskal),通过避免环的方式构造最小生成树。
      • 删除图中冗余的边以防止形成环(如冗余连接问题)。
是否有环 问题代码
  • 关键是在join函数中进行判断,正常的逻辑是找到根节点,如果不同就把一个的father指向另一个;
  • 而现在是如果相同就说明存在还,如果需要判断的时候就直接返回true就可以啦!
#include<iostream>
using namespace std;

const int MAXN = 100010;
int fa[MAXN];  // 并查集的父节点数组

// 初始化,每个节点都是自己的父节点
void init(int n) {
    for(int i = 1; i <= n; i++)
        fa[i] = i;
}

// 查找根节点,并进行路径压缩
int find(int x) {
    if (fa[x] != x) {
        fa[x] = find(fa[x]);  
    }
    return fa[x];
}

// 合并两个节点所在的集合

bool join(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    if(fx == fy)
        return true;  // 如果两个节点的根节点相同,说明形成了环
    fa[fx] = fy;
    return false;
}

int main() {
    int n, m;
    cin >> n >> m;  // n为节点数,m为边数

    init(n);  // 初始化并查集

    bool hasCycle = false;

    // 读取每条边并进行合并
    for(int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        if(join(u, v)) {
            hasCycle = true;  // 一旦发现环,标记并停止后续检测
        }
    }

    // 输出是否存在环
    if(hasCycle)
        cout << "Graph contains a cycle." << endl;
    else
        cout << "Graph does not contain a cycle." << endl;

    return 0;
}

例题:

Problem Description
上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。

在这里插入图片描述

Input
输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。
整个文件以两个-1结尾。

Output
对于输入的每一组数据,输出仅包括一行。如果该迷宫符合小希的思路,那么输出"Yes",否则输出"No"。

Sample Input
6 8 5 3 5 2 6 4
5 6 0 0

8 1 7 3 6 2 8 9 7 5
7 4 7 8 7 6 0 0

3 8 6 8 6 4
5 3 5 6 5 2 0 0

-1 -1

Sample Output
Yes
Yes
No

  • 分析:判断两个点,1.是不是只有一个连通分量(整体是一个连通图)2.是否有环
  • 因为在图中使用点不一定是0开始逐一往上,所以需要visit数组来辅助我们判断
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

int vis[1000010], fa[1000010];
int m, n, flag;

// 初始化每个节点的父节点为其自身
void init() {
    for(int i = 1; i < 1000010; i++)
        fa[i] = i;
}

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

// 合并两个节点所属的集合
void join(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    if(fx == fy)
        flag = 1;  // 出现环
    else
        fa[fx] = fy;
}

int main() {
    while(scanf("%d%d", &n, &m) != EOF) {
        if(m == 0 && n == 0) {
            printf("Yes\n");
            continue;  // 空树,自动满足条件
        }
        if(m == -1 && n == -1) break;  // 结束输入

        memset(vis, 0, sizeof(vis));  // 重置访问数组
        init();
        flag = 0;
        vis[n] = vis[m] = 1;  // 标记访问过的节点
        join(n, m);

        // 继续读入其他边
        //首先执行 scanf,然后判断 a | b 的值,如果 a 或 b 不为 0,则进入循环。
        while(scanf("%d%d", &n, &m), n | m) {
            vis[n] = vis[m] = 1;
            join(m, n);
        }

        int s = 0;
        // 检查连通分量的数量
        for(int i = 1; i < 1000010; i++) {
            if(fa[i] == i && vis[i])
                s++;
            if(s > 1) {  // 超过一个连通分量,不是树
                flag = 1;
                break;
            }
        }

        // 输出结果
        printf(flag ? "No\n" : "Yes\n");
    }
    return 0;
}

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

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

相关文章

《Python爬虫逆向实战》绕过debugger的方法汇总

禁用断点 打开控制台&#xff0c;点击右边的禁用断点按钮。 点击之后再刷新下&#xff0c;就会发现debugger失效了。 注&#xff1a;这种方法有个 弊端&#xff0c;就是我们在代码中下的断点也都将失效。 Add script to ignore list 在代码文件中任意位置右键&#xff0c;然…

SpringBoot读取resources下文件,不区分window和Linux系统

起因 每次发布读取项目下的文件总是要区分系统环境&#xff0c;烦人。 解决 log.info("读取excel开始");//获取文件路径ClassLoader classLoader getClass().getClassLoader();//获取文件流InputStream stream classLoader.getResourceAsStream("1.xlsx&qu…

PHP西陆多城市多门店多端健身系统小程序源码

&#x1f3cb;️‍♀️全国畅练无阻&#xff01;探索“多城市多门店多端健身系统”的无限可能&#x1f3c3;‍♂️ &#x1f30d; 开篇&#xff1a;跨越地域的健身自由 你是否曾因工作调动、旅行或是居住地变化而烦恼健身计划的中断&#xff1f;别担心&#xff0c;“多城市多…

数字化营销:以数据为笔,绘品牌与消费者的未来画卷

数据在数字化营销中的重要性简直超乎想象&#xff01;它能让企业精准洞察消费者的需求。就像电商平台&#xff0c;根据咱们的浏览和购买记录&#xff0c;就能推荐个性化的商品&#xff0c;是不是感觉特贴心&#xff0c;购买意愿也瞬间提高了&#xff1f;而且数据还能帮企业优化…

大数据-84 Spark 集群 RDD创建 RDD-Transformation操作算子 详解

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

CSS 伪类和伪元素

也是选择器的一种&#xff0c;被称为伪类和伪元素。这一类选择器的数量众多&#xff0c;通常用于很明确的目的。 伪类 什么是伪类 伪类是选择器的一种&#xff0c;它用于选择处于特定状态的元素。 比如当它们是这一类型的第一个元素时&#xff08;:first-child&#xff09;&…

Sentinel入门与进阶:微服务流量控制的最佳实践 ( 六 )

8.Gateway 整合 Sentinel&#xff08;熔断、限流&#xff09; 8.1.引入依赖 在 Spring Cloud Alibaba 2.1.6之前的版本&#xff0c;引入的是 sentinel-spring-cloud-gateway-adapter 包&#xff0c;并且需要自己实现好多配置类&#xff0c;2.1.6 之后的版本内部已经帮我们实现…

php 在app中唤起微信app进行支付,并处理回调通知

<?phpnamespace app\api\controller;use think\facade\Db; use think\facade\Log;class Wxzf {

计算机网络之分组交换时延的计算

一.类型 分组交换的时延包括一下几种&#xff1a; 1.1发送时延 发送时延&#xff0c;也叫传输时延&#xff0c;结点将分组的所有比特推向链路所需要的时间&#xff0c;即从发送分组的第一个比特算起&#xff0c;到该分组的最后一个比特发送完为止。 发送时延 分组长度 / 发…

Web Image scr图片从后端API获取基本实现

因系统开发中需求&#xff0c;会有页面显示图片直接从后端获取后显示&#xff0c;代码如下&#xff1a; 后端&#xff1a; /*** 获取图片流* param response* param fileName*/RequestMapping(value"getImgStream",method RequestMethod.GET)public void getImgStr…

【JAVA】深入理解守护线程与非守护线程:概念、应用及示例

文章目录 介绍1. 线程的基础知识2. 守护线程与非守护线程2.1 什么是守护线程&#xff1f;特点&#xff1a; 2.2 什么是非守护线程&#xff1f;特点&#xff1a; 3. 为什么需要守护线程&#xff1f;示例&#xff1a;后台任务处理示例&#xff1a;日志记录 4. 非守护线程的应用场…

Scrapy 项目部署问题及解决方案

部署 Scrapy 项目时可能会遇到一些常见问题。以下是几个常见的部署问题及其解决方案&#xff1a; 1、依赖问题 问题&#xff1a;部署后爬虫运行失败&#xff0c;通常是由于缺少依赖库。 2、配置问题 问题&#xff1a;爬虫在部署环境中无法正常运行&#xff0c;可能是由于配…

stm32智能颜色送餐小车(openmv二维码识别+颜色识别+oled显示)

大家好啊&#xff0c;我是情谊&#xff0c;今天我们来介绍一下我最近设计的stm32产品&#xff0c;我们在今年七月份的时候参加了光电设计大赛&#xff0c;我们小队使用的就是stm32的智能送餐小车&#xff0c;虽然止步于省赛&#xff0c;但是还是一次成长的经验吧&#xff0c;那…

深度学习基础之反向传播算法

目录 原理与过程 1. 前向传播&#xff08;Forward Pass&#xff09; 2. 计算误差&#xff08;Error Calculation&#xff09; 3. 反向传播&#xff08;Backpropagation&#xff09; 4. 参数更新&#xff08;Parameter Update&#xff09; 应用与实例 总结 反向传播算法…

1秒出图,全球最快的开源Stable Diffusion出炉

前言 OneFlow 将 Stable Diffusion 的推理性能推向了一个全新的 SOTA。 第一辆汽车诞生之初&#xff0c;时速只有 16 公里&#xff0c;甚至不如马车跑得快&#xff0c;很长一段时间&#xff0c;汽车尴尬地像一种“很酷的玩具”。人工智能作图的出现也是如此。 AI 作图一开始的…

大数据面试SQL(八):求连续段的起始位置和结束位置

文章目录 求连续段的起始位置和结束位置 一、题目 二、分析 三、SQL实战 四、样例数据参考 求连续段的起始位置和结束位置 一、题目 有一张表t2_id记录了id&#xff0c;id不重复&#xff0c;但是会存在间断&#xff0c;求出连续段的起始位置和结束位置。 样例数据&…

两个若依系统,不能同时登录问题解决方案

原因&#xff1a; 问题根源在于两个独立的系统&#xff08;A系统与B系统&#xff09;共享了同一cookie键名来存储各自用户的认证令牌&#xff08;token&#xff09;。这种设计导致了以下情形&#xff1a; 当用户在A系统登录后&#xff0c;一个token被存储在cookie中&#xff0…

【LeetCode每日一题】——623.在二叉树中增加一行

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 广度优先遍历 二【题目难度】 中等 三【题目编号】 623.在二叉树中增加一行 四【题目描述】…

c语言学习,memset()函数分析

1&#xff1a;memset() 函数说明&#xff1a; 将字符c&#xff08;unsigned char&#xff09;复制到str字符串的前n个字符 2&#xff1a;函数原型&#xff1a; void * memset(void * str,int c,size_t n) 3&#xff1a;函数参数&#xff1a; 参数str要填充的指针,c 要设置的值…

2024下半年EI学术会议一览表

2024下半年将举办多个重要的EI学术会议&#xff0c;涵盖了从机器视觉、图像处理与影像技术到感知技术、绿色通信、计算机、大数据与人工智能等多个领域。 2024下半年EI学术会议一览表 第二届机器视觉、图像处理与影像技术国际会议&#xff08;MVIPIT 2024&#xff09;将于2024…