1. 程式人生 > >求無向圖的關節點演算法

求無向圖的關節點演算法

一、      

      求無向的圖的關節點的這個演算法是我覺得比較難理解的演算法之一,我覺得難並不是難在算

法本身,而是難在該演算法的遞迴的實現上,特別是在DFSArticul()遞迴退出以後才可以進行

low[]函式的計算,這點,如果是在自己獨立思考來進行程式設計的話,可能是很難想出來的,因

為計算low[]函式必須要深度優先生成樹建立之後在可以進行求解,即求解的順序實質是對

DFSTree進行後序遍歷,這是其一。其二,該演算法的核心思想是求三者中的最小,即dfn函

數,還是有困難的,當初,我自己的想法是先深度優先遍歷圖,建立相應DFS樹,同時記錄下

每個頂點的DFS序數,然後再後續遍歷這棵樹來求解low[]函式,如果是這樣的話,時間複雜

度就大多了,下面是我寫的程式碼,這個程式碼本質上並不是我自己編寫的,是參照嚴蔚敏教材上

程式碼寫的一個成員函式,但我覺得這個程式碼寫得確實是太精闢了,如果是我自己,是寫不出

來的,下面的程式碼我付上了詳細的註釋,大家參考,另外程式碼已經作了一定的改動,因為嚴蔚

敏教材上的程式碼僅僅針對鄰接表,而我經過修改後也可以針對鄰接矩陣,程式碼已經通過 測試了,

大家參考:

二、求解演算法

  利用深度優先搜尋便可以求的圖的關節點,本由此可判別圖是否重連通。

  從任一點出發深度優先遍歷得到優先生成樹,對於樹中任一頂點V而言,其孩子節點為鄰接點。由深度優先生成樹可得出兩類關節點的特性:

  (1)若生成樹的根有兩棵或兩棵以上的子樹,則此根頂點必為關節點。因為圖中不存在連線不同子樹頂點的邊,若刪除此節點,則樹便成為森林。

  (2)若生成樹中某個非葉子頂點V,其某棵子樹的根和子樹中的其他節點均沒有指向V的祖先的回邊,則V為關節點。因為刪去v,則其子樹和圖的其它部分被分割開來

  定義

  low[v] 設對連通圖G=(V,E)進行先深搜尋的先深編號為dnf[v],產生的先深生成樹為S=(V,T),B試回退邊之集。對每個頂點v,low[v]定義如下

  low[v]=Min{dfn[v],Min{low[w]|w是v的一個子女},Min{dfn[x]|(v,x)是一條回邊}}//dfn陣列記錄頂點的深度優先數

三、
/////////////////////////////////////////////////
//DFSArticul()公有成員函式
//遞迴:從v0出發,通過深度優先遍歷當前圖,
//查詢並輸出該深度優先生成樹上的所有關節點
//演算法描述概要:定義了dfn[]函式,存放結點的DFS遍歷
//序數,low[]函式存放通過,當前結點可以
/////////////////////////////////////////////////
template
int Graphlink::DFSArticul(int v0,int* dfn,int* low)
{
static int count=0; //得到DFS序數的計數器
count++;
int min=count; //當前訪問的結點v0的DFS序數
dfn[v0]=count; //得到當前訪問頂點的DFS序數
for(int ptr=getFirstNeighbor(v0)
;ptr!=-1 //遍歷結點v0所有的鄰接結點
;ptr=getNextNeighbor(v0,ptr))
{
    int w=ptr; //w是v0的鄰接結點,w也是v0的子結點
    if(dfn[w]==0) //如果v0的子結點w沒有被訪問過
     {
           DFSArticul( //遞迴:對以w為開始頂點的子圖進行遞迴求關節點
               w,dfn,low);
           if(low[w]<min] min=low[w]; //比v0能達到的更小
           if(low[w]>=dfn[v0])//如果子結點u所能到達的頂點的DFS序數還沒有v0大
                     cout< < <<"是一個關節點."<

      }
     else if(dfn[w] min=dfn[w]; //說明w已經在v0之前訪問過了是一條回邊
}
low[v0]=min; //得到當前結點v0的low[]函式值
return count; //返回當前遍歷過的頂點的個數
};
/////////////////////DFSArticul()公有成員函式結束

/////////////////////////////////////////////////
//FindArticul()公有成員函式
//呼叫DFSArticul()函式找出當前圖的
//所有的關節點,並顯示出來,思想:首先判斷根結點
//是否有多於一個子樹,如果是說明根也是關節點,然後
//以根的每棵子樹根結點為起點繼續找關節點
/////////////////////////////////////////////////
template
void Graphlink::FindArticul()
{
     int n=numVertices;
     int* dfn=new int[n]; //dfn[]函式的陣列
     int* low=new int[n]; //low[]函式的陣列
     for(int i=0;i dfn=1; //以0號頂點為遍歷的根結點
           int p=getFirstNeighbor(0); //獲取根結點0的第一個子結點
     int k=DFSArticul(p,dfn,low);//對第一棵子樹進行尋找,得到第一棵子樹頂點個數
     if(k!=n-1) //如果頂點個數不是總頂點數-1
        { //怎說明根結點是關節點
            cout<<0<<":"< <<"是一個關節點."< p=getNextNeighbor(0,p); //獲得子樹p的兄弟子樹
               while(p!=-1) //對其他的子樹進行再次的關節點尋找
                   {
                      if(dfn[p]==0) //如果p還沒有被訪問過,就從該頂點開始尋找
                           DFSArticul(p,dfn,low);
                      p=getNextNeighbor(0,p);
                    };
            };
};
////////////////////////////FindArticul()函式結束