1. 程式人生 > 其它 >PHP資料結構(十一) ——圖的連通性問題與最小生成樹演算法(2)

PHP資料結構(十一) ——圖的連通性問題與最小生成樹演算法(2)

PHP資料結構(十一)——圖的連通性問題與最小生成樹演算法(2)

(原創內容,轉載請註明來源,謝謝)

再次遇到微信公眾號限制字數3000字的問題。因此將Kruskal演算法放於本文中進行描述。本文接上一篇文章。

4、Kruskal演算法

1)該演算法的時間複雜度為O(eloge),e表示邊的數目,即該演算法的時間複雜度和頂點數目無關。該演算法適用於邊數較少的稀疏網。

2)演算法內容

假設N={V, {E}}是連通網,演算法初始狀態為包含圖中的所有的點,沒有邊的T=(V, {})開始,圖中的每一個頂點自成一個連通分量,重複執行以下操作:

在E中選一條代價最小的邊,如果此邊符合該邊依附在兩個不同的連通分量上的要求,則將此邊加入T,否則找代價第二小的邊。以此類推,直至T中所有頂點都落在同一個連通分量上位置。則TE包含n-1條邊,T=(V, {TE})是最小生成樹。

該演算法需要引入一個二維陣列,記錄任意兩個頂點之間的權值,如果兩個頂點沒有連線,則權值為無窮大。

5、總結

Prim演算法和Kruskal演算法,區別在於從頂點切入還是從邊切入。因此,當頂點較多但邊相對較少時,可以使用Kruskal演算法;反之,頂點較少而邊相對較多時,可以使用Prim演算法。兩個演算法都需要引入一個二維陣列,用於儲存任意兩點間的權值,當兩點沒有連線時,權值為無窮大,表示該點無法直接到達另一點。

6、編碼實現

PHP實現Prim演算法和Kruskal演算法的執行結果如下:

    //Kruskal演算法:以邊為依據生成最小生成樹
         publicfunction getKruskalResult(){
                   //進行陣列轉換,將siteWeigh轉成一維陣列'ij'=>weigh的形式
                   $arrKruskal= $this->changeArray($this->siteWeigh);
                   //對轉換後的陣列進行排序,由從小到大的序列進行排序,便於後面取權值小的邊
                   $arrKruskal= $this->sortArray($arrKruskal);
                   /*Array( [0] => Array ( [line] => 41 [weigh] => 5 ) [1] => Array ( [line]=> 10 [weigh] => 10 ) [2] => Array ( [line] => 31 [weigh] => 10)
                   [3]=> Array ( [line] => 43 [weigh] => 10 ) [4] => Array ( [line] =>20 [weigh] => 15 )
                   [5]=> Array ( [line] => 40 [weigh] => 20 ) [6] => Array ( [line] =>32 [weigh] => 25 ) )*/
                   $nodeNum= count($arrKruskal);//獲取節點數目
                   $resStack= array();//用於存放結果路徑,格式0=>ij,1=>jk
                   $nodeStack= array();//判斷新取的邊是否在同一個連通分量
                   //遍歷生成的陣列,因為已經排好序,所以從第一個開始遍歷
                   //如果遍歷到的邊,兩個節點都已經納入結果集,說明該邊是冗餘的邊
                   //則該邊不是最小生成樹,跳過,遍歷下一條
                   foreach($arrKruskalas $line){
                            $node1= $line['line'][0];//獲取兩個階段的編號
                            $node2= $line['line'][1];
                            if(in_array($node1,$nodeStack) && in_array($node2, $nodeStack)){
                                     continue;//判斷兩個節點都在結果集就跳過
                            }
                            array_push($resStack,$line);//邊進結果集
                            if(!in_array($node1,$nodeStack)){
                                     array_push($nodeStack,$node1);//邊的節點進結果集
                            }
                            if(!in_array($node2,$nodeStack)){
                                     array_push($nodeStack,$node2);
                            }                          
                   }       
                   //計算權值
                   $sum= 0;
                   foreach($resStackas $nodeNum => $node){
                            $sum+= $node['weigh'];
                   }
                   returnarray('resRoad' => $resStack, 'resSum' => $sum);
         }
        //提供給kruskal演算法的陣列轉換,將二維陣列轉換成一維陣列
         privatefunction changeArray($arrToChange){
                   $res= array();
                   $nodeNum= count($arrToChange);
                   for($i=0;$i<$nodeNum;$i++){
                            //因為陣列是三角對稱的,只需要遍歷半邊即可
                            //並且i==j即單個頂點的情況不需要遍歷
                            for($j=0;$j<$i;$j++){
                                     if(999== $arrToChange[$i][$j]){
                                               continue;//不存在的線不需要遍歷
                                     }
                                     //$i.$j表示邊ij
                                     $res[]= array('line' => $i.$j, 'weigh' => $arrToChange[$i][$j]);
                            }
                   }
                   return$res;
         }
    //提供給kruskal演算法的陣列排序,採用快速排序的思想
         privatefunction sortArray($arrToSort){
                   $res= array();
                   $nodeNum= count($arrToSort);//獲取節點總數
                   $firstNode= array_shift($arrToSort);//取第一個節點,用於快速排序 
                   $leftSmallArr= array();//存放比第一個節點小的
                   $rightBigArr= array();//存放比第一個節點大的
                   foreach($arrToSortas $node){
                            if($node['weigh']< $firstNode['weigh']){
                                     array_push($leftSmallArr,$node);//小於第一個節點的分到左邊陣列,否則去右邊陣列
                            }else{
                                     array_push($rightBigArr,$node);
                            }
                   }
                   $leftRes= array();
                   if(count($leftSmallArr)> 1){
                            $leftRes= $this->sortArray($leftSmallArr);//當陣列多個元素,呼叫函式再次排序
                   }elseif(1 == count($leftSmallArr)){
                            $leftRes= $leftSmallArr;//當陣列只有一個元素,則返回此元素
                   }else{
                   }
                   //同leftRes
                   $rightRes= array();
                   if(count($rightBigArr)>= 1){
                            $rightRes= $this->sortArray($rightBigArr);
                   }elseif(1 == count($rightBigArr)){
                            $rightRes= $rightBigArr;
                   }else{
                   }                          
                   returnarray_merge($leftRes, array($firstNode), $rightRes);
         }
$minTree = new MinTree();
$kruskalRes = $minTree->getKruskalResult();
echo '採用kruskal演算法,獲取的最小生成樹為:';
print_r($kruskalRes['resRoad']);
echo '<br />最終的權值和為:'.$primRes['resSum'].'<br/>';

題外話:兩種最小生成樹演算法,Prim以節點為切入點獲取最小生成樹,Kruskal以邊為切入點獲取最小生成樹。兩者實現方式較為不同,Prim演算法主要以棧的思想進行解決,因此實際編碼過程中進出棧的處理邏輯需要理清楚;Kruskal重在排序,當每條邊的長度排好時,其他問題迎刃而解。

——written by linhxx 2017.07.09

相關閱讀:

PHP資料結構(十一) ——圖的連通性問題與最小生成樹演算法(1)

PHP資料結構(十) ——有向無環圖與拓撲演算法

PHP資料結構(九) ——圖的定義、儲存與兩種方式遍歷

PHP資料結構(八) ——赫夫曼樹實現字串編解碼(實踐2)

PHP資料結構(八) ——赫夫曼樹實現字串編解碼(實踐1)

PHP資料結構(八) ——赫夫曼樹實現字串編解碼(理論)

PHP資料結構(七) ——串與實現KMP演算法

PHP資料結構(六) ——樹與二叉樹之概念及儲存結構

PHP資料結構(六) ——陣列的相乘、廣義表

PHP資料結構(五) ——陣列的壓縮與轉置

PHP資料結構(四) ——佇列

PHP資料結構(三)——運用棧實現括號匹配

PHP資料結構(二)——鏈式結構線性表

PHP資料結構(一)——順序結構線性表