php之二叉树,数据结构之二叉树——链式存储结构(php代码实现)
/**
*?ClearBiTree()?清空二叉樹(shù)
*?CreateBiTree()?創(chuàng)建二叉樹(shù)
*?BiTreeEmpty()?判斷二叉樹(shù)是否為空
*?BiTreeDepth()?返回二叉樹(shù)的深度
*?root()?返回二叉樹(shù)的根
*?Parent()?返回給定元素的雙親
*?LeftChild()?要返回左孩子的元素
*?RightChild()?要返回右孩子的元素
*?LeftSibling()?要返回左兄弟的元素
*?RightSibling()?要返回右兄弟的元素
*?Insert($data)?插入節(jié)點(diǎn)——遞歸算法
*?insert2($data)?插入節(jié)點(diǎn)——非遞歸算法
*?DeleteSubtree($elem,$LR)?刪除某個(gè)節(jié)點(diǎn)的左(右)子樹(shù)
*?PreOrderTraverse()?先序遍歷——遞歸算法
*?InOrderTraverse()?中序遍歷——遞歸算法
*?PostOrderTraverse()?后序遍歷——非遞歸算法
*?preOrderTraverse2()?先序遍歷——非遞歸算法
*?preOrderTraverse3()?先序遍歷——非遞歸算法
*?inOrderTraverse2()?中序遍歷——非遞歸算法
*?inOrderTraverse3()?中序遍歷——非遞歸算法
*?postOrderTraverse2()?后序遍歷——非遞歸算法
*/
class?BiNode{
public?$data;
public?$lchild;
public?$rchild;
public?function?__construct($data){
$this->data=$data;?//節(jié)點(diǎn)數(shù)據(jù)
$this->lchild=null;//左孩子的指針
$this->rchild=null;//右孩子的指針
}
}
class?LinkBiTree{
private??$root;?//二叉樹(shù)的根節(jié)點(diǎn)
private?static?$preArr;?//用于保存先序遍歷后的數(shù)據(jù)
private?static?$inArr;?//用于保存中序遍歷后的數(shù)據(jù)
private?static?$postArr;?//用于保存后序遍歷后的數(shù)據(jù)
private?static?$levelArr;?//用于保存后序遍歷后的數(shù)據(jù)
private?static?$count;?//記錄創(chuàng)建二叉樹(shù)結(jié)點(diǎn)的個(gè)數(shù)
const?MAX_LEVEL=2;//二叉樹(shù)最大的層數(shù)
public?static??$test;
public?function?__construct(){
$this->root=null;//指向根節(jié)點(diǎn),初始化時(shí)為空樹(shù)
self::$count=0;
}
/**
*?清空二叉樹(shù)
*/
public?function?ClearBiTree(){
$this->clearTree($this->root);
}
/**
*?@param?$root?表示樹(shù)的根節(jié)點(diǎn)
*/
private?function?clearTree($root){
if($root){
if($root->lchild){
$this->clearTree($root->lchild);?//清空左子樹(shù)
}
if($root->rchild){
$this->clearTree($root->rchild);?//清空右子樹(shù)
}
unset($root);?//釋放根節(jié)點(diǎn)
$root=null;
}
}
//先序遍歷
public?function?PreOrderTraverse(){
$this->preTraverse($this->root);
return?self::$preArr;
}
private?function?preTraverse($root){
if($root){
self::$preArr[]=$root->data;?//先訪問(wèn)根節(jié)點(diǎn)
$this->preTraverse($root->lchild);//再先序遍歷左子樹(shù)
$this->preTraverse($root->rchild);//最后先序遍歷右子樹(shù)
}
}
//中序遍歷
public?function??InOrderTraverse(){
$this->inTraverse($this->root);
return?self::$inArr;
}
private?function?inTraverse($root){
if($root){
$this->inTraverse($root->lchild);?//先中序遍歷左子樹(shù)
self::$inArr[]=$root->data;?//再訪問(wèn)根節(jié)點(diǎn)
$this->inTraverse($root->rchild);//最后中序遍歷右子樹(shù)
}
}
//后序遍歷
public?function?PostOrderTraverse(){
$this->postTraverse($this->root);
return?self::$postArr;
}
private?function?postTraverse($root){
if($root){
$this->postTraverse($root->lchild);?//先后序遍歷左子樹(shù)
$this->postTraverse($root->rchild);?//再后序遍歷右子樹(shù)
self::$postArr[]=$root->data;?//最后再訪問(wèn)根節(jié)點(diǎn)
}
}
//層序遍歷
public?function?LevelOrderTraverse(){
for($i=1;$i<=$this->BiTreeDepth();$i++){
$this->levelTraverse($this->root,$i);
}
return?self::$levelArr;
}
private?function?levelTraverse($root,$level){
if($root){
if($level==1){
self::$levelArr[]=$root->data;
}
$this->levelTraverse($root->lchild,$level-1);
$this->levelTraverse($root->rchild,$level-1);
}
}
//創(chuàng)建二叉樹(shù)
public?function?CreateBiTree(){
$this->createTree($this->root);
}
//此處使用先序輸入數(shù)據(jù)的方式來(lái)創(chuàng)建的
private?function?createTree(&$root){
$node=new?BiNode(mt_rand(1,20));
self::$count++;
if(self::$count<=pow(2,self::MAX_LEVEL)-1){
$root=$node;
self::$test[]=$root->data;
$this->createTree($root->lchild);
$this->createTree($root->rchild);
}
}
//判斷二叉樹(shù)是否為空
public?function?BiTreeEmpty(){
//????????if($this->root){
//????????????return?false;
//????????}else{
//????????????return?true;
//????????}
return?$this->root==null;
}
//返回二叉樹(shù)的深度
public?function?BiTreeDepth(){
return?$this->treeDepth($this->root);
}
private?function?treeDepth($root){
//求左子樹(shù)的深度
$arr=array();
$root=$this->root;
$level=0;
$num=0;
array_push($arr,$root);
while(count($arr)!=0){
$root=array_shift($arr);
$num++;
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild){
array_push($arr,$root->rchild);
}
}
while($num>pow(2,$level-1)-1){
$level++;
}
$level--;
return?$level;
}
//返回二叉樹(shù)的根
public?function?Root(){
return?$this->root==null???'Null':$this->root->data;
}
//返回給定元素的雙親
//此處分別使用php內(nèi)部的array_push()和array_shift()這兩個(gè)函數(shù)模擬隊(duì)列
/**
*?@param?$elem
*?@return?string
*?返回給定元素的雙親
*?思路:1.使用數(shù)組隊(duì)列來(lái)保存節(jié)點(diǎn)的指針
*???????2.將根節(jié)點(diǎn)從隊(duì)尾壓入數(shù)組隊(duì)列中
*???????3.然后取出隊(duì)首元素,使其左節(jié)點(diǎn)、右節(jié)點(diǎn)分別于給定的元素比較
*???????4.相等的就返回上一步中取出的隊(duì)首元素,否則,將此隊(duì)首元素的左右節(jié)點(diǎn)指針?lè)謩e壓入隊(duì)尾
*???????5.重復(fù)第3步
*/
public?function?Parent($elem){
if($this->root){
$arr=array();//此處數(shù)組是當(dāng)隊(duì)列來(lái)使用的,用于存放樹(shù)(包括子樹(shù))的根指針
array_push($arr,$this->root);
while(count($arr)!=0){
$root=array_shift($arr);
if($root->lchild?&&?$root->lchild->data==$elem?||
$root->rchild?&&?$root->rchild->data==$elem){
return?$root->data;
}else{
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild){
array_push($arr,$root->rchild);
}
}
}
}
return?false;
}
/**
*?@param?$elem?要返回左孩子的元素
*?@return?string
*?思路:同上
*/
public?function?LeftChild($elem){
if($this->root){
$arr=array();
array_push($arr,$this->root);
while(count($arr)!=0){
$root=array_shift($arr);
if($root->data==$elem?&&?$root->lchild){
return?$root->lchild->data;
}
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild)?{
array_push($arr,?$root->rchild);
}
}
}
return?false;
}
/**
*?@param?$elem?要返回左孩子的元素
*?@return?string
*?思路:同上
*/
public?function?RightChild($elem){
if($this->root){
$arr=array();
array_push($arr,$this->root);
while(count($arr)!=0){
$root=array_shift($arr);
if($root->data==$elem?&&?$root->rchild){
return?$root->rchild->data;
}
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild){
array_push($arr,$root->rchild);
}
}
}
return?false;
}
/**
*?@param?$elem?要返回左兄弟的元素
*?@return?string
*/
public?function?LeftSibling($elem){
$parent=$this->Parent($elem);
$leftChild=$this->LeftChild($parent);
$rightChild=$this->RightChild($elem);
if($rightChild==$elem?&&?$leftChild){
return?$leftChild;
}
return?'Error';
}
/**
*?@param?$elem?要返回右兄弟的元素
*?@return?string
*/
public?function?RightSibling($elem){
$parent=$this->Parent($elem);
$leftChild=$this->LeftChild($parent);
$rightChild=$this->RightChild($elem);
if($leftChild==$elem?&&?$rightChild){
return?$rightChild;
}
return?'Error';
}
/**
*?@param?$data?要插入的數(shù)據(jù)
*?思路:1.插入的數(shù)據(jù)比樹(shù)中的根(包括子樹(shù))節(jié)點(diǎn)小時(shí),就放在根節(jié)點(diǎn)的左子樹(shù)上;
*???????2.比根節(jié)點(diǎn)大時(shí),插入到右子樹(shù)上;
*??注意:因?yàn)椴迦氲奈恢貌皇侨~節(jié)點(diǎn)就是只有左(或右)子樹(shù)的節(jié)點(diǎn),所以可以得知此遞歸的出口肯定是某個(gè)節(jié)點(diǎn)的左(或右)子樹(shù)指針為空的時(shí)候。當(dāng)此節(jié)點(diǎn)的左(右)都不為空的時(shí)候,遞歸就會(huì)持續(xù)下去,直到為左(右)子樹(shù)有一邊或全部為空的節(jié)點(diǎn)出現(xiàn)為止。
*/
public?function?Insert($data){
$node?=?new?BiNode($data);
$this->insertNode($node,$this->root);
}
private?function?insertNode($node,&$root){
if(!$root){
$root=$node;
}else{
if($node->data?>?$root->data){
$this->insertNode($node,$root->rchild);
}else?if($node->data?data){
$this->insertNode($node,$root->lchild);
}else{
return;
}
}
}
//非遞歸算法實(shí)現(xiàn)插入節(jié)點(diǎn)操作
public?function?insert2($data){
$node=new?BiNode($data);
if(!$this->root){
$this->root=$node;
}else?{
$arr?=?array();
array_push($arr,?$this->root);
while?(count($arr)?!=?0)?{
$root?=?array_shift($arr);
//表示如果要插入的數(shù)據(jù)$node->data大于根節(jié)點(diǎn)的數(shù)據(jù)$root->data并且根節(jié)點(diǎn)的
//左子樹(shù)為空的話,那么就將$node->data賦值給左子樹(shù)
if?($node->data?data?&&?!$root->lchild)?{
$root->lchild?=?$node;
break;
//此處為大于,思路與小于相似
}else?if($node->data?>?$root->data?&&?!$root->rchild){
$root->rchild?=?$node;
break;
}
//以下兩個(gè)if語(yǔ)句,表示如果上面的兩個(gè)條件都不滿(mǎn)足的話,那么就將跟的左右節(jié)點(diǎn)分別要入隊(duì)列,繼續(xù)循環(huán)
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild){
array_push($arr,$root->rchild);
}
}
}
}
/**
*?@param?$elem?要?jiǎng)h除的那個(gè)節(jié)點(diǎn)的子樹(shù)
*?@param?$LR?表示是要?jiǎng)h除左子樹(shù)還是右子樹(shù)
*/
public?function?DeleteSubtree($elem,$LR){
if($this->root){
$arr=array();
array_push($arr,$this->root);
while(count($arr)!=0){
$root=array_shift($arr);
if($root->data==$elem?&&?$LR==0){
$root->lchild=null;
}
if($root->data==$elem?&&?$LR==1){
$root->rchild=null;
}
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild){
array_push($arr,$root->rchild);
}
}
}
}
/*
以下是先序,中序,后序,層序的非遞歸實(shí)現(xiàn)算法
除了層序遍歷使用了隊(duì)列外,其他的是利用棧來(lái)實(shí)現(xiàn)的
思路:?1.輸出當(dāng)前根節(jié)點(diǎn)
2.將當(dāng)前根節(jié)點(diǎn)的右孩子做壓棧處理
3.將當(dāng)前節(jié)點(diǎn)的右孩子作為新的根節(jié)點(diǎn),如果為空的話,將棧頂元素彈出作為新的根節(jié)點(diǎn)。
4.重復(fù)1,2,3
*/
public?function?preOrderTraverse2(){
$arr=array();
$root=$this->root;
$arrPre=array();
while($root?||?count($arr)!=0){
$arrPre[]=$root->data;
if($root->rchild){
$rootR=$this->rchild;
array_push($arr,$rootR);
}
$root=$root->lchild;
if(!$root){
$root=array_pop($arr);
}
}
return?$arrPre;
}
/*
*?思路:1.將根節(jié)點(diǎn)壓棧
*???????2.彈出棧頂元素作為新的根節(jié)點(diǎn)
*???????3.根據(jù)棧——先進(jìn)后出的特性,先進(jìn)根節(jié)點(diǎn)的右孩子做壓棧處理,再將其左孩子做壓棧處理;
*???????4.重復(fù)2,3
*?注:此算法與上面的算法基本思想是相同的,只是細(xì)節(jié)處理上有所不同。
*/
public?function?preOrderTraverse3(){
$arr=array();
$root=$this->root;
array_push($arr,$root);
while(count($arr)!=0){
$root=array_pop($arr);
$arrPre[]=$root->data;
if($root->rchild){
array_push($arr,$root->rchild);
}
if($root->lchild){
array_push($arr,$root->lchild);
}
}
return?$arrPre;
}
//中序遍歷算法2
/*
*?思路:1.將根節(jié)點(diǎn)壓棧
*???????2.將根節(jié)點(diǎn)的左孩子作為新的根節(jié)點(diǎn),對(duì)其進(jìn)行遍歷
*???????3.如果左子樹(shù)遍歷完畢,就將棧中的左子樹(shù)結(jié)點(diǎn)彈出并輸出,然后將此彈出結(jié)點(diǎn)的右孩子作為新的根節(jié)點(diǎn)。
*???????4.重復(fù)1,2,3
*??注:此處或許有人對(duì)while循環(huán)的判斷條件有所不理解。因?yàn)榧偃缯f(shuō)我們只用$root是否為空來(lái)作為判斷條件的話,那么當(dāng)我們遍歷完左子樹(shù)后,程序就結(jié)束了,顯然不是我們要的結(jié)果;假如我們只用棧$arr是否為空為判斷條件的話,那么循環(huán)根本無(wú)法進(jìn)行。
*
*/
public?function?inOrderTraverse2(){
$arr=array();
$root=$this->root;
while($root?||?count($arr)!=0){
if($root){
//根指針進(jìn)棧,遍歷左子樹(shù).此處之所以沒(méi)有在循環(huán)外先將整棵樹(shù)的根節(jié)點(diǎn)做壓棧處理,是因?yàn)?#xff0c;如果這樣做了,那么此處對(duì)左子樹(shù)的遍歷就會(huì)出現(xiàn)死循環(huán),因?yàn)檫@是的判斷條件就是$root->lchild,而不是$root了,倘若還是$root那么棧中就會(huì)有兩個(gè)根(整棵樹(shù))。
array_push($arr,$root);
$root=$root->lchild;
}else{
//根指針退棧,訪問(wèn)根節(jié)點(diǎn),遍歷右子樹(shù)
$root=array_pop($arr);
$arrIn[]=$root->data;
$root=$root->rchild;
}
}
return?$arrIn;
}
//中序遍歷算法3
/*
*?思路:1.先進(jìn)根做壓棧處理
*???????2.遍歷左子樹(shù)
*???????3.取出棧頂元素并將輸出的節(jié)點(diǎn)作為新的根節(jié)點(diǎn)
*???????4.將根節(jié)點(diǎn)的右孩子壓棧并重新作為新的根節(jié)點(diǎn)
*???????5.重復(fù)2,3,4
*?注:此算法和上面的算法的整體思想是一樣的
*/
public?function?inOrderTraverse3(){
$arr?=?array();
$root?=?$this->root;
array_push($arr,$root);
while?(count($arr)?!=?0)?{
while($root){
array_push($arr,$root->lchild);
$root=$root->lchild;
}
array_pop($arr);
if(count($arr)!=0){
$root=array_pop($arr);
$arrIn[]=$root->data;
array_push($arr,$root->rchild);
$root=$root->rchild;
}
}
return?$arrIn;
}
//因?yàn)橄刃蚴歉笥?#xff0c;而后序是左右根,如果將后序反轉(zhuǎn)180度的話,那么順序就是根右左.根據(jù)遞歸轉(zhuǎn)換為非遞歸(棧)的方法——如果一個(gè)函數(shù)內(nèi)有多于一個(gè)的遞歸調(diào)用那么此時(shí),棧的進(jìn)入順序應(yīng)該與遞歸調(diào)用的順序相反。因?yàn)闂5奶匦允窍冗M(jìn)后出。
//后序遍歷算法2
public?function?postOrderTraver2(){
$arr=array();
$root=$this->root;
array_push($arr,$root);
while(count($arr)!=0){
$root=array_pop($arr);
$arrPost[]=$root->data;
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild){
array_push($arr,$root->rchild);
}
}
return?array_reverse($arrPost);
}
//層級(jí)遍歷算法2
public?function?levelOrderTraverse2(){
$arr=array();
$root=$this->root;
array_push($arr,$root);
while(count($arr)!=0){
$root=array_shift($arr);
$arrLevel[]=$root->data;
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild){
array_push($arr,$root->rchild);
}
}
return?$arrLevel;
}
/*
*?遞歸轉(zhuǎn)非遞歸算法小結(jié):
*??1.如果函數(shù)體內(nèi)只有一個(gè)遞歸調(diào)用,那么直接使用棧或隊(duì)列等轉(zhuǎn)換即可;
*??2.如果有多個(gè)遞歸調(diào)用并且相鄰,比如先序和后序遍歷算法,那么轉(zhuǎn)為非遞歸算法時(shí),先后順序要倒轉(zhuǎn);
*??3.如果有多個(gè)遞歸但不相鄰,比如中序遍歷,那么就直接按照原先的順序依次轉(zhuǎn)換即可。但如果里面依然有部分相鄰,那么就按小結(jié)2操作。
*??4.轉(zhuǎn)換時(shí),我們應(yīng)該將哪些數(shù)據(jù)放入棧中呢。根據(jù)函數(shù)調(diào)用的原理,在調(diào)用一個(gè)函數(shù)時(shí),內(nèi)存中就會(huì)開(kāi)辟一個(gè)棧空間,里面保存了函數(shù)的實(shí)參,局部變量和函數(shù)調(diào)用時(shí)的返回地址等,而我們要放入棧中的就是實(shí)參和局部變量(此處的局部變量是指后序遞歸要用到的局部變量)。
*/
}
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的php之二叉树,数据结构之二叉树——链式存储结构(php代码实现)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: oracle ogg00423,【案例】
- 下一篇: 云虚机php.ini在,虚拟主机php.