图中连通块的个数:并查集
图的连通性问题
在地图上有若干城镇(点),已知所有有道路直接相连的城镇对。要解决整幅图的连通性问题。比如,随意给你两个点,让你判断它们是否连通;或者问你整幅图一共有几个连通块,也就是被分成了几个互相独立的块。
修路工程问题会问还需要修几条路才能将所有城镇连通起来,实质就是求有几个连通块。如果只有1个连通块,说明整幅图上的点都连起来了,不用再修路了;如果是2个连通块,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都连通了;如果是3个连通块,则只要再修2条路……
所以,若存在n个连通块,只要修n-1条路,就能把所有点连通。
实际给定的城镇有几百个,路有若干条,而且可能存在回路。 这时就要用到并查集。
PS:
其实求连通子图个数,除了并查集,用DFS和BFS也能够实现。
DFS:每次从某一点出发,遍历完与它相连的所有点,子图数num+1;当遍历完所有点后,num即为所求。
并查集
并查集的概念
并查集由两个函数(并、查函数)和一个整型数组(集)构成。
- 函数join是合并;
- 函数find是查找;
- 数组pre记录了每个点的前导点是什么;
并查集的局限及改进
单纯的并查集只能保证得到独立子图的个数,但是,(即使经过了路径压缩)它不能保证同属于一个独立子图的节点的前导节点都相同。
在这种情况下,如果还要判断两个节点是否属于同一个子图,还要再进行一次find(i)
操作。
换句话说,经过一次对每个节点的遍历的find()操作,可以保证同一子图中的节点的前导节点都相同。
并查集的实现
用一个有趣的方式解释并查集的实现:
江湖上的大侠有师父和徒弟、下属,构成了许多树结构。连通块的个数可以认为是门派的个数。
pre数组
pre[1000]
这个数组记录了每个大侠的上级是谁。
pre[15]=3
表示15号大侠的上级是3号大侠。如果一个人的上级就是他自己,那说明他就是掌门了,查找到此为止。也有孤家寡人自成一派的,比如欧阳锋,那么他的上级就是他自己。每个人都只认自己的上级。比如胡青牛只知道自己的上级是杨左使,而不认识张无忌。要想知道自己的掌门是谁,只能一级级查上去。
find函数和路径压缩
路径压缩算法是指在每次查询上级的同时,都进行优化处理,所以整个树的层数都会维持在比较低的水平上。
find函数以及压缩路径可以用一个递归函数简单实现:
int find(int a){ if(pre[a]!=a) pre[a]=find(pre[a]);//路径压缩,本结点更新为根结点的子结点 return pre[a];
}
join函数
再来看看join函数,就是在两个点之间连一条线,这样一来,原先它们所在的两个块的所有点就都连通了。
用pre[]数组,该如何实现连线呢? 很简单,将一个门派的掌门指定为另一个门派掌门的下属。
//让虚竹和周芷若做朋友
void join(int x,int y) {int fx=find(x),fy=find(y);//虚竹的老大是玄慈,周芷若的老大是灭绝//玄慈和灭绝显然不是同一个人//于是让前者成为后者的下属 if(fx!=fy) pre[fx]=fy;
}
完整代码实现
#include<iostream>
using namespace std; int pre[1050]; //保存节点的直接父节点//查找x的根节点
int find(int a){ if(pre[a]!=a) pre[a]=find(pre[a]);//路径压缩,本结点更新为根结点的子结点 return pre[a];
}
//连接两个连通块
void join(int x,int y) { int fx=Find(x),fy=Find(y); if(fx!=fy) pre[fy]=fx;
} int main() { int N,M,a,b,i,j,ans=0; while(scanf("%d%d",&N,&M) && N) {//初始化pre数组for(i=1;i<=N;i++) pre[i]=i; //根据连通情况,构建pre数组for(i=1;i<=M;i++) { scanf("%d%d",&a,&b); join(a,b);} for(i=1;i<=N;i++) if(pre[i]==i) ans++; //计算连通子图的个数anscout<<ans;return 0;
}
求小岛个数
给定一个由1和0组成的二维字符数组,1代表陆地,0代表水。问被水包围的连通陆地区域的个数。
这题可以用DFS的递归着色来解,也可以用并查集来做。
class Solution {
public:vector<int> pre;int count=0;int numIslands(vector<vector<char>>& grid) {if(grid.size()==0 || grid[0].size()==0)return 0;int row = grid.size();int col = grid[0].size();pre.resize(row*col+1,0);//对pre数组进行初始化for(int i=0;i<row;i++)for(int j=0;j<col;j++){int num = col*i+j;pre[num] = num;}//遍历图中的每个点for(int i=0;i<row;i++)for(int j=0;j<col;j++){if(grid[i][j]=='1'){int down=i+1,right=j+1;if(down<row && grid[down][j]=='1')join(col*i+j,col*down+j);if(right<col && grid[i][right]=='1')join(col*i+j,col*i+right);}}//再遍历一次,计算islands的个数int ans = 0;for(int i=0;i<row;i++)for(int j=0;j<col;j++){int num = col*i+j;if(pre[num] == num && grid[i][j]=='1')ans++;}return ans;}//并,将联通的点的pre设为同一个值void join(int x,int y){int fx=find(x);int fy=find(y);if(fx != fy)pre[fx] = fy;}//找到a的祖先,并且路径压缩int find(int a){if(pre[a] != a)pre[a] = find(pre[a]);return pre[a];}
};
图中连通块的个数:并查集相关推荐
- 白魔法师--图的连通块问题(牛客小白月赛25)
链接:题目链接 来源:牛客网 白魔法师 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld 题目描述 你 ...
- 2152 Balloons(两遍bfs求图的连通块)
2152 Balloons(两遍bfs求图的连通块) Problem Description Both Saya and Kudo like balloons. One day, they heard ...
- 统计图的连通块的个数的两种方法
@算法学习 两种方法 DFS遍历法 并查集法 1. DFS遍历计算连通块 先上代码: #include <stdio.h> #include <vector>using nam ...
- AtCoder Regular Contest 061 E - Snuke‘s Subway Trip(建图 + dijkstra最短路 / 0/1bfs / 并查集)
AtCoder Regular Contest 061 E - Snuke's Subway Trip problem 洛谷翻译 my idea 最近一直在做网络流,所以一读这题后,我就想到了最小费用 ...
- LeetCode 323. 无向图中连通分量的数目(并查集)
文章目录 1. 题目 2. 解题 1. 题目 给定编号从 0 到 n-1 的 n 个节点和一个无向边列表(每条边都是一对节点),请编写一个函数来计算无向图中连通分量的数目. 示例 1: 输入: n = ...
- 中石油训练赛 - 奎奎画画(思维+并查集+离线处理)
题目描述 "为你写诗,为你静止,为你做不可能的事",爱情是一种怪事,它让奎奎开始学习画画.奎奎认为一张画的艺术价值等于画上的白色联通块个数(当一个格子和它上下左右四个方向上的某个相 ...
- [重修数据结构0x03]并查集、堆、优先队列(2021.8.11)
前言 在做遍历的题目的时候,发现掌握一些特殊的数据结构和技巧有时对解决题目有着决定性的作用,不可不学.因此特地拿出来两天学习一下并查集.堆.优先队列.以后有更多思考和感悟再加补充吧.内容来自算法笔记, ...
- 【牛客 - 368D】动态连通块(并查集+bitset优化)
题干: 小T有n个点,每个点可能是黑色的,可能是白色的. 小T对这张图的定义了白连通块和黑连通块: 白连通块:图中一个点集V,若满足所有点都是白点,并且V中任意两点都可以只经过V中的点互相到达,则称V ...
- 证明kruskal算法求解图的最小生成树具有贪心选择性质_将并查集应用在图论中的最小生成树算法——Kruskal...
点击上方蓝字,和我一起学技术. 今天是算法和数据结构专题的第19篇文章,我们一起来看看最小生成树. 我们先不讲算法的原理,也不讲一些七七八八的概念,因为对于初学者来说,看到这些术语和概念往往会很头疼. ...
最新文章
- python ftp 上传文档出现 553 Could not create file
- 北大教授最短毕业致辞。#人性的可爱 #饶毅#自我尊重
- DVWA-SQL注入(SQL Injection)低/中/高级别
- BZOJ1345 [Baltic2007]序列问题Sequence
- ubuntu sun-java-jdk(zhuan)
- uni-app开发规范
- 网易2018校招机器学习算法工程师笔试卷
- Redis发布订阅[西橙先生]
- WebRTC系列补充--native音量控制level
- html5 spice 虚拟桌面,开源桌面虚拟化spice体验
- 聊一个自己写的MVC框架
- pyqt5:利用QFileDialog从本地选择图片\文本文档显示到label、保存图片\label文本到本地(附代码)
- Tesseract-Ocr图片内容识别
- Houdini图文笔记:VEX知识点小结(一)
- 一、Mahony姿态解算——坐标系变换
- 服务器硬盘接口有哪些种类
- IDEA 集成git
- 计算机信息技术对医院医疗服务工作的影响,计算机在医院信息管理工作中应用探究.doc...
- 实验 详解LNMP应用部署Discuz
- oracle expense po,ORACLE ERP中PO/INV/AP/GL流程对应那些关键基表、接口表?
热门文章
- stm32开发3D打印机(二)——方向、相关资料链接
- 历数金融危机 摘自http://www.ftchinese.com/sc/index.jsp
- xp安全模式下如何修复计算机,xp系统电脑安全模式进不去的处理方法
- 读书印记 - 《大学潜规则:谁能优先进入美国顶尖大学》
- ES的ik分词器ik_smart和ik_max_word区别
- TCP/UDP端口大全
- vi和vt的区别小窍门_十大vi技巧和窍门
- 挣值管理EVM(PV,EV,AC,SV,CV,SPI,CPI);
- Linux-如何查看进程和关闭进程
- 13 【操作mysql数据库】