写在前面:
一,本文侧重诠释对算法的思考记录过程,忽略其他诸如代码简洁、字符编码等细节问题。
二,本文结合 红黑树插入过程图示 这篇一起看,有助于理解。
<?php
class Node {
public $data;
public $left = NULL;
public $right = NULL;
public $color = 0;
public $parent = NULL;
public function __construct($data) {
$this->data = $data;
}
}
//红黑树(Red-Black Tree)是每个节点都带有颜色属性的二叉排序(查找)树,具备以下特性:
//1,节点是红色或黑色
//2,根节点是黑色的
//3,每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存储数据
//4,任何相邻的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的
//5,每个节点,从该节点到达其可达叶子节点的所有路径,都包含相同数目的黑色节点
class RBTree {
private static $tree;
const RED = 1;
const BLACK = 0;
public function getTree() {
return self::$tree;
}
//中序遍历
public function midOrderTraverse($p) {
if (!isset($p->data) || !$p) {
return;
}
if (isset($p->left->data)) {
$this->midOrderTraverse($p->left);
}
if (isset($p->data)) {
if ($p->color == self::RED) {
echo "<input style='color:red;font-size:15px;border:none;width:30px;' value=$p->data>";
} else {
printf("%s\n", $p->data);
}
}
// printf("%s\n", $p->data);
// var_dump($p);
if (isset($p->right->data)) {
$this->midOrderTraverse($p->right);
}
}
public function newNode($data) {
$node = new Node($data);
$node->left = new Node(NULL);//红黑树的每个叶子节点都是黑色,不存储数据
$node->left->parent = $node;
$node->right = new Node(NULL);
$node->right->parent = $node;
return $node;
}
public function insert($data) {
if (!self::$tree) {
$node = $this->newNode($data);
self::$tree = $node;
return;
}
$p = self::$tree;
while (isset($p->data)) {
if ($data < $p->data) {
if (!isset($p->left->data)) {
$node = $this->newNode($data);
$node->color = self::RED;
$node->parent = $p;
$p->left = $node;
$this->changeColor($p->left);
return;
}
$p = $p->left;
} elseif ($data > $p->data) {
if (!isset($p->right->data)) {
$node = $this->newNode($data);
$node->color = self::RED;
$node->parent = $p;
$p->right = $node;
$this->changeColor($p->right);
return;
}
$p = $p->right;
}
}
}
public function changeColor($tree) {
//被关注节点的父节点不是根节点
if (isset($tree->parent->parent)) {
$isLeft = $this->isLeft($tree);
$parentLeft = $this->isLeft($tree->parent);
$uncle = $this->getUncle($tree);
//case1:被关注节点的父节点和叔叔/伯伯节点都是红色
if ($tree->parent->color == self::RED && $uncle->color == self::RED) {
$tree->parent->color = self::BLACK;
$uncle->color = self::BLACK;
if (isset($tree->parent->parent->parent)) {//祖父不是根节点
$tree->parent->parent->color = self::RED;
$tree = $tree->parent->parent;//关注节点变成祖父节点
if (isset($tree->parent->parent)) {
$isLeft = $this->isLeft($tree);
$parentLeft = $this->isLeft($tree->parent);
$uncle = $this->getUncle($tree);
}
}
}
//case2:关注节点和其父节点都是红色,但不在同一侧(同为左侧或同为右侧),且叔叔/伯伯节点是黑色
if (isset($tree->parent->parent) && $tree->parent->color == self::RED && ($isLeft ^ $parentLeft) && $uncle->color == self::BLACK) {
if ($parentLeft) {//父节点在左侧
$tree = $this->leftRotate($tree->parent)->left;
$isLeft = TRUE;
} else {
$tree = $this->rightRotate($tree->parent)->right;
$isLeft = FALSE;
}
}
//case3:关注节点和其父节点都是红色,且在同一侧(同为左侧或同为右侧),且叔叔/伯伯节点是黑色
if (isset($tree->parent->parent) && $tree->parent->color == self::RED && !($isLeft ^ $parentLeft) && $uncle->color == self::BLACK ) {
$tree->parent->color = self::BLACK;
$tree->parent->parent->color = self::RED;
if ($parentLeft) {//父节点在左侧
$tree = $this->rightRotate($tree->parent->parent);
} else {
$tree = $this->leftRotate($tree->parent->parent);
}
} elseif (isset($tree->parent->parent) && $tree->parent->color == self::RED && $uncle->color == self::RED) {//case1
$this->changeColor($tree);
}
}
}
public function isLeft($tree) {
$isLeft = FALSE;
if ($tree->parent->left == $tree) {
$isLeft = TRUE;
}
return $isLeft;
}
public function getUncle(&$tree) {
$uncle = $tree->parent->parent->left;
if ($tree->parent->parent->left == $tree->parent) {
$uncle = $tree->parent->parent->right;
}
return $uncle;
}
//右旋
public function rightRotate($tree) {
$subTree = $tree->left;
if ($tree->parent) {
$subTree->parent = $tree->parent;//更新新子树的父节点
$left = FALSE;
if ($tree->parent->left == $tree) {
$left = TRUE;
}
} else {
$subTree->parent = NULL;
}
$tree->left = $subTree->right;//原来左节点的右子树挂到老的根节点的左子树
$subTree->right->parent = $tree;
$tree->parent = $subTree;//原来的左节点变成老的根节点的父节点
$subTree->right = $tree;//原来的根节点变成原来左节点的右子树
$tree = $subTree;
if (!$tree->parent) {//旋转的是整棵树的根节点
self::$tree = $tree;
} else {
if ($left) {//更新老的子树根节点父节点指针指向新的根节点
$tree->parent->left = $tree;
} else {
$tree->parent->right = $tree;
}
}
return $tree;
}
//左旋
public function leftRotate($tree) {
$subTree = $tree->right;
if ($tree->parent) {
$subTree->parent = $tree->parent;
$left = FALSE;
if ($tree->parent->left == $tree) {
$left = TRUE;
}
} else {
$subTree->parent = NULL;
}
$tree->right = $subTree->left;
$subTree->left->parent = $tree;
$tree->parent = $subTree;
$subTree->left = $tree;
$tree = $subTree;
if (!$tree->parent) {
self::$tree = $tree;
} else {
if ($left) {
$tree->parent->left = $tree;
} else {
$tree->parent->right = $tree;
}
}
return $tree;
}
}
//测试数据
$treeObj = new RBTree();
$treeObj->insert(99);
// $treeObj->insert(78);
// $treeObj->insert(120);
// $treeObj->insert(66);
// $treeObj->insert(83);
// $treeObj->insert(57);
// $treeObj->insert(52);
// $treeObj->insert(61);
// $treeObj->insert(64);
// $treeObj->insert(59);
// $treeObj->insert(60);
// $treeObj->insert(70);
// $treeObj->insert(75);
// $treeObj->insert(72);
// $treeObj->insert(74);
$tree = $treeObj->getTree();
$treeObj->midOrderTraverse($tree);