图的邻接表,关节点与重连通分量

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_39956356/article/details/80514672

写在前面:由于关节点是这次主角,所以把邻接表存储结构放在了后面,不过你是来学习图的邻接表,也不用担心,后面一段是图的邻接表详细讲解。自己都是把存储结构和其应用放在一起的。没办法,内容有点多,尽请见谅以下!

关节点与重连通分量

一:基本概念

1:关节点

在某图中,若删除顶点v以及v相关的边后,图的一个连通分量分割为两个或两个以上的连通分量,则称顶点V为该图的一个关节点。

2:重连通分量

一个没有关节点的连通图称为重连通图。在重连通图中,任意一对顶点之间至少存在两条路径,则再删去某个顶点即相关各边后也不破坏图的连通性。

3:连通度

若在图的连通图上删去k个节点才能破坏图的连通性,则称K为此图的连通度。显然,K越大,系统越稳定。

4:应用

关节点是一个很重要的概念,直接关系到整个系统的稳定性,比如:运输网络、通信网络、航空网等等。

二:关节点算法

1:关节点的特征

(1):如果在生成树中,根节点有两个或以上的孩子节点,那么根节点为关节点。 这里不防自己称为第一类关节点。
(2):在生成树中某个非叶子节点v,其某颗子树的根子树的其他结点均没有指向v的祖先的回边。这里不防自己称为第二类关节点。
这里解释一下(1),(2)两点的含义:
连通图G5的关节点是A、B、D、G
连通图G5
G5的关节点

2:实现思路
总体基础:DFS遍历图

第一类关节点:比较简单。DFS回来发现并没有遍历完所有结点,那么根节点必是关节点。
这里写图片描述

第二类关节点:比较复杂,这里拆分几个步骤。

1:这里需要全局变量count记录遍历的访问顺序,即visited[v],visited[v]是DFS进入的时候就有了。
2:low[w]是孩子结点的min,是DFS退出的时候才有的。有点像后序遍历出来的。
3:visited[k],回边(包括双亲),就是回边祖先的visited[v],应为这是visited[k]、low[w]都是倒着出来的。
4:low[w] >= visited[v],v必是第二类关节点,为什么是这样呢?因为 孩子 >= 双亲,不就是孩子没有回边,就是没有回边的原因,孩子low[w] 才会大于双亲visited[v]。(这里注意下:这里是大于等于,不仅仅是大于)

这里写图片描述
分析流程:
这里写图片描述

相关代码:

/*************************************************************************************
                                    深度优先寻找关节点
**  low(v) = min{DFSvisted[v], low[w], DFSvisted[k]} 从本身,孩子,双亲,回边选最小的
**  DFSvisted[v]:   深度优先各结点的访问次序
**  low[w]:     孩子
**  DFSvisted[w]:  双亲,回边
**************************************************************************************/
int DFSvisted[20];                                                  //这里定义最多有20个结点
int count = 1;                                                      //全局变量,对访问计数
void FindArticul(ALGraph &G)
{
    DFSvisted[0] = 1;                                               //设定邻接表的0号是生成树的根
    for (int i = 1; i < G.vexnum; i++)
    {
        DFSvisted[i] = 0;                                           //其余顶点都没有访问
    }

    ArxNode *p0 = G.vertices[0].firstarc;                           //选取节点0(A),作为DFS人口
    int v = p0->adjvex;
    DFSArticul(G, v);                                               //从v顶点出发深度优先查找关节点

    if (count < G.vexnum) {                                         //生成树上至少有两颗子树
        printf("Joint Point(Vertex): %c\n", G.vertices[0].data);    //输出v顶点是关节点
        while (p0->nextarc) {
            p0 = p0->nextarc;       v = p0->adjvex;
            if(DFSvisted[v] == 0)
                DFSArticul(G, v);                                   //从新的v顶点出发深度优先查找关节点
        }
    }
}


int low[20];                                                        //DFSArticul()是递归调用,low[20]定义在外
void DFSArticul(ALGraph &G, int v0)
{
    ArxNode *p;
    int w;
    int min = DFSvisted[v0] = ++count;                              //v0是第count访问的顶点
    for (p = G.vertices[v0].firstarc; p; p = p->nextarc)            //沿着一个单链表一个一个往下走,并检查
    {
        w = p->adjvex;                                              //w是v0的孩子
        if (DFSvisted[w] == 0)                                      //W没有访问,是v0的孩子
        {
            DFSArticul(G, w);                                       //返回前求得low[w]
            if (low[w] < min)
                min = low[w];                                       //孩子(w)与min比较
            if(low[w] >= DFSvisted[v0])
                printf("Joint Point: %c\n", G.vertices[v0].data);   //输出v0顶点是关节点
        }
        else if (DFSvisted[w] < min)                                //w已访问(回边),那么w以是v0的祖先
        {
            min = DFSvisted[w];                                     
        }
    }
    low[v0] = min;                                                  //保持low数组最小
}

二:主函数与输出

主函数

#include "stdafx.h"
#include "Joint.h"


int main()
{
    ALGraph G;
    printf("Please enter vexnum and arcnum: ");
    scanf("%d %d", &G.vexnum, &G.arcnum);                                               //输入结点数,弧数

    CreatALGraph(G);                                                                    //创建无向图的邻接表
    printf("\n无向图的邻接表输出:\n");
    PrintALGraph(G);                                                                    //输出无向图的邻接表
    printf("\n关节点输出:\n");
    FindArticul(G);                                                                     //关节点输出
    return 0;
}

2:输出
下面提供两组数据供大家测试用。(输出关节点竟多输出一次,自己猜测是邻接表的问题,因为处于同一层次的所有点,谁先访问依据输入的次序。所以之后也是沿着表走的)
图G5:
这里写图片描述
图G5输出:
连通图G5
图G6:
这里写图片描述
图G6输出:
这里写图片描述

图的邻接表

1:图的四种存储
十字链表———-有向图的十字链表存储;深度优先、广度优先遍历
https://blog.csdn.net/weixin_39956356/article/details/80371735
数组表示法——-图的数组表示法(邻接矩阵)与Prim算法
https://blog.csdn.net/weixin_39956356/article/details/80470091
邻接多重表
邻接表————下面就介绍邻接表及创建无(有)向图

在这里介绍图的邻接表

1:基本知识

(1):邻接表用到了两个结构体

一个是顶点数组,包括点的数据(data)和连接此起点的第一条边(firstarc)。
一个是边链表,包括连接此边的终点在数组中的位置(adjvex)和对应之前起点的下一条边(*nextarc)。
这里写图片描述
这里写图片描述
这里还是举个例子(无向图G2的,有向图更简单):
这里写图片描述
无向图G2的邻接表:
这里写图片描述

存储结构代码:

#define MAX_VERTEX_NUM  20
typedef char VertexType;

//一个顶点的单链表
typedef struct ArxNode {
    int             adjvex;                 //弧的位置
    struct ArxNode *nextarc;                //该弧的下一弧
}ArxNode;

//顶点数组
typedef struct VNode {
    VertexType  data;                       //顶点存放的数据(这里用char--A B C···)
    ArxNode     *firstarc;                  //指向第一条弧的指针
    int          locationOfData;            //把data数组位置记下来,方便知道位置找data
}VNode, AdjList[MAX_VERTEX_NUM];

//ALGraph的整体信息
typedef struct {
    AdjList vertices;
    int     vexnum, arcnum;                 //弧的顶点数和弧数
}ALGraph;

(2):邻接表即可以创建有向图,也可以创建无向图。

选取其中一部分就是有向图。无向图两个部分都要有。
这里写图片描述

(3):邻接表很好的解决了邻接矩阵占用空间较大的问题,当边和信息多的时候,邻接表优势更加明显。

2:创建无向图的邻接表

(1):输入各顶点的data,firstarc指向NULL
这里写图片描述
里面scanf的%c输入问题在下面文章会更加详细:
https://blog.csdn.net/weixin_39956356/article/details/80371735

(2):输入所有的弧(没有先后顺序)
这里写图片描述
(3):创建单链表
这里写图片描述
里面核心两句代码分析:
一定要知道这个链表是倒着建立的
这里写图片描述

3:输出

图G5:
这里写图片描述
图G5邻接表:
这里写图片描述
图G6:
这里写图片描述
图G6邻接表:
这里写图片描述

感谢与源代码

(1):感谢以下文章对我的帮助
https://blog.csdn.net/dwenxue/article/details/72831234
(2):源代码(VS2017)
链接: https://pan.baidu.com/s/1Y_L6axWEH1RFJpqWyjnQrQ 密码: np4t

猜你喜欢

转载自blog.csdn.net/weixin_39956356/article/details/80514672