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
相關閱讀: