1. 程式人生 > 其它 >PHP資料結構(六) ——樹與二叉樹之概念及儲存結構

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

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

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

一、樹的含義

1、樹為非線性結構,是n(n>=0)個節點的有限集,非空樹有一個根節點,n>1時有m(m>0)個互不相交的子樹。

2、樹的節點包含一個數據元素及若干指向其他節點的分支,節點擁有子樹的數目稱為樹的度,度為0的節點稱為葉子或終端節點,度不為0的節點稱為非終端節點或分支節點。樹的度為各節點度的最大值。

3、節點的子樹稱為節點的孩子,節點稱為孩子的雙親,同一個雙親的節點稱為兄弟,節點的祖先為從根到該節點所經分支上的所有節點。根的子樹中任一節點稱為子孫。

4、節點層次從根算起,根為第一層。雙親在同一層節點的互為堂兄弟。節點最大的層次稱為節點的深度。各子樹從左到右有秩序,則成為有序樹,否則為無序樹。

5、森林是m(m>=0)棵互不相交的樹的集合。對於樹的每個節點,其子樹的集合即為森林。

二、二叉樹

1、二叉樹是一種樹型結構,每個節點至多有兩個子樹,且有左右之分,次序不能顛倒,是有序樹。

2、根據二叉樹的定義,其有五種形態:空樹、僅有根的樹、有根及左節點、有根及右節點、有根及兩個節點。

3、二叉樹具有五個性質

1)在二叉樹的第i層,至多有2i-1個節點,i>=1

2)深度為k的二叉樹,至多有2k-1個節點,k>=1

3)對任一棵二叉樹,假設終端節點數目為a,度為2的節點數為b,則a=b+1

4)具有n個節點的完全二叉樹,深度為(log2n)+1,其中log2n的值向下取整

5)如果一棵有n個節點的完全二叉樹,對任意第i個節點(1<=i<=n)

A. 如果i=1,則節點i是二叉樹的根,無雙親;i>1,則雙親節點的序號為i/2的結果向下取整

B. 如果2i>n,則節點i無左孩子,否則左孩子的節點是2i

C. 當2i+1>n,則節點i無右孩子,否則右孩子的節點是2i+1

4、滿二叉樹概念:深度為k,且有2k-1個節點。

5、完全二叉樹概念:深度為k,有n個節點,當且僅當每個節點都與深度為k的滿二叉樹從1至n的編號一一對應。

6、二叉樹的儲存結構

1)順序結構:用一組連續的儲存空間儲存二叉樹,按照完全二叉樹的定義對每個節點進行儲存,不是完全二叉樹的則需要相應的位置留空。因此該方案僅適用於完全二叉樹,非完全二叉樹用該方式儲存會浪費大量儲存空間。

2)鏈式結構:二連結串列方式(資料域、左指標、右指標),三連結串列方式(二連結串列+父指標)。該方式儲存時,n個節點的二叉連結串列,有n+1個空鏈域。

三、線索二叉樹

1、定義:在鏈式二叉樹的基礎上進行改動。當鏈式二叉樹某個節點的左指標沒有指向時,其指向該節點的前驅,相對應的右指標指向後繼。

2、結構:為了區分指標是指向前驅/後繼還是指向子節點,需要增加兩個標誌域,分別表示其左/右節點指向子節點還是指向前驅/後繼。總體結構包含:左指標、左標誌域、數值、右標誌域、右指標。

四、遍歷二叉樹

1、定義:沿某路徑逐個訪問每個節點,使每個節點均被訪問正好一次。

2、三種遍歷方式

1)先序遍歷:先遍歷根節點,再遍歷左節點,最後遍歷右節點。

2)中序遍歷:先遍歷左節點,再遍歷根節點,最後遍歷右節點。

3)後序遍歷:先遍歷左節點,再遍歷右節點,最後遍歷根節點。

3、對二叉樹進行遍歷,本質是將非線性結構的二叉樹進行線性化,使每個節點至多一個前驅與一個後繼。

4、用PHP遍歷二叉樹

二叉樹結構如圖:

程式碼執行結果如圖:

原始碼如下:

<?php
//線索二叉樹
class Node{
         public$left = null;
         public$right = null;
         public$data = null;
}
///先序遍歷——根->左->右
function preSearch($root){
         $nodeStack= array();
         $resultDataStack= array();
         $resultNodeStack= array();
         array_push($nodeStack,$root);
         while(!empty($nodeStack)){
                   //array_pop為後進先出,是棧的資料結構
                   $node= array_pop($nodeStack);//每次while迴圈,相當於獲取一個子樹的根
                  array_push($resultDataStack,$node->data);
                   array_push($resultNodeStack,$node);
                   //先把右節點壓進棧,後壓左節點
                   //則下個迴圈的pop會先把左邊節點彈出,以達到先左後右的目的
                   if($node->right!= null){
                            array_push($nodeStack,$node->right);
                   }
                   if($node->left!= null){
                            array_push($nodeStack,$node->left);
                   }       
         }
         returnarray('data'=>$resultDataStack,'node'=>$resultNodeStack);
}
//中序遍歷——左->根->右
function centerSearch($root){
         $nodeStack= array();
         $resultDataStack= array();
         $resultNodeStack= array();
         $centerNode= $root;
         while(!empty($nodeStack)|| $centerNode!=null){
                   while($centerNode!= null){
                            //對於每個節點,先把其壓進棧,再不斷遍歷其左節點
                            //以達到從左節點開始遍歷的目的
                            array_push($nodeStack,$centerNode);
                            $centerNode= $centerNode->left;
                   }
                   //上面while彈出,相當於取到每個子樹的最左邊的子樹節點的下一個位置(null)
                   //由於其是null,表面該子樹沒有左節點,則此子樹為本次遍歷的最左節點
                   //因此需要回滾(pop)
                   $centerNode= array_pop($nodeStack);
                   array_push($resultDataStack,$centerNode->data);
                   array_push($resultNodeStack,$centerNode);
                   //取此根的右節點,並進入下次迴圈,繼續找該節點的最左節點
                   $centerNode= $centerNode->right;
         }
         returnarray('data'=>$resultDataStack,'node'=>$resultNodeStack);    
}
//後序遍歷——左->右->根
function backSearch($root){
         $nodeStack= array();
         $assistStack= array();//需要藉助輔助棧
         $resultDataStack= array();
         $resultNodeStack= array();
         array_push($nodeStack,$root);
         while(!empty($nodeStack)){
                   $node= array_pop($nodeStack);
                   //把彈出的node壓入輔助棧
                   array_push($assistStack,$node);
                   //先壓左邊後壓右邊,這樣下一輪迴圈的時候會先彈出右邊的node
                   //以便把右邊的node先壓入最終的輔助棧
                   if($node->left!= null){
                            array_push($nodeStack,$node->left);
                   }
                   if($node->right!= null){
                            array_push($nodeStack,$node->right);
                   }                
         }
         while(!empty($assistStack)){
                   $node= array_pop($assistStack);
                   array_push($resultDataStack,$node->data);
                   array_push($resultNodeStack,$node);          
         }
         returnarray('data'=>$resultDataStack,'node'=>$resultNodeStack);    
}
$a = new Node(); 
$b = new Node(); 
$c = new Node(); 
$d = new Node(); 
$e = new Node(); 
$f = new Node(); 
$a->data = '1'; 
$b->data = '2'; 
$c->data = '3'; 
$d->data = '4'; 
$e->data = '5'; 
$f->data = '6'; 
$a->left = $c; 
$a->right = $b; 
$b->left = $f; 
$c->left = $e; 
$c->right = $d; 
$res = preSearch($a);
echo '先序遍歷的結果是:';
print_r($res['data']);
echo '<br />';
$res = centerSearch($a);
echo '中序遍歷的結果是:';
print_r($res['data']);
echo '<br />';
$res = backSearch($a);
echo '後序遍歷的結果是:';
print_r($res['data']);
echo '<br />';

——written by linhxx 2017.06.29

相關閱讀:

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

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

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

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

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

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