日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > php >内容正文

php

php之二叉树,数据结构之二叉树——链式存储结构(php代码实现)

發(fā)布時(shí)間:2024/9/27 php 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php之二叉树,数据结构之二叉树——链式存储结构(php代码实现) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

/**

*?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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。