首先 说明一下 概念问题:
关节点 :如果删除无向 图中的一个顶点,以及与顶点相关的边,把 图的 一个连通 分量 变成 两个 以上的 连通 分量。这样的顶点叫做关节点。
没有 关节点的 无向图,叫做 重连通图。重连通图中 任意 两个顶点 至少 存在 两条以上的 通路。
如果 删除 连通图上的 k个 节点,才能 破坏 他的连通性,那么 这个连通图的 连通度 为k。
下面的算法 是 求 连通图的 关节点,并没有 考虑 求图的 关节点,不过 要 改成 图的 关节点 也不难,只要 加 一个 for i = 0 ,...g.verNum即可.
求关节点,我所知道的 有两种方法:
1.定义法: 依次 删除 连通图的 的节点,然后深度优先 遍历 连通图,看 连通图 是否 连通。 假设 n个顶点,e条边,,时间复杂度 O(n * (n+e))
2.根据 关节点 的两种特性 来 深度优先遍历连通图。时间复杂度 O(n+e)
当然 第二种 方法 比较 好。
书上 给出了 关节点的 两种特性
下面的 代码 完全 是 根据 这两种特性 来 写的。
下面 给出 代码:
findArticul 函数 依据了 第一条 特性,判断 连通图的 深度优先生成树 的 孩子节点的个数,如果 超过 2,根节点 必是 关节点。
//图 必须 是 连通图. void findArticul(Graph g){ int visited[MAX_VERTEX_NUM] = {0};//访问节点的顺序 int count = 1; visited[0] = count;// 设置根节点访问过了. printf("图的 关节点有:"); //求根节点 是否 还有 第二颗子树. ArcNode * firstArc = g.list[0].head->nextArc; dfsArticul(g,firstArc->adjVex,&count,visited); //现在count = 根节点(1) + 第一颗子树(所有节点数) if (count < g.vexNum){//根节点有第二颗子树,继续遍历 剩下的子树,否则函数退出 //打印根关节点 printf("%c",g.list[0].vexName); //遍历 剩下的子树 ArcNode * next = firstArc->nextArc; while (next != NULL){ //根节点的 邻接节点 有可能 访问过了 int v = next->adjVex; if (visited[v] == 0){ dfsArticul(g,v,&count,visited); } } } printf("\n"); }dfsArticul 函数 根据 特性2 来判断 节点v 是否 是 关节点
//寻找 关节点.. //low(v) = min(visited(v),low(w),visited(k)),w 是k的孩子节点,k是v的祖先回边.. //条件: low(w) >= visited(v),这时v是 关节点,w及其子孙 均无指向v祖先的 无 回边. //函数返回值 low(v) int dfsArticul(Graph g,int v,int * count,int * visited){ int min = visited[v] = ++(*count);//设置 访问顺序 //跟书上 稍有不同,head->nextArc 是 v顶点的 第一个 边 ArcNode * next = g.list[v].head->nextArc; for (; next != NULL; next = next->nextArc) { int w = next->adjVex; if (visited[w] == 0){//未访问过 int low = dfsArticul(g,w,count,visited); if (min > low){ min = low; } if(low >= visited[v]){//w子树上 无回边 printf("%c",g.list[v].vexName); } } else if(min > visited[w]){//访问过,w 是v 节点的 祖先节点. min = visited[w]; } } return min; }求 上面的 连通图G5 的 关节点,截图 如下:
完整代码工程文件网盘地址:点击打开链接
原文地址:http://blog.csdn.net/fuming0210sc/article/details/45024013