php之二叉树,数据结构之二叉树——链式存储结构(php代码实现)
/**
* ClearBiTree() 清空二叉树
* CreateBiTree() 创建二叉树
* BiTreeEmpty() 判断二叉树是否为空
* BiTreeDepth() 返回二叉树的深度
* root() 返回二叉树的根
* Parent() 返回给定元素的双亲
* LeftChild() 要返回左孩子的元素
* RightChild() 要返回右孩子的元素
* LeftSibling() 要返回左兄弟的元素
* RightSibling() 要返回右兄弟的元素
* Insert($data) 插入节点——递归算法
* insert2($data) 插入节点——非递归算法
* DeleteSubtree($elem,$LR) 删除某个节点的左(右)子树
* PreOrderTraverse() 先序遍历——递归算法
* InOrderTraverse() 中序遍历——递归算法
* PostOrderTraverse() 后序遍历——非递归算法
* preOrderTraverse2() 先序遍历——非递归算法
* preOrderTraverse3() 先序遍历——非递归算法
* inOrderTraverse2() 中序遍历——非递归算法
* inOrderTraverse3() 中序遍历——非递归算法
* postOrderTraverse2() 后序遍历——非递归算法
*/
class BiNode{
public $data;
public $lchild;
public $rchild;
public function __construct($data){
$this->data=$data; //节点数据
$this->lchild=null;//左孩子的指针
$this->rchild=null;//右孩子的指针
}
}
class LinkBiTree{
private $root; //二叉树的根节点
private static $preArr; //用于保存先序遍历后的数据
private static $inArr; //用于保存中序遍历后的数据
private static $postArr; //用于保存后序遍历后的数据
private static $levelArr; //用于保存后序遍历后的数据
private static $count; //记录创建二叉树结点的个数
const MAX_LEVEL=2;//二叉树最大的层数
public static $test;
public function __construct(){
$this->root=null;//指向根节点,初始化时为空树
self::$count=0;
}
/**
* 清空二叉树
*/
public function ClearBiTree(){
$this->clearTree($this->root);
}
/**
* @param $root 表示树的根节点
*/
private function clearTree($root){
if($root){
if($root->lchild){
$this->clearTree($root->lchild); //清空左子树
}
if($root->rchild){
$this->clearTree($root->rchild); //清空右子树
}
unset($root); //释放根节点
$root=null;
}
}
//先序遍历
public function PreOrderTraverse(){
$this->preTraverse($this->root);
return self::$preArr;
}
private function preTraverse($root){
if($root){
self::$preArr[]=$root->data; //先访问根节点
$this->preTraverse($root->lchild);//再先序遍历左子树
$this->preTraverse($root->rchild);//最后先序遍历右子树
}
}
//中序遍历
public function InOrderTraverse(){
$this->inTraverse($this->root);
return self::$inArr;
}
private function inTraverse($root){
if($root){
$this->inTraverse($root->lchild); //先中序遍历左子树
self::$inArr[]=$root->data; //再访问根节点
$this->inTraverse($root->rchild);//最后中序遍历右子树
}
}
//后序遍历
public function PostOrderTraverse(){
$this->postTraverse($this->root);
return self::$postArr;
}
private function postTraverse($root){
if($root){
$this->postTraverse($root->lchild); //先后序遍历左子树
$this->postTraverse($root->rchild); //再后序遍历右子树
self::$postArr[]=$root->data; //最后再访问根节点
}
}
//层序遍历
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);
}
}
//创建二叉树
public function CreateBiTree(){
$this->createTree($this->root);
}
//此处使用先序输入数据的方式来创建的
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);
}
}
//判断二叉树是否为空
public function BiTreeEmpty(){
// if($this->root){
// return false;
// }else{
// return true;
// }
return $this->root==null;
}
//返回二叉树的深度
public function BiTreeDepth(){
return $this->treeDepth($this->root);
}
private function treeDepth($root){
//求左子树的深度
$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;
}
//返回二叉树的根
public function Root(){
return $this->root==null ? 'Null':$this->root->data;
}
//返回给定元素的双亲
//此处分别使用php内部的array_push()和array_shift()这两个函数模拟队列
/**
* @param $elem
* @return string
* 返回给定元素的双亲
* 思路:1.使用数组队列来保存节点的指针
* 2.将根节点从队尾压入数组队列中
* 3.然后取出队首元素,使其左节点、右节点分别于给定的元素比较
* 4.相等的就返回上一步中取出的队首元素,否则,将此队首元素的左右节点指针分别压入队尾
* 5.重复第3步
*/
public function Parent($elem){
if($this->root){
$arr=array();//此处数组是当队列来使用的,用于存放树(包括子树)的根指针
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 要插入的数据
* 思路:1.插入的数据比树中的根(包括子树)节点小时,就放在根节点的左子树上;
* 2.比根节点大时,插入到右子树上;
* 注意:因为插入的位置不是叶节点就是只有左(或右)子树的节点,所以可以得知此递归的出口肯定是某个节点的左(或右)子树指针为空的时候。当此节点的左(右)都不为空的时候,递归就会持续下去,直到为左(右)子树有一边或全部为空的节点出现为止。
*/
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;
}
}
}
//非递归算法实现插入节点操作
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);
//表示如果要插入的数据$node->data大于根节点的数据$root->data并且根节点的
//左子树为空的话,那么就将$node->data赋值给左子树
if ($node->data data && !$root->lchild) {
$root->lchild = $node;
break;
//此处为大于,思路与小于相似
}else if($node->data > $root->data && !$root->rchild){
$root->rchild = $node;
break;
}
//以下两个if语句,表示如果上面的两个条件都不满足的话,那么就将跟的左右节点分别要入队列,继续循环
if($root->lchild){
array_push($arr,$root->lchild);
}
if($root->rchild){
array_push($arr,$root->rchild);
}
}
}
}
/**
* @param $elem 要删除的那个节点的子树
* @param $LR 表示是要删除左子树还是右子树
*/
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);
}
}
}
}
/*
以下是先序,中序,后序,层序的非递归实现算法
除了层序遍历使用了队列外,其他的是利用栈来实现的
思路: 1.输出当前根节点
2.将当前根节点的右孩子做压栈处理
3.将当前节点的右孩子作为新的根节点,如果为空的话,将栈顶元素弹出作为新的根节点。
4.重复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.将根节点压栈
* 2.弹出栈顶元素作为新的根节点
* 3.根据栈——先进后出的特性,先进根节点的右孩子做压栈处理,再将其左孩子做压栈处理;
* 4.重复2,3
* 注:此算法与上面的算法基本思想是相同的,只是细节处理上有所不同。
*/
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.将根节点压栈
* 2.将根节点的左孩子作为新的根节点,对其进行遍历
* 3.如果左子树遍历完毕,就将栈中的左子树结点弹出并输出,然后将此弹出结点的右孩子作为新的根节点。
* 4.重复1,2,3
* 注:此处或许有人对while循环的判断条件有所不理解。因为假如说我们只用$root是否为空来作为判断条件的话,那么当我们遍历完左子树后,程序就结束了,显然不是我们要的结果;假如我们只用栈$arr是否为空为判断条件的话,那么循环根本无法进行。
*
*/
public function inOrderTraverse2(){
$arr=array();
$root=$this->root;
while($root || count($arr)!=0){
if($root){
//根指针进栈,遍历左子树.此处之所以没有在循环外先将整棵树的根节点做压栈处理,是因为,如果这样做了,那么此处对左子树的遍历就会出现死循环,因为这是的判断条件就是$root->lchild,而不是$root了,倘若还是$root那么栈中就会有两个根(整棵树)。
array_push($arr,$root);
$root=$root->lchild;
}else{
//根指针退栈,访问根节点,遍历右子树
$root=array_pop($arr);
$arrIn[]=$root->data;
$root=$root->rchild;
}
}
return $arrIn;
}
//中序遍历算法3
/*
* 思路:1.先进根做压栈处理
* 2.遍历左子树
* 3.取出栈顶元素并将输出的节点作为新的根节点
* 4.将根节点的右孩子压栈并重新作为新的根节点
* 5.重复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;
}
//因为先序是根左右,而后序是左右根,如果将后序反转180度的话,那么顺序就是根右左.根据递归转换为非递归(栈)的方法——如果一个函数内有多于一个的递归调用那么此时,栈的进入顺序应该与递归调用的顺序相反。因为栈的特性是先进后出。
//后序遍历算法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);
}
//层级遍历算法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;
}
/*
* 递归转非递归算法小结:
* 1.如果函数体内只有一个递归调用,那么直接使用栈或队列等转换即可;
* 2.如果有多个递归调用并且相邻,比如先序和后序遍历算法,那么转为非递归算法时,先后顺序要倒转;
* 3.如果有多个递归但不相邻,比如中序遍历,那么就直接按照原先的顺序依次转换即可。但如果里面依然有部分相邻,那么就按小结2操作。
* 4.转换时,我们应该将哪些数据放入栈中呢。根据函数调用的原理,在调用一个函数时,内存中就会开辟一个栈空间,里面保存了函数的实参,局部变量和函数调用时的返回地址等,而我们要放入栈中的就是实参和局部变量(此处的局部变量是指后序递归要用到的局部变量)。
*/
}
php之二叉树,数据结构之二叉树——链式存储结构(php代码实现)相关推荐
- 数据结构学习笔记——链式存储结构实现栈(链栈)
目录 一.链栈的定义 二.链栈的初始化 三.判断链栈是否为空栈 四.进栈(插入操作) 五.出栈(删除操作) 六.读取链栈的栈顶元素 七.链栈的建立 八.链栈的遍历输出 链式存储结构实现栈完整代码 一个 ...
- 线性表的链式存储结构及代码实现(单链表,双链表,循环链表)
在上一篇博文中介绍了线性表的顺序存储方式,它最大的缺点就是在插入和删除操作时会移动大量的元素,这显然会耗费很多时间.后来人们便想到了用链式存储方式来解决上面这一问题.链式存储线性表时,不需要使用地 ...
- 【数据结构】 实验报告10 顺序、链式存储结构的二叉树递归遍历、层次遍历求高度
一.实验目的和要求 (源码在最后) 要求: 两种及以上存储结构(建议 顺序存储结构和链式存储结构各一).两种及以上方法(建议 递归遍历和层次遍历方法各一).分析各代码性能. 抽象数据类型(二叉树)独立 ...
- 二叉树的链式存储结构(线索二叉树)
一.链式存储结构 由于顺序存储二叉树的空间利用率较低,因此二叉树一般都采用链式存储结构,用链表结点来存储二叉树中的每个结点.在二叉树中,结点结构通过包括若干数据域和若干指针域,二叉链表至少包含3个域: ...
- Java实现队列 链式存储结构
2019独角兽企业重金招聘Python工程师标准>>> 队列的链式存储结构 示例代码如下, package hash;/*** Created with IntelliJ IDEA. ...
- 数据结构(C语言版)严蔚敏->二叉树(链式存储结构)的构造及其几种遍历方式(先序、中序、后序、层次)和线索二叉树
二叉树每个节点至多只有两棵子树(即二叉树中不存在度大于2的节点),并且二叉树的子树有左右之分,其次序不能任意颠倒. 1. 二叉树 二叉树一般采用链式存储结构,用链表节点来存储二叉树中每个节点.在二叉树 ...
- (数据结构)二叉树的链式存储结构
二叉树的顺序存储的缺点 因为并不是每个二叉树都是完全二叉树,普通二叉树使用顺序表存储或多或少会存在空间浪费的现象 图 1 普通二叉树的转化 如上图 1,普通二叉树里只有二个元素,最好的存储方式当然是开 ...
- 二叉树的链式存储结构--二叉链表
1 二叉树的链式存储结构 //二叉链表的结点结构定义typedef int TElemType; typedef struct BiTNode {TElemType data;struct BiTNo ...
- C语言手写二叉树(链式存储结构)
C语言手写二叉树(链式存储结构) 二叉树结构 二叉树基本运算 代码 图例(main函数执行过程如下:) 阶段I 阶段II 阶段III 阶段IV 阶段V 先序遍历输出过程 二叉树结构 二叉树可以用顺序存 ...
- 二叉树的链式存储结构
文章目录 前言 正文 总结 前言 上一节讲了二叉树的顺序存储,通过学习你会发现,其实二叉树并不适合用数组存储,因为并不是每个二叉树都是完全二叉树,普通二叉树使用顺序表存储或多或多会存在空间浪费的现象. ...
最新文章
- 快要普通话考试了,急需最后一题的根据话题自由讲话的演讲稿!(不要那些已经被用过...
- SQL性能优化整合持续更新
- 严重漏洞可导致 Juniper 设备遭劫持或破坏
- Long Short-Term Memory Over Tree Structures
- Expression Blend实例中文教程(11) - 视觉管理器快速入门Visual State Manager(VSM)
- JUnit 4 vs JUnit 5
- 真实的软件测试日常工作是咋样的?
- c语言max比较字符串,c语言中能不能用max函数求三个数的最大者呢?
- 使用阿里云的ip地址查询服务-使用java调用ip地址查询服务
- oracle01004,Oraclegoldengate的OGG-01004OGG-1296错误
- redis--ServiceStack
- Android仿京东、天猫商品详情页
- VS2019怎么没有C++的窗体应用模板_简历中常被忽视的「自我评价」,怎么写?
- PCB板HDI板几阶是什么意思
- 程序员的浪漫情诗,您学会了吗?
- 计算机组成原理3.6高速缓冲存储器Cache
- Go Mod引用私有库
- 12位符号数转归一化float的实现
- 友盟分享 QQ分享后,不显示左下角的小图标
- CSR8675 使用串口 UART 收发功能
热门文章
- 云原生时代来袭 下一代云数据库技术将走向何方?
- 阿里云数据传输服务低价不低质,服务再升级
- AliOS Things v1.1.2新特性
- 面试中遇到这 3 个SQL问题,最容易掉坑里!
- 剖析疫情环境下的国内云市场:大势所趋,正是大展拳脚的好时机!
- “AI捡垃圾”上热搜了!46城垃圾分类将投200亿,你怎么看?
- 成立一周?谷歌人工智能道德委员会解散了?近日,金山云和小米刚签订了不超过9000万的硬件产品供应协议,闹哪样? | 极客头条...
- 福利 | 2018 OpenInfra Days China限量版免费票任性放出
- mysql数据库前端缓存_什么是MySQL数据库的缓存池原理?看完或许就懂了
- 最小覆盖模型matlab_数学规划模型的matlab求解 非线性最小二乘lsqnonlin