算法 : 关节点问题(邻接表,图的遍历)

系列文章目录

本系列文章以《算法设计与分析》--C++语言描述 为准,对算法问题进行一一收录

 

 

前导:问题描述

问题 : 关节点问题

题目描述

对于一个给定的连通图G,采用深度优先搜索的方法,识别出G的所有关节点。

输入

第一行输入结点个数n和边的个数m,下面m行输入各边。

输出

关节点个数

样例输入

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

样例输出 3

一、基本算法介绍

策略:图的遍历,双连通分量

方法:深度优先生成树(DFS),然后判断是否是关节点。

关节点定义:无向图G=(V,E)中,可能存在某个(多个)结点a,删除a后,图G就不再是连通图,则a就是图G的关节点。

应用场景:例如:通讯网络中的关键结点(如果断开就没办法连接,通讯网络就中断了)

如图:通信网络中的结点

5efe327186ed4a4694749de87564900d.png

1就是关节点,删除1后图:

16cf8d08d5b044d99e6136d8dd9ad6be.png

 

  

二、算法设计

1.算法原理

给定无向连通图G=<V,E>,S=<V,T>是图G的一颗深度优先树,图中的结点a是关节点,当且仅当

(1)a是根,且a至少有两个孩子

如图:

759a0c6ac8334cd2b4e81b8c7aee02ad.png

 

(2)或者a不是根,且a的某子树上没有指向a祖宗的反向边

如图:(此图为非关节点)

3d2e035ad3844f1dae133741b5ea8ffe.png

  (3)最低深度优先数:引入low[u];就是在深度遍历的时候每个结点访问的自己最低的深度。

 

ea3b4c9a1f7c4b1d8d65b5d0cdbc54b3.png

dedca32cbba64d6183f74c8076f3a6ff.png

 

 

 

2.程序代码

 

1.邻接表建立代码如下:

#include <iostream>
using namespace std; 
enum ColorType{White,Gray,Black};
typedef struct ENode
{
 int adjvex;  
 ENode* nexArc;
}ENode;
int count=0;//根节点的孩子数
int t=0;//时间戳 
int d[100],low[100];//d是当前位置,low是能连接到的最早的祖先
int sum=0;//总个数
int pa[100];//parents数组
class Graph
{
 public:
 Graph( int n, int e);
 void DFS(int u,int p);//深度有限遍历 
 void Sum(int n); 
 private:
 ENode **adjlist;
 int vertexNum;
 int edgeNum; 
};
//无向图邻接表存储的构造函数
Graph::Graph(int n, int e){
 int i, j, k;
 ENode* s;
 vertexNum = n;
 edgeNum = e;
 adjlist=new ENode* [vertexNum];
 for (i = 0; i < vertexNum; i++) {	
  adjlist[i]= NULL;
 }
 for(i=0;i<n;i++)//初始化
        d[i]=-1;
   
 for(i=0;i<n;i++)
        pa[i]=-1;
        
 for (k = 0; k < edgeNum; k++) {
  cin >> i >> j; //输入边依附的两个顶点的编号
  s = new ENode;//生成一个边表结点s
  s->adjvex =j;
  s->nexArc = adjlist[i];//将结点s插入表头
  adjlist[i]= s;
  
  s = new ENode;//生成一个边表结点s
  s->adjvex =i;
  s->nexArc = adjlist[j];//将结点s插入表头
  adjlist[j]= s;
 }
}

 

2.深度优先搜索(构造深度优先数)代码如下

void Graph::DFS(int u,int p){ 
    if(p==-1)
        count++;
    ENode *w;
    low[u]=d[u]=t++;
    for(w=adjlist[u];w;w=w->nexArc){
        int v=w->adjvex;
        if(d[v]==-1){
            DFS(v,u);
            if(low[u]>low[v])
                low[u]=low[v];
        }
        else if(v!=p&&low[u]>d[v])
            low[u]=d[v];
        else if(v==p)
            pa[u]=v;
    }
}

 

3.计算关节点个数代码如下

void Graph::Sum(int n){
    int i,j;
    for(i=1;i<n;i++){
        for(j=1;j<n;j++){
            if(pa[j]==i){
                if(low[j]>=d[i]){
                    sum++;
                    break;
                }
            }
        }
    }
}

 

总结

整体代码如下:


#include <iostream>
using namespace std; 
enum ColorType{White,Gray,Black};
typedef struct ENode
{
 int adjvex;  
 ENode* nexArc;
}ENode;
int count=0;//根节点的孩子数
int t=0;//时间戳 
int d[100],low[100];
int sum=0;//总个数
int pa[100];//parents数组
class Graph
{
 public:
 Graph( int n, int e);
 void DFS(int u,int p);//深度有限遍历 
 void Sum(int n); 
 private:
 ENode **adjlist;
 int vertexNum;
 int edgeNum; 
};
  
//无向图邻接表存储的构造函数
Graph::Graph(int n, int e){
 int i, j, k;
 ENode* s;
 vertexNum = n;
 edgeNum = e;
 adjlist=new ENode* [vertexNum];
 for (i = 0; i < vertexNum; i++) {	
  adjlist[i]= NULL;
 }
 for(i=0;i<n;i++)//初始化
        d[i]=-1;
   
 for(i=0;i<n;i++)
        pa[i]=-1;
        
 for (k = 0; k < edgeNum; k++) {
  cin >> i >> j; //输入边依附的两个顶点的编号
  s = new ENode;//生成一个边表结点s
  s->adjvex =j;
  s->nexArc = adjlist[i];//将结点s插入表头
  adjlist[i]= s;
  
  s = new ENode;//生成一个边表结点s
  s->adjvex =i;
  s->nexArc = adjlist[j];//将结点s插入表头
  adjlist[j]= s;
 }
}

void Graph::DFS(int u,int p){ 
    if(p==-1)
        count++;
    ENode *w;
    low[u]=d[u]=t++;
    for(w=adjlist[u];w;w=w->nexArc){
        int v=w->adjvex;
        if(d[v]==-1){
            DFS(v,u);
            if(low[u]>low[v])
                low[u]=low[v];
        }
        else if(v!=p&&low[u]>d[v])
            low[u]=d[v];
        else if(v==p)
            pa[u]=v;
    }
}
void Graph::Sum(int n){
    int i,j;
    for(i=1;i<n;i++){
        for(j=1;j<n;j++){
            if(pa[j]==i){
                if(low[j]>=d[i]){
                    sum++;
                    break;
                }
            }
        }
    }
}
int main() {
 int m,e;
 cin>>m>>e;
 Graph graph(m,e);
 graph.DFS(0,-1);//深度优先搜索
 if(count>=2)//判断根节点是不是关节点
        sum++;
 graph.Sum(m);
 cout<<sum<<endl;
 return 0;
}

 

 

 

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值