前言
图是一种挺复杂的数据结构,在此介绍一下图的遍历以及连通分量的求法:
一、连通分量是什么?
我们在次不做定义的介绍,用通俗的语言:
连通分量可以理解为:图可以分为 互相没有联系的几个部分:
如下图:
第一张图片里面节点全相通(从任意一个节点都有路径到其他节点),所以称其连通分量为1
第二张图里面显然可以分为两个部分,连通分量为2
二、图的相关操作
1. 存储的实现(邻接表)
我们利用以下结构进行存储图相关信息:
const int N = 10001;
vector<int> g[N];
2.读入数据
n代表节点的个数, m代表路径的条数;
a , b 接收的是两个相通的节点编号信息;
测试数据(6个节点,8条边,后面8行代表:边的端点信息(编号0到5)):
6 8
0 1
0 2
0 5
1 2
1 3
1 4
3 4
3 5
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 10001;
vector<int> g[N];
int main(){
int n , m;
cin >> n >> m;
memset(id , -1 , sizeof(id));
memset(visited , false , sizeof(visited));
for(int i = 0 ; i < m ; i++){
int a , b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
}
2.图的遍历(递归实现DFS)
在此我们使用深度优先(DFS)进行遍历(递归实现):
我们需要一个visisted[]数组来标识对应下标节点是否被访问过;
代码如下:
void dfs(int v){
visited[v] = true;//标注为已访问
//进行遍历此节点可以达到的节点(联通的)
for(int i = 0 ; i < g[v].size() ; i++){
if(!visited[g[v][i]]){//节点没被访问过
dfs(g[v][i]);//进行访问
}
}
}
3. 连通分量的求解
连通分量的求解很简单:
我们对所有的节点依次进行DFS访问,在对第一个节点DFS的时候,我们可以得到此节点可以达到的所有节点的信息,我们标记为 已访问 ;
如果一轮结束后 , 还有没被访问过的节点, 再次对其进行访问,并且连通分量+1即可:
代码如下:
for(int j = 0 ; j < n ; j++){//遍历所有节点
if(visited[j] == 0){//节点没被访问
dfs(j);//访问
count2++;//连通分量加一
}
}
4. 连通分量判断两点是否可达
我们用id[]数组来标注节点对应的连通分量的编号(相当于给此节点在哪一个联通的图做了标记)
最后只需要判断两个节点是否在一个连通图里即可
bool isConnected(int a , int b){
return id[a] == id[b];
}
#include<bits/stdc++.h>
using namespace std;
const int N = 10001;
vector<int> g[N];
int visited[N] = {0};
int id[N];
int count2 = 0;
void dfs(int v){
visited[v] = true;
id[v] = count2;//在一个连通图里的获得相同编号
for(int i = 0 ; i < g[v].size() ; i++){
if(!visited[g[v][i]]){
dfs(g[v][i]);
}
}
}
//判断两点是否连通
bool isConnected(int a , int b){
return id[a] == id[b];
}
int main(){
int n , m;
cin >> n >> m;
memset(id , -1 , sizeof(id));
memset(visited , false , sizeof(visited));
for(int i = 0 ; i < m ; i++){
int a , b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
for(int j = 0 ; j < n ; j++){
if(visited[j] == 0){
dfs(j);
count2++;
}
}
}