求割点
求割点
割点:删除该点,图不是连通图
建立深度优先生成树(深度优先搜索从某点开始遍历图),即以某点出发遍历无向图的点,出发点为根结点,通过深度优先搜索,从一个点x到另外一个点y,则在树中点x是点y的父亲。
树中的定理:
1.该树是一棵有序树。点与点之间有遍历次序先后之说,先遍历的点的编号比后遍历的点的编号小。
2.若某点x不是根结点,则该点的祖先的孩子作为根结点的树(该树满足点x不是树中的结点)的所有结点与点x没有边相连。否则,若存在点y(编号比点y编号小的点没有与点x有边相连),则点x是点y的孩子(原来点y的编号小于点x的编号) 或者 点y是点x的孩子(原来点y的编号大于点x的编号),矛盾。
判断一个图是否有割点:
1.若根结点有两个及以上孩子,则根结点是割点。
证明:即根存在第二棵子树(该子树的根为树的根结点的第二个孩子)。前面已证明根结点(根结点的第二棵子树的所有结点的祖先)的编号最小的孩子作为根结点的树(该树满足根结点的第二棵子树的所有结点不是根的第一棵子树中的结点)的所有结点与根的第二棵子树的所有结点没有边。删去根结点后,根的第一个子树的点与根的第二棵子树的点不可互达,则根结点是割点。
2.若以点x为根结点的子树的所有结点与点x的父亲的祖先没有边相连,则点x的父亲(非根结点)为割点。
另外一种说法:判断一个点是否割点:对于非叶子结点x其某棵子树的根和子树的其它结点没有与x的祖先结点连接的边,则v为割点。这个说法用于程序编译中,则不会重复运算。
证明:前面已证明“若某点y不是根结点,则该点的祖先的孩子作为根结点的树(该树满足点y不是树中的结点)的所有结点与点y没有边相连”【点y指的是以点x为根结点的子树的所有结点】,再加上这里的“点x(非根结点)为根结点的子树的所有结点与点x的父亲的祖先没有边相连”,所以以点x为根结点的子树的所有结点与树中的其它点(除了点x的父亲)没有边相连。删除点x的父亲,那么以点x为根结点的子树的所有结点与点x的父亲也没有边相连,以点x为根结点的子树的所有结点与树中的其它点没有边相连,点x的父亲为割点。
若图遍历得到的深度优先生成树不满足以上两个条件,即需满足以下两个条件,则图没有割点。
1. 根结点只有一个孩子
2. 所有点x为根结点的子树的所有结点与点x的父亲的祖先有边相连。其中点x的父亲为非根结点。
证明:
1.树中的两个点有边相连代表两个点有边相连,树的任何两个结点可互达。由于根结点只有一个孩子,则删除根结点,剩下以根结点的唯一孩子作为根结点的树,树中的点仍然两两可达,则根结点不是割点。
2.设树所有的点的集合为T。删除点x的父亲,对于以点x为根结点的子树的所有结点,设点集合为A,仍然两两互达;对于树中所有的点除去以点x为根结点的子树的所有结点和点x的父亲y,设点集合为B,B=T-A-{y},仍然两两互达。因为所有以点x为根结点的子树的所有结点与点x的父亲的祖先有边相连,所以集合A中的某个点仍与集合B中的点的某个点有边相连,则删去点x的父亲,集合A中的点与集合B中的点互达。A中的点可互达,B中的点可互达,A中的点与B中的点可互达,即树中除了点x的父亲外的其它点仍然两两互达。所以点x的父亲(非根结点)不是割点。
3.所有的叶子结点都不是割点。删去其中一个叶子结点,仍然是树,树中的所有点两两互达。
由1,2,3得根结点,非根结点的内部结点(点是某个点的父亲,所以点不是叶子结点),叶子结点都不是割点,三者包含整个树,即图中的所有点,所以图中没有割点。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <malloc.h> 4 #include <stdbool.h> 5 #define maxn 1000 6 7 //前提是无向图中的连通图 8 //按照遍历顺序给点编号 9 ///对于生成树中非叶结点v其某棵子树的根和子树的其它结点没有与v的祖先结点连接的边,则v为割点 10 11 struct node 12 { 13 long d; 14 struct node *next; 15 }*info[maxn+1]; 16 long num[maxn+1],treepre[maxn+1],step; 17 bool vis[maxn+1],v; 18 19 long min(long a,long b) 20 { 21 if (a>b) 22 return b; 23 else 24 return a; 25 } 26 27 void dfs(long d) 28 { 29 struct node *p; 30 long pre; 31 bool vd; 32 vis[d]=true; 33 step++; 34 num[d]=step; 35 pre=step; 36 p=info[d]; 37 while (p) 38 { 39 if (vis[p->d]==false) 40 dfs(p->d); 41 //求与该点相邻其它相邻的点的编号的最小值 42 else 43 pre=min(pre,num[p->d]); 44 p=p->next; 45 } 46 47 vd=true; 48 treepre[d]=2000000000; 49 p=info[d]; 50 while (p) 51 { 52 //编号比其大且与其相连的点为该点的孩子 53 if (num[p->d]>num[d]) 54 { 55 //对于生成树中非叶结点v其某棵子树的根和子树的其它结点没有与v的祖先结点连接的边,则v为割点。 56 if (treepre[p->d]==num[d]) 57 vd=false; 58 else 59 treepre[d]=min(treepre[d],treepre[p->d]); 60 } 61 p=p->next; 62 } 63 if (vd==false) 64 { 65 v=false; 66 printf("%ld ",d); 67 } 68 //以该点为根结点的子树的所有结点与其它点相连的编号最小的点的编号 69 //若该点是叶子结点,则值为该点的编号 70 treepre[d]=min(treepre[d],pre); 71 } 72 73 int main() 74 { 75 long n,m,i,x,y; 76 bool vr; 77 struct node *p; 78 scanf("%ld%ld",&n,&m); 79 for (i=1;i<=n;i++) 80 info[i]=NULL; 81 for (i=1;i<=m;i++) 82 { 83 scanf("%ld%ld",&x,&y); 84 p=(struct node *) malloc (sizeof(struct node)); 85 p->d=y; 86 p->next=info[x]; 87 info[x]=p; 88 p=(struct node *) malloc (sizeof(struct node)); 89 p->d=x; 90 p->next=info[y]; 91 info[y]=p; 92 } 93 //从任意一个点开始遍历,这里起点为1 94 v=true; 95 for (i=1;i<=n;i++) 96 vis[i]=false; 97 step=1; 98 vis[1]=true; 99 num[1]=1; 100 treepre[1]=1; 101 printf("\n"); 102 dfs(info[1]->d); 103 104 p=info[1]->next; 105 vr=true; 106 while (p) 107 { 108 //若根结点有两个及以上孩子,则根结点是割点。 109 if (vis[p->d]==false) 110 { 111 v=false; 112 vr=false; 113 dfs(p->d); 114 } 115 p=p->next; 116 } 117 if (vr==false) 118 printf("1 "); 119 if (v==true) 120 printf("No CutVertex\n"); 121 else 122 printf("\n"); 123 return 0; 124 } 125 /* 126 5 6 127 2 1 128 3 1 129 4 1 130 5 1 131 2 3 132 4 5 133 */ 134 /* 135 7 8 136 1 2 137 1 3 138 2 3 139 3 4 140 4 5 141 4 6 142 4 7 143 6 7 144 */
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <malloc.h> 4 #include <stdbool.h> 5 #define maxn 1000 6 7 //前提是无向图中的连通图 8 //按照遍历顺序给点编号 9 ///若以点x为根结点的子树的所有结点与点x的父亲的祖先没有边相连,则点x的父亲(非根结点)为割点。 10 11 struct node 12 { 13 long d; 14 struct node *next; 15 }*info[maxn+1]; 16 long num[maxn+1],minpn[maxn+1],father[maxn+1],step; 17 bool vis[maxn+1],v,vg[maxn+1]; 18 19 long min(long a,long b) 20 { 21 if (a>b) 22 return b; 23 else 24 return a; 25 } 26 27 void dfs(long d) 28 { 29 struct node *p; 30 vis[d]=true; 31 step++; 32 num[d]=step; 33 minpn[d]=step; 34 p=info[d]; 35 while (p) 36 { 37 if (vis[p->d]==false) 38 { 39 father[p->d]=d; 40 dfs(p->d); 41 //子结点 42 minpn[d]=min(minpn[d],minpn[p->d]); 43 } 44 //非子结点 45 else 46 minpn[d]=min(minpn[d],minpn[p->d]); 47 p=p->next; 48 } 49 50 if (minpn[d]==num[father[d]]) 51 { 52 v=false; 53 vg[father[d]]=true; 54 } 55 } 56 57 58 int main() 59 { 60 long n,m,i,x,y; 61 struct node *p; 62 scanf("%ld%ld",&n,&m); 63 for (i=1;i<=n;i++) 64 info[i]=NULL; 65 for (i=1;i<=m;i++) 66 { 67 scanf("%ld%ld",&x,&y); 68 p=(struct node *) malloc (sizeof(struct node)); 69 p->d=y; 70 p->next=info[x]; 71 info[x]=p; 72 p=(struct node *) malloc (sizeof(struct node)); 73 p->d=x; 74 p->next=info[y]; 75 info[y]=p; 76 } 77 //从某一点开始遍历 78 v=true; 79 for (i=1;i<=n;i++) 80 { 81 vis[i]=false; 82 vg[i]=false; 83 } 84 step=1; 85 vis[1]=true; 86 father[info[1]->d]=1; 87 dfs(info[1]->d); 88 89 vg[1]=false; //!!! 判断方法的前提是xx的父亲是非根结点 90 p=info[1]->next; 91 while (p) 92 { 93 //若根结点有两个及以上孩子,则根结点是割点。 94 if (vis[p->d]==false) 95 { 96 vg[1]=true; 97 v=false; 98 dfs(p->d); 99 } 100 p=p->next; 101 } 102 if (v==true) 103 printf("\nNo CutVertex\n"); 104 else 105 { 106 printf("\n"); 107 for (i=1;i<=n;i++) 108 if (vg[i]==true) 109 printf("%ld ",i); 110 printf("\n"); 111 } 112 return 0; 113 } 114 /* 115 5 6 116 2 1 117 3 1 118 4 1 119 5 1 120 2 3 121 4 5 122 */ 123 /* 124 7 8 125 1 2 126 1 3 127 2 3 128 3 4 129 4 5 130 4 6 131 4 7 132 6 7 133 */