平衡二叉树-PHP

本文详细介绍了平衡二叉树的概念,特点及其实现方法。包括插入、删除操作中如何通过旋转保持树的平衡,提供了PHP代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.平衡树是基于搜索树之上的,主要的特点就是左右子树高度之差不能大于1,即就是平衡因子

2.插入和删除的过程中,要维持各个节点树高度,和维护树平衡

3.维持树平衡,有四种情况,左旋,右旋,先左旋在右旋,先右旋在左旋

github地址 : https://github.com/luolaifa000/phpStudyCode/blob/master/BlanceTree.php

<?php
function pre($arr)
{
    $data = func_get_args();
    foreach($data as $key=>$val)
    {
        echo '<pre>';
        print_r($val);
        echo '</pre>';
    }
}

function prend()
{
    $data = func_get_args();
    foreach($data as $key=>$val)
    {
        echo '<pre>';
        print_r($val);
        echo '</pre>';
    }
    exit();
}
/**
 * 树节点
 * 
 * @author yumancang
 *
 */
class TreeNode 
{
    /**
     * 左节点
     * @var unknown
     */
    public $lNode;
    
    /**
     * 右节点
     * @var unknown
     */
    public $rNode;
    
    /**
     * 父节点
     * @var unknown
     */
    public $pNode;
    /**
     * 值
     * @var unknown
     */
    public $val;
    
    public $data;
    
    /**
     * 树高度
     * @var unknown
     */
    public $height;
    
    /**
     * 构造
     * 
     * @param int $val
     * @param TreeNode $lNode
     * @param TreeNode $rNode
     */
    public function __construct(int $val = null ,TreeNode $lNode = null ,TreeNode $rNode = null, 
        TreeNode $pNode = null, int $height = 1)
    {
        $this->val = $val;
        $this->lNode = $lNode;
        $this->rNode = $rNode;
        $this->pNode = $pNode;
        $this->height = 1;
    }
}
/**
 * 平衡二叉树
 * 两边子树之前高度差不能大于1,否则需要旋转来达到树平衡
 * @author yumancang
 *
 */
class BlanceTree
{
    public $treeRootNode;
    
    public function __construct()
    {
        $this->treeRootNode = null;
    }

    /**
     * 前序输出树 5 2 1 3 9 8 10
     * 根左右
     * 
     * @param TreeNode $rootNode
     */
    public function prePrintTree(TreeNode $rootNode = null)
    {
        pre($rootNode->val);
        if ($rootNode->lNode) {
            $this->prePrintTree($rootNode->lNode);
        }
        if ($rootNode->rNode) {
            $this->prePrintTree($rootNode->rNode);
        }
        //pre($rootNode);
    }
    /**
     * 中序输出   1 2 3 5 8 9 10
     * 左根右
     * 
     * @param TreeNode $rootNode
     */
    public function middlePrintTree(TreeNode $rootNode = null)
    {
        
        if ($rootNode->lNode) {
            $this->middlePrintTree($rootNode->lNode);
        }
        pre($rootNode->val);
        if ($rootNode->rNode) {
            $this->middlePrintTree($rootNode->rNode);
        }
    }
    
    /**
     * 后序输出  1 3 2 8 10 9 5
     * 左右根
     * 
     * @param TreeNode $rootNode
     */
    public function afterPrintTree(TreeNode $rootNode = null) 
    {
        if ($rootNode->lNode) {
            $this->afterPrintTree($rootNode->lNode);
        }
        if ($rootNode->rNode) {
            $this->afterPrintTree($rootNode->rNode);
        }
        pre($rootNode->val);
    }
    
    /**
     * 
     * 平衡二叉树插入数据
     * 
     */
    public function insertTree($val, &$rootNode, $parentNode = null)
    {
        if ($this->treeRootNode == null) {
            $rootNode = new TreeNode();
            $rootNode->val = $val;
            $rootNode->lNode = null;
            $rootNode->rNode = null;
            $rootNode->pNode = null;
            $rootNode->height = 1;
            return true;
        } else {
            if ($rootNode == null) {
                $rootNode = new TreeNode();
                $rootNode->val = $val;
                $rootNode->lNode = null;
                $rootNode->rNode = null;
                $rootNode->pNode = $parentNode;
                $rootNode->height = 1;
                return true;
            }
        }
        //插入过程已存在就返回不处理
        if ($val == $rootNode->val) {
            return false;
        }
        if ($val < $rootNode->val) {
            $this->insertTree($val, $rootNode->lNode, $rootNode);
        }
        if ($val > $rootNode->val) {
            $this->insertTree($val, $rootNode->rNode, $rootNode);
        }
        $lNodeHeight = $rootNode->lNode ? $rootNode->lNode->height : 1;
        $rNodeHeight = $rootNode->rNode ? $rootNode->rNode->height : 1;
        //求节点的高度
        $rootNode->height = max($lNodeHeight,$rNodeHeight) + 1;
        //节点的平衡因子是否大于1
        #左左情况,右旋
        if (($lNodeHeight - $rNodeHeight) > 1) {
            if ($rootNode->lNode->lNode->height > $rootNode->lNode->rNode->height) {
                $this->rightRotate($rootNode->lNode, $rootNode);
            } else {
                
                $this->leftAndRightRotate($rootNode);
            }
            
        }
        
        #右右情况,左旋
        if (($lNodeHeight - $rNodeHeight) < -1) {
            
            if ($rootNode->rNode->lNode->height > $rootNode->rNode->rNode->height) {
                
                $this->rightAndLeftRotate($rootNode);
            } else {
                $this->leftRotate($rootNode->rNode, $rootNode);
            }
            
        }
        
    }
    
    /**
     * 右左情况,右旋在左旋
     * @param TreeNode $newNode
     * @param TreeNode $oldNode
     */
    public function rightAndLeftRotate(TreeNode $rootNode = null)
    {
        
        //先右旋
        $newNode = $rootNode->rNode->lNode;
        $oldNode = $rootNode->rNode;
        
        $oldNode->lNode = $newNode->rNode;
        $newNode->rNode->pNode = $oldNode;
        
        $newNode->pNode = $oldNode->pNode;
        $oldNode->pNode->rNode = $newNode;
        
        $newNode->rNode = $oldNode;
        $oldNode->pNode = $newNode;

        $newNode->height++;
        $oldNode->height--;
        //在左旋
        $this->leftRotate($rootNode->rNode,$rootNode);
        $this->treeRootNode->pNode = null;
        //prend($this->treeRootNode);
    }
    
    /**
     * 左右情况,左旋在右旋
     * @param TreeNode $newNode
     * @param TreeNode $oldNode
     */
    public function leftAndRightRotate(TreeNode $rootNode = null)
    {
        //先左旋
        $newNode = $rootNode->lNode->rNode;
        $oldNode = $rootNode->lNode;
        
        $oldNode->rNode = $newNode->lNode;
        $newNode->lNode->pNode = $oldNode;
        
        $newNode->pNode = $oldNode->pNode;
        $oldNode->pNode->lNode = $newNode;
        
        $newNode->lNode = $oldNode;
        $oldNode->pNode = $newNode;
        
        $newNode->height++;
        $oldNode->height--;
        
        //在右旋
        $this->rightRotate($rootNode->lNode,$rootNode);
        $this->treeRootNode->pNode = null;
    }
    
    /**
     * 右右情况,左旋
     * @param TreeNode $newNode
     * @param TreeNode $oldNode
     */
    public function leftRotate(TreeNode $newNode = null, TreeNode $oldNode = null)
    {
        $oldNode->rNode = $newNode->lNode;
        if ($newNode->lNode) {
            $newNode->lNode->pNode = $oldNode;
        }
        $newNode->lNode = $oldNode;
        $oldNode->pNode = $newNode;
        
        
        if ($oldNode == $this->treeRootNode)
            $this->treeRootNode = $newNode;
        
        $this->treeRootNode->pNode = null;
            
        $oldNode->height--;
        $oldNode->height--;
        
        //prend($this->treeRootNode);
    }
    
    /**
     * 左左情况,右旋
     * @param TreeNode $newNode
     * @param TreeNode $oldNode
     */
    public function rightRotate(TreeNode $newNode = null, TreeNode $oldNode = null)
    {
        
        $oldNode->lNode = $newNode->rNode;
        if ($newNode->rNode) {
            $newNode->rNode->pNode = $oldNode;
        }
        $newNode->rNode = $oldNode;
        $oldNode->pNode = $newNode;
        
        if ($oldNode == $this->treeRootNode)
            $this->treeRootNode = $newNode;
        $this->treeRootNode->pNode = null;
        $oldNode->height--;
        $oldNode->height--;

        //prend($this->treeRootNode);
    }
    
    /**
     * 查询二叉树
     * 
     * @param int $val
     * @param TreeNode $rootNode
     */
    public function serarchTree(int $val, TreeNode &$rootNode = null)
    {
        if ($rootNode == null) {
            return false;
        }
        if ($val == $rootNode->val) {
            return $rootNode;
        }
        if ($val < $rootNode->val) {
            return $this->serarchTree($val, $rootNode->lNode);
        }
        if ($val > $rootNode->val) {
            return $this->serarchTree($val, $rootNode->rNode);
        }
    }
    
    /**
     * 平衡二叉树中删除节点
     * 跟搜索二叉树删除节点还是不一样的
     * 平衡二叉树原本就维持左右子树高度差不大于1,所有删除过程中
     * 不会出现后继节点出现在子树的子树下面
     * 
     * @param int $val
     * @param TreeNode $rootNode
     */
    public function deleteTree(int $val, TreeNode &$rootNode = null)
    {
        $node = $this->serarchTree($val,$rootNode);
        if (!$node) {
            return false;
        }
        //没有右子树
        if ($node->rNode == null) {
            $this->transplantNode($node,$node->lNode);
        }
        //有右子树
        if ($node->rNode) {
            //右子节点下面没有左节点的话
            if ($node->rNode->lNode == null) {
                $this->transplantNode($node,$node->rNode);
            }
            //右子节点下面有左节点的话
            if ($node->rNode->lNode) {
                //找出下面最小的那个节点,就递归左边
                $minNode = $this->findNodeBelowMin($node->rNode->lNode);
                //移动节点
                $this->transplantNode($minNode,$minNode->rNode);
                $this->transplantSingleNode($node,$minNode);
            }
        }
        
        $lNodeHeight = $node->pNode->lNode ? $rootNode->pNode->lNode->height : 1;
        $rNodeHeight = $rootNode->pNode->rNode ? $rootNode->pNode->rNode->height : 1;
        //求节点的高度
        $node->pNode->height = max($lNodeHeight,$rNodeHeight) + 1;
        //节点的平衡因子是否大于1
        #左左情况,右旋
        if (($lNodeHeight - $rNodeHeight) > 1) {
            if ($node->pNode->lNode->lNode->height > $node->pNode->lNode->rNode->height) {
                $this->rightRotate($node->pNode->lNode, $node->pNode);
            } else {
                
                $this->leftAndRightRotate($node->pNode);
            }
            
        }
        
        #右右情况,左旋
        if (($lNodeHeight - $rNodeHeight) < -1) {
            
            if ($node->pNode->rNode->lNode->height > $node->pNode->rNode->rNode->height) {
                
                $this->rightAndLeftRotate($node->pNode);
            } else {
                $this->leftRotate($node->pNode->rNode, $node->pNode);
            }
            
        }
    }
    /**
     * 移动节点不需要附带自己的左右
     * 
     * 
     * @param TreeNode $source
     * @param TreeNode $des
     * @return boolean
     */
    public function transplantSingleNode(TreeNode $source = null, TreeNode $des = null)
    {
        if ($source == null) {
            return false;
        }
        if ($source->pNode == null) {
            $this->treeRootNode = $des;
            return true;
        }
        //原节点是其父节点的左子树
        if ($source === $source->pNode->lNode) {
            $source->pNode->lNode = $des;
        }
        //原节点是其父节点的右子树
        if ($source === $source->pNode->rNode) {
            $source->pNode->rNode = $des;
        }
        if ($des != null) {
            $des->pNode = $source->pNode;
            $des->rNode = $source->rNode;
            $des->lNode = $source->lNode;
        }
        $des->height = $source->height;
        return true;
    }
    
    /**
     * 找出当前节点和它子树下面最小的节点
     * 
     * @param TreeNode $node
     * @return NULL|TreeNode
     */
    public function findNodeBelowMin(TreeNode $node = null)
    {
        if ($node == null) {
            return null;
        }
        while ($node->lNode != null) {
            $node = $node->lNode;
        }
        return $node;
    }
    
    /**
     * 找出当前节点和它子树下面最大的节点
     * 
     * @param TreeNode $node
     * @return NULL|TreeNode|unknown
     */
    public function findNodeBelowMax(TreeNode $node)
    {
        if ($node == null) {
            return null;
        }
        while ($node->rNode != null) {
            $node = $node->rNode;
        }
        return $node;
    }
    
    /**
     * 移动节点 需要附带自己的左右
     * 
     * @param TreeNode $source
     * @param TreeNode $des
     * @return boolean
     */
    public function transplantNode(TreeNode $source = null, TreeNode $des = null)
    {
        if ($source == null) {
            return false;
        }

        //原节点是其父节点的左子树
        if ($source === $source->pNode->lNode) {
            $source->pNode->lNode = $des;
            
        }
        //原节点是其父节点的右子树
        if ($source === $source->pNode->rNode) {
            $source->pNode->rNode = $des;
        }
        $des != null && $des->pNode = $source->pNode;
        
        while ($source->pNode) {
            $source->pNode->height--;
            $source->pNode = $source->pNode->pNode;
        }

        return true;
    }
    
    public function getNodeBlanceNum(TreeNode $node)
    {
        $lNodeHeight = $node->lNode ? $node->lNode->height : 0;
        $rNodeHeight = $node->rNode ? $node->rNode->height : 0;
        return max($lNodeHeight,$rNodeHeight) + 1;
    }
    
    /**
     * 查找一个节点前继节点
     * 
     * @param int $val
     * @param TreeNode $rootNode
     */
    public function findPreNodeTree(int $val,TreeNode $rootNode = null)
    {

    }
    
    /**
     * 
     * 查找一个节点后继节点
     *
     * @param int $val
     * @param TreeNode $rootNode
     */
    public function findAfterNodeTree(int $val,TreeNode $rootNode = null)
    {

    }
}

$start = memory_get_usage();
$binTree = new BlanceTree();
#插入树
//插入7,左左情况,右旋
/* $binTree->insertTree(10,$binTree->treeRootNode);
$binTree->insertTree(9,$binTree->treeRootNode);
$binTree->insertTree(11,$binTree->treeRootNode);
$binTree->insertTree(8,$binTree->treeRootNode);
$binTree->insertTree(9.1,$binTree->treeRootNode);
$binTree->insertTree(7,$binTree->treeRootNode); */
//插入13,右右情况,左旋
/* $binTree->insertTree(10,$binTree->treeRootNode);
$binTree->insertTree(9,$binTree->treeRootNode);
$binTree->insertTree(11,$binTree->treeRootNode);
$binTree->insertTree(10.1,$binTree->treeRootNode);
$binTree->insertTree(12,$binTree->treeRootNode);
$binTree->insertTree(13,$binTree->treeRootNode); */

//插入9.1,左右情况,先按9左旋,再按9.2右旋
/* $binTree->insertTree(10,$binTree->treeRootNode);
$binTree->insertTree(9,$binTree->treeRootNode);
$binTree->insertTree(11,$binTree->treeRootNode);
$binTree->insertTree(8,$binTree->treeRootNode);
$binTree->insertTree(9.2,$binTree->treeRootNode);
$binTree->insertTree(9.1,$binTree->treeRootNode); */

//插入10.2,右左情况,先按11右旋,再按10.1左旋
/* $binTree->insertTree(10,$binTree->treeRootNode);
$binTree->insertTree(9,$binTree->treeRootNode);
$binTree->insertTree(11,$binTree->treeRootNode);
$binTree->insertTree(10.1,$binTree->treeRootNode);
$binTree->insertTree(12,$binTree->treeRootNode);
$binTree->insertTree(10.2,$binTree->treeRootNode); */

#删除树
//删除根节点,左子树高度大于右边
/* $binTree->insertTree(10,$binTree->treeRootNode);
$binTree->insertTree(9,$binTree->treeRootNode);
$binTree->insertTree(11,$binTree->treeRootNode);
$binTree->insertTree(8,$binTree->treeRootNode);
$binTree->deleteTree(10,$binTree->treeRootNode); */
//删除根节点,左子树高度小于右边
/* $binTree->insertTree(10,$binTree->treeRootNode);
$binTree->insertTree(9,$binTree->treeRootNode);
$binTree->insertTree(11,$binTree->treeRootNode);
$binTree->insertTree(12,$binTree->treeRootNode);
$binTree->deleteTree(10,$binTree->treeRootNode); */
//删除根节点,左子树高度等于右边
/* $binTree->insertTree(10,$binTree->treeRootNode);
$binTree->insertTree(9,$binTree->treeRootNode);
$binTree->insertTree(8,$binTree->treeRootNode);
$binTree->insertTree(11,$binTree->treeRootNode);
$binTree->insertTree(12,$binTree->treeRootNode);
$binTree->deleteTree(10,$binTree->treeRootNode); */
//删除非根节点,左子树高度大于右边
$binTree->insertTree(10,$binTree->treeRootNode);
$binTree->insertTree(9,$binTree->treeRootNode);
$binTree->insertTree(11,$binTree->treeRootNode);
$binTree->insertTree(8,$binTree->treeRootNode);
$binTree->deleteTree(9,$binTree->treeRootNode);
prend($binTree->treeRootNode);
#打印树
pre("中序");
$binTree->middlePrintTree($binTree->treeRootNode);
pre($binTree->treeRootNode);
$end = memory_get_usage();
prend("占用内存:",($end-$start)/1024/1024);

?>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值