编程技术记录

世界你好!

<?php

//大部分代码按照自己对算法导论的伪代码的理解编写
namespace zxszl;

/*
红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶节点(NIL)是黑色。 [注意:这里叶节点,是指为空(NIL或NULL)的子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的叶节点的所有路径上包含相同数目的黑节点。
*/
//参考 https://blog.csdn.net/v_JULY_v/article/details/6105630

class RedBlackNode
{
    public $parent;//父节点
    public $left; //左子节点
    public $right; //右子节点
    public $isRed; //红色节点或者黑色节点

    /**
     * 这里引入两个变量,是为了方便字典数据结构
     * 其中,key作为红黑树的排序依据    
     */
    public $key; //节点值
    public $value; //节点值

    public function __construct ()
    {
        $this->parent = null;
        $this->left = null;
        $this->right = null;
        $this->isRed = true;

        $this->key = null;
        $this->value = null;
    }
}
/**
 * 红黑树 ,左边小节点,右边大节点
 */
class RedBlackTree
{
     /**
     * 深度(树高度)
     * @param RedBlackNode $node
     * @return int
     */
    static public function depth($node)
    {
        if ($node === NULL) {
            return 0;
        }
        $left_d = RedBlackTree::depth($node->left);
        $right_d = RedBlackTree::depth($node->right);
        return ($left_d > $right_d ? $left_d : $right_d) + 1;
    }
    /**
     * 最小节点
     * @return RedBlackNode 最小节点
     */
    static public function minNode($node)
    {
        $current = $node;
        if ($current !== null) {
            while ($current->left !== null) {
                $current = $current->left;
            }
        }
        return $current;
    }

    /**
     * 最大节点
     * @return RedBlackNode 最大节点
     */
    static public function maxNode($node)
    {
        $current = $node;
        if ($current !== null) {
            while ($current->right !== null) {
                $current = $current->right;
            }
        }
        return $current;
    }

    /**
     * 查找指定节点
     * @return RedBlackNode 节点
     */
    public function searchNode($key)
    {
        $current = $this->rootNode;
        while ($current !== NULL) 
        {
            if ($current->key === $key) {
                return $current;
            } elseif ($current->key > $key) {
                $current = $current->left;
            } else {
                $current = $current->right;
            }
        }
        return null;
    }
    /**
     * 插入节点,若$key已经存在,则替换对应节点的$value
     * @return RedBlackNode 插入的节点
     */
    public function insertNode($key,$value)
    {
        //查找节点插入时的父亲节点
        $nodeParent = null;
        do
        {
            $tmp = $this->rootNode;
            while ($tmp !== null)
            {
                $nodeParent = $tmp;
                if ($key === $tmp->key ) {//已经存在key,不必再插入,只要替换value就可以
                    $tmp->value = $value;
                    return; 
                }
                else if ( $key < $tmp->key ) {
                    $tmp = $tmp->left;
                }
                else {
                    $tmp = $tmp->right;
                }
            }
        }while(0);

        //创建新节点,节点为红色
        $node = new RedBlackNode();
        // $node->isRed = true;
        $node->key = $key;
        $node->value = $value;

        //插入节点
        $node->parent = $nodeParent;
        if ($nodeParent === null) {
            $this->rootNode = $node;
        }
        else if ($node->key < $nodeParent->key) {
            $nodeParent->left = $node;
        }
        else {
            $nodeParent->right = $node;
        }
        //修正红黑树
        $this->p_insert_fixup($node);
    }
    /**
     * 删除节点
     */
    public function deleteNode ($key) {
        $z = $this->searchNode($key);
        if ( $z !== null ) //找到$key对应的节点
        {   //$z //待删除节点
            $y = null;
            $x = null;

            ///>>>START $z不为null,那么经过操作,$y一定不为null
            if ($z->left === null || $z->right === null) {
                $y = $z;
            } else {
                $y = RedBlackTree::minNode($z->right); //$z的最小上限
            }
            ///<<<END

            if ( $y->left !== null ) {
                $x = $y->left;
            } else {
                $x = $y->right;
            }

            //删除节点$y
            if ($x !== null){
                $x->parent = $y->parent;
            }

            if ($y->parent === null ) {
                $this->rootNode = $x;
            }
            else if ( $y === $y->parent->left ) {
                $y->parent->left = $x;
            }
            else {
                $y->parent->right = $x;
            }
            $xParent = $y->parent;

            //如果$y不是$z,那么用$y的信息覆盖$z,这样达到删除$z的目的
            if ($y !== $z) {
                $z->key = $y->key;
                $z->value = $y->value;
            }
            //如果删除的节点是黑色,则需要修正
            if ($y->isRed === false) {//$y可能是$z ,也可能是$z的最小上限
                $this->p_delete_fixup($x,$xParent);
            }
        }
    }

    /**
     * 修正红黑树
     * @param RedBlackNode node 插入的节点
     */
    private function p_insert_fixup($node)
    {
        while ($node->parent !== null && $node->parent->isRed === true ) //如果node的父节点是红色 (node是红色)
        {
            $grandParent = $node->parent->parent; //祖父节点,一定存在 (根据红黑树性质,红色节点一定有父节点))
            if ($node->parent === $grandParent->left ) { //node的父节点是左孩子
                $uncle = $grandParent->right;
                //case1:node的父节点是红色,且叔节点是红色
                if ($uncle && $uncle->isRed === true ) {
                    $uncle->isRed = false;
                    $node->parent->isRed = false;
                    $grandParent->isRed = true;
                    $node = $grandParent;
                    continue; //经过这一步之后,组父节点作为新节点存在(跳到case2)
                }
                //case2:node的父节点是红色,叔节点是黑色,node是其父节点的右孩子
                else if ($node === $node->parent->right ) {
                    $node = $node->parent;
                    $this->p_left_rotate($node);
                }
                //case3:node的父节点是红色,叔节点是黑色,node是其父节点的左孩子
                $node->parent->isRed = false;
                $grandParent->isRed = true;
                $this->p_right_rotate($grandParent);
            }
            else { //node的父节点是右孩子
                $uncle = $grandParent->left;
                //case1:node的父节点是红色,且叔节点是红色
                if ($uncle && $uncle->isRed === true ) {
                    $uncle->isRed = false;
                    $node->parent->isRed = false;
                    $grandParent->isRed = true;
                    $node = $grandParent;
                    continue; //经过这一步之后,组父节点作为新节点存在(跳到case2)
                }
                //case2:node的父节点是红色,叔节点是黑色,node是其父节点的左孩子
                else if ($node === $node->parent->left ) {
                    $node = $node->parent;
                    $this->p_right_rotate($node);
                }
                //case3:node的父节点是红色,叔节点是黑色,node是其父节点的左孩子
                $node->parent->isRed = false;
                $grandParent->isRed = true;
                $this->p_left_rotate($grandParent);
            } 
        }
        $this->rootNode->isRed = false;
    }

    /**
     * 修正红黑树
     * @param RedBlackNode $x
     * @param RedBlackNode $xParent
     */
    private function p_delete_fixup($x,$xParent)
    {
        while ( $x !== $this->rootNode && ($x ===null || $x->isRed === false ) )
        {
            if ($x === $xParent->left ) //$x不是根节点,那么$xParent一定存在
            { 
                $w = $xParent->right;
                if ($w !== null ) //若为null,则$w黑色
                {
                    //case1:$x黑, 兄弟节点$w红,父节点黑
                    if ($w->isRed === true ) 
                    {
                        $w->isRed = false;
                        $xParent->isRed = true;
                        $this->p_left_rotate($xParent);
                        $w = $xParent->right; //在左旋处理后,$xParent->right指向的是原来兄弟结点$w的右孩子,黑色
                    }
                    //case2:$x黑色, $w是黑色,且$w两个子节点都是黑色
                    if ( ($w->left===null || $w->left->isRed === false)
                            && ($w->right===null || $w->right->isRed === false) )
                    {
                        $w->isRed = true;
                        $x = $xParent;
                        $xParent = $x->parent;
                    }
                    else 
                    {
                        //case3:$x黑色,$w黑色,$w左子是红色,$w右子是黑色
                        if ( $w->right===null || $w->right->isRed === false )
                        {
                            $w->left->isRed = false;
                            $w->isRed = true;
                            $this->p_right_rotate($w);
                            $w = $xParent->right;
                        }
                        //case4:$x黑色,$w黑色,$w右子是红色,$w左子的颜色任意
                        $w->isRed = $xParent->isRed;
                        $xParent->isRed = false;
                        if ($w->right !== null ) {
                            $w->right->isRed = false;
                        }
                        $this->p_left_rotate($xParent);
                        $x = $this->rootNode;
                        break;//加不加break都可以,因为下次循环条件不满足
                    }
                }
            }
            else //$x是右孩子  $x不是根节点,那么$xParent一定存在
            { 
                $w = $xParent->left;
                if ($w !== null ) //若为null,则$w黑色
                {
                    //case1:$x黑, 兄弟节点$w红,父节点黑
                    if ($w->isRed === true ) 
                    {
                        $w->isRed = false;
                        $xParent->isRed = true;
                        $this->p_right_rotate($xParent);
                        $w = $xParent->left; //在右旋处理后,$xParent->left指向的是原来兄弟结点$w的左孩子,黑色
                    }
                    //case2:$x黑色, $w是黑色,且$w两个子节点都是黑色
                    if ( ($w->left===null || $w->left->isRed === false)
                            && ($w->right===null || $w->right->isRed === false) )
                    {
                        $w->isRed = true;
                        $x = $xParent;
                        $xParent = $x->parent;
                    }
                    else 
                    {
                        //case3:$x黑色,$w黑色,$w左子是黑色,$w右子是红色
                        if ( $w->left===null || $w->left->isRed === false )
                        {
                            $w->right->isRed = false;
                            $w->isRed = true;
                            $this->p_left_rotate($w);
                            $w = $xParent->left;
                        }
                        //case4:$x黑色,$w黑色,$w左子是红色,$w右子的颜色任意
                        $w->isRed = $xParent->isRed;
                        $xParent->isRed = false;
                        if ($w->left !== null ) {
                            $w->left->isRed = false;
                        }
                        $this->p_right_rotate($xParent);
                        $x = $this->rootNode;
                        break;//加不加break都可以,因为下次循环条件不满足
                    }
                }
            }
        }
        if ($x !== null ) {
            $x->isRed = false;
        }
    }

    /**
     * 对指定节点进行左旋操作
     * 左旋中的“左”,意味着“被旋转的节点将变成(自己右孩子的)一个左节点
     *      node                  right
     *      /  \                  /  \ 
     *     a  right   ======>   node  c
     *        /  \              /  \
     *       b    c            a    b
     * 
     * @param RedBlackNode $node
     */
    private function p_left_rotate($node)
    {
        $rightChild = $node->right; //右孩子
        if ($rightChild === null){
            //右孩子为null,左旋结束
            return;
        }
        $rightChild->parent = $node->parent; //将 “node的父亲” 设为 “rightChild的父亲”
        if ($rightChild->parent === null) { 
            //情况1:如果 “rightChild的父亲” 是空节点,则将rightChild设为根节点
            $this->rootNode = $rightChild;
        }
        else if ($node === $node->parent->left ) { 
            //情况2:如果 node是它父节点的左孩子,则将rightChild设为“node的父节点的左孩子”
            $node->parent->left = $rightChild; 
        }
        else {
            // 情况3:(node是它父节点的右孩子) 将rightChild设为“node的父节点的右孩子”
            $node->parent->right = $rightChild; 
        }
        $node->parent = $rightChild; //将 “node的父节点” 设为 “rightChild”

        $node->right = $rightChild->left;    //将 “rightChild的左孩子” 设为 “node的右孩子”
        if ($rightChild->left !== null) {
            $rightChild->left->parent = $node;   //将 “node” 设为 “rightChild的左孩子的父亲”
        }
        $rightChild->left = $node; //将 “node” 设为 “rightChild的左孩子”
    }

    /**
     * 对指定节点进行右旋操作
     * 右旋中的“右”,意味着“被旋转的节点将变成(自己左孩子的)一个右节点”
     *      left                  node
     *      /  \                  /  \ 
     *     a  node   <======    left  c
     *        /  \              /  \
     *       b    c            a    b
     * 
     * @param RedBlackNode $node
     */    
    private function p_right_rotate($node)
    {
        $leftChild = $node->left; //左孩子
        if ($leftChild === null) {
            //左孩子为null,右旋结束
            return;
        }
        $leftChild->parent = $node->parent; //将 “node的父节点” 设为 “leftChild的父节点”
        if ($leftChild->parent === null) {
            // 情况1:如果 “leftChild的父亲” 是空节点,则将leftChild设为根节点
            $this->rootNode = $leftChild;
        }
        else if ($node === $node->parent->left ) {
            // 情况2:如果 node 是它父节点的左孩子,则将leftChild设为“node的父节点的左孩子”
            $node->parent->left = $leftChild;
        }
        else {
            // 情况3:如果 node 是它父节点的右孩子,则将leftChild设为“node的父节点的右孩子”
            $node->parent->right = $leftChild;
        }
        $node->parent = $leftChild; //将leftChild设为“node的父节点”

        $node->left = $leftChild->right; //将 “leftChild的右孩子” 设为"node的左孩子"
        if ( $leftChild->right !== null ){
            $leftChild->right->parent = $node; //将node 设为 “leftChild的右孩子的父节点”
        }
        $leftChild->right = $node;   //将 "node" 设为 “leftChild的右孩子”
    }

    private $rootNode;
}

 $rbTree = new RedBlackTree();
 $testN = 10000;

 function testInsert( $rbTree )
 {
     global $testN;
     $tmpArray = array();
     for ($i = 0 ; $i < $testN ; $i++)
     {
         array_push($tmpArray,$i);
     }
     shuffle($tmpArray);

     for ($i = 0 ; $i < $testN ; $i++)
     {
         $k = $tmpArray[$i];
         $rbTree->insertNode($k,$k);
     }

     for ($i = 0 ; $i < $testN ; $i++)
     {
         $node = $rbTree->searchNode($i);
         if($node === null) {
             echo "not found $i";
         }
         else if ($node->value !== $i ) {
             $value = $node->value;
             echo "found $i,but value is $value";
         }
     }
 }

 function testDelete( $rbTree )
 {
     global $testN;
     $tmpArray = array();
     for ($i = 0 ; $i < $testN ; $i++)
     {
         array_push($tmpArray,$i);
     }
     shuffle($tmpArray);

     for ($i = 0 ; $i < $testN ; $i++)
     {
         $k = $tmpArray[$i];
         $rbTree->deleteNode($k);
     }

     for ($i = 0 ; $i < $testN ; $i++)
     {
         $node = $rbTree->searchNode($i);
         if($node !== null) {
             echo "found $i";
         }
     }
 }

 testInsert($rbTree);
 testDelete($rbTree);

© Beli. All Rights Reserved.