红黑树实现 --php版本

<?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);

September 17, 2019 · 7 min · holdsky