PHP實(shí)現(xiàn)繪制二叉樹圖形顯示功能詳解【包括二叉搜索樹、平衡樹及紅黑樹】
本文實(shí)例講述了PHP實(shí)現(xiàn)繪制二叉樹圖形顯示功能。分享給大家供大家參考,具體如下:
前言:
最近老師布置了一個(gè)作業(yè):理解并實(shí)現(xiàn)平衡二叉樹和紅黑樹,本來老師是說用C#寫的,但是我學(xué)的C#基本都還給老師了,怎么辦?那就用現(xiàn)在最熟悉的語言PHP來寫吧!
有一個(gè)問題來了,書上在講解樹的時(shí)候基本上會(huì)給出形象的樹形圖。但是當(dāng)我們自己試著實(shí)現(xiàn)某種樹,在調(diào)試、輸出的時(shí)候確只能以字符的形式順序地輸出。這給調(diào)試等方面帶來了很大的不便。然后在各種百度之后,我發(fā)現(xiàn)利用PHP實(shí)現(xiàn)二叉樹的圖形顯示的資源幾乎是零!好吧,那我就自己個(gè)兒實(shí)現(xiàn)一個(gè)!
效果顯示:
如果我是直接在這一步擺代碼的話,估計(jì)大家會(huì)比較煩悶,那我就直接上結(jié)果吧,后面在補(bǔ)代碼,先激發(fā)激發(fā)大家的閱讀興趣:
1、搜索二叉樹:

2、平衡二叉樹:

3、紅黑樹:

上代碼:
我們給圖片創(chuàng)建一個(gè)類吧,顯得稍微的小高級:
image.php 文件:
<?php
/**
* author:LSGOZJ
* description: 繪制二叉樹圖像
*/
class image
{
//樹相關(guān)設(shè)置
//每層之間的間隔高度
private $level_high = 100;
//最底層葉子結(jié)點(diǎn)之間的寬度
private $leaf_width = 50;
//結(jié)點(diǎn)圓的半徑
private $rad = 20;
//根節(jié)點(diǎn)離邊框頂端距離
private $leave = 20;
//樹(保存樹對象的引用)
private $tree;
//樹的層數(shù)
private $level;
//完全二叉樹中最底層葉子結(jié)點(diǎn)數(shù)量(計(jì)算圖像寬度時(shí)用到,論如何實(shí)現(xiàn)圖片大小自適應(yīng))
private $maxCount;
//圖像相關(guān)設(shè)置
//畫布寬度
private $width;
//畫布高度
private $height;
//畫布背景顏色(RGB)
private $bg = array(
220, 220, 220
);
//節(jié)點(diǎn)顏色(搜索二叉樹和平衡二叉樹時(shí)用)
private $nodeColor = array(
255, 192, 203
);
//圖像句柄
private $image;
/**
* 構(gòu)造函數(shù),類屬性初始化
* @param $tree 傳遞一個(gè)樹的對象
* @return null
*/
public function __construct($tree)
{
$this->tree = $tree;
$this->level = $this->getLevel();
$this->maxCount = $this->GetMaxCount($this->level);
$this->width = ($this->rad * 2 * $this->maxCount) + $this->maxCount * $this->leaf_width;
$this->height = $this->level * ($this->rad * 2) + $this->level_high * ($this->level - 1) + $this->leave;
//1.創(chuàng)建畫布
$this->image = imagecreatetruecolor($this->width, $this->height); //新建一個(gè)真彩色圖像,默認(rèn)背景是黑色
//填充背景色
$bgcolor = imagecolorallocate($this->image, $this->bg[0], $this->bg[1], $this->bg[2]);
imagefill($this->image, 0, 0, $bgcolor);
}
/**
* 返回傳進(jìn)來的樹對象對應(yīng)的完全二叉樹中最底層葉子結(jié)點(diǎn)數(shù)量
* @param $level 樹的層數(shù)
* @return 結(jié)點(diǎn)數(shù)量
*/
function GetMaxCount($level)
{
return pow(2, $level - 1);
}
/**
* 獲取樹對象的層數(shù)
* @param null
* @return 樹的層數(shù)
*/
function getLevel()
{
return $this->tree->Depth();
}
/**
* 顯示二叉樹圖像
* @param null
* @return null
*/
public function show()
{
$this->draw($this->tree->root, 1, 0, 0);
header("Content-type:image/png");
imagepng($this->image);
imagedestroy($this->image);
}
/**
* (遞歸)畫出二叉樹的樹狀結(jié)構(gòu)
* @param $root,根節(jié)點(diǎn)(樹或子樹) $i,該根節(jié)點(diǎn)所處的層 $p_x,父節(jié)點(diǎn)的x坐標(biāo) $p_y,父節(jié)點(diǎn)的y坐標(biāo)
* @return null
*/
private function draw($root, $i, $p_x, $p_y)
{
if ($i <= $this->level) {
//當(dāng)前節(jié)點(diǎn)的y坐標(biāo)
$root_y = $i * $this->rad + ($i - 1) * $this->level_high;
//當(dāng)前節(jié)點(diǎn)的x坐標(biāo)
if (!is_null($parent = $root->parent)) {
if ($root == $parent->left) {
$root_x = $p_x - $this->width / (pow(2, $i));
} else {
$root_x = $p_x + $this->width / (pow(2, $i));
}
} else {
//根節(jié)點(diǎn)
$root_x = (1 / 2) * $this->width;
$root_y += $this->leave;
}
//畫結(jié)點(diǎn)(確定所畫節(jié)點(diǎn)的類型(平衡、紅黑、排序)和方法)
$method = 'draw' . get_class($this->tree) . 'Node';
$this->$method($root_x, $root_y, $root);
//將當(dāng)前節(jié)點(diǎn)和父節(jié)點(diǎn)連線(黑色線)
$black = imagecolorallocate($this->image, 0, 0, 0);
if (!is_null($parent = $root->parent)) {
imageline($this->image, $p_x, $p_y, $root_x, $root_y, $black);
}
//畫左子節(jié)點(diǎn)
if (!is_null($root->left)) {
$this->draw($root->left, $i + 1, $root_x, $root_y);
}
//畫右子節(jié)點(diǎn)
if (!is_null($root->right)) {
$this->draw($root->right, $i + 1, $root_x, $root_y);
}
}
}
/**
* 畫搜索二叉樹結(jié)點(diǎn)
* @param $x,當(dāng)前節(jié)點(diǎn)的x坐標(biāo) $y,當(dāng)前節(jié)點(diǎn)的y坐標(biāo) $node,當(dāng)前節(jié)點(diǎn)的引用
* @return null
*/
private function drawBstNode($x, $y, $node)
{
//節(jié)點(diǎn)圓的線顏色
$black = imagecolorallocate($this->image, 0, 0, 0);
$nodeColor = imagecolorallocate($this->image, $this->nodeColor[0], $this->nodeColor[1], $this->nodeColor[2]);
//畫節(jié)點(diǎn)圓
imageellipse($this->image, $x, $y, $this->rad * 2, $this->rad * 2, $black);
//節(jié)點(diǎn)圓顏色填充
imagefill($this->image, $x, $y, $nodeColor);
//節(jié)點(diǎn)對應(yīng)的數(shù)字
imagestring($this->image, 4, $x, $y, $node->key, $black);
}
/**
* 畫平衡二叉樹結(jié)點(diǎn)
* @param $x,當(dāng)前節(jié)點(diǎn)的x坐標(biāo) $y,當(dāng)前節(jié)點(diǎn)的y坐標(biāo) $node,當(dāng)前節(jié)點(diǎn)的引用
* @return null
*/
private function drawAvlNode($x, $y, $node)
{
$black = imagecolorallocate($this->image, 0, 0, 0);
$nodeColor = imagecolorallocate($this->image, $this->nodeColor[0], $this->nodeColor[1], $this->nodeColor[2]);
imageellipse($this->image, $x, $y, $this->rad * 2, $this->rad * 2, $black);
imagefill($this->image, $x, $y, $nodeColor);
imagestring($this->image, 4, $x, $y, $node->key . '(' . $node->bf . ')', $black);
}
/**
* 畫紅黑樹結(jié)點(diǎn)
* @param $x,當(dāng)前節(jié)點(diǎn)的x坐標(biāo) $y,當(dāng)前節(jié)點(diǎn)的y坐標(biāo) $node,當(dāng)前節(jié)點(diǎn)的引用
* @return null
*/
private function drawRbtNode($x, $y, $node)
{
$black = imagecolorallocate($this->image, 0, 0, 0);
$gray = imagecolorallocate($this->image, 180, 180, 180);
$pink = imagecolorallocate($this->image, 255, 192, 203);
imageellipse($this->image, $x, $y, $this->rad * 2, $this->rad * 2, $black);
if ($node->IsRed == TRUE) {
imagefill($this->image, $x, $y, $pink);
} else {
imagefill($this->image, $x, $y, $gray);
}
imagestring($this->image, 4, $x, $y, $node->key, $black);
}
}
好,現(xiàn)在我們來看看在客戶端如何調(diào)用:
client.php
class Client
{
public static function Main()
{
try {
//實(shí)現(xiàn)文件的自動(dòng)加載
function autoload($class)
{
include strtolower($class) . '.php';
}
spl_autoload_register('autoload');
$arr = array(62, 88, 58, 47, 35, 73, 51, 99, 37, 93);
// $tree = new Bst(); //搜索二叉樹
$tree = new Avl(); //平衡二叉樹
// $tree = new Rbt(); //紅黑樹
$tree->init($arr); //樹的初始化
// $tree->Delete(62);
// $tree->Insert(100);
// $tree->MidOrder(); //樹的中序遍歷(這也是調(diào)試的一個(gè)手段,看看數(shù)字是否從小到大排序)
$image = new image($tree);
$image->show(); //顯示圖像
} catch (Exception $e) {
echo $e->getMessage();
}
}
}
Client::Main();
這里用到的那三個(gè)樹的類如下:
二叉搜索樹bst.php:
<?php
/**
* author:zhongjin
* description: 二叉查找樹
*/
//結(jié)點(diǎn)
class Node
{
public $key;
public $parent;
public $left;
public $right;
public function __construct($key)
{
$this->key = $key;
$this->parent = NULL;
$this->left = NULL;
$this->right = NULL;
}
}
//二叉搜索樹
class Bst
{
public $root;
/**
* 初始化樹結(jié)構(gòu)
* @param $arr 初始化樹結(jié)構(gòu)的數(shù)組
* @return null
*/
public function init($arr)
{
$this->root = new Node($arr[0]);
for ($i = 1; $i < count($arr); $i++) {
$this->Insert($arr[$i]);
}
}
/**
* (對內(nèi))中序遍歷
* @param $root (樹或子樹的)根節(jié)點(diǎn)
* @return null
*/
private function mid_order($root)
{
if ($root != NULL) {
$this->mid_order($root->left);
echo $root->key . " ";
$this->mid_order($root->right);
}
}
/**
* (對外)中序遍歷
* @param null
* @return null
*/
public function MidOrder()
{
$this->mid_order($this->root);
}
/**
* 查找樹中是否存在$key對應(yīng)的節(jié)點(diǎn)
* @param $key 待搜索數(shù)字
* @return $key對應(yīng)的節(jié)點(diǎn)
*/
function search($key)
{
$current = $this->root;
while ($current != NULL) {
if ($current->key == $key) {
return $current;
} elseif ($current->key > $key) {
$current = $current->left;
} else {
$current = $current->right;
}
}
return $current;
}
/**
* 查找樹中的最小關(guān)鍵字
* @param $root 根節(jié)點(diǎn)
* @return 最小關(guān)鍵字對應(yīng)的節(jié)點(diǎn)
*/
function search_min($root)
{
$current = $root;
while ($current->left != NULL) {
$current = $current->left;
}
return $current;
}
/**
* 查找樹中的最大關(guān)鍵字
* @param $root 根節(jié)點(diǎn)
* @return 最大關(guān)鍵字對應(yīng)的節(jié)點(diǎn)
*/
function search_max($root)
{
$current = $root;
while ($current->right != NULL) {
$current = $current->right;
}
return $current;
}
/**
* 查找某個(gè)$key在中序遍歷時(shí)的直接前驅(qū)節(jié)點(diǎn)
* @param $x 待查找前驅(qū)節(jié)點(diǎn)的節(jié)點(diǎn)引用
* @return 前驅(qū)節(jié)點(diǎn)引用
*/
function predecessor($x)
{
//左子節(jié)點(diǎn)存在,直接返回左子節(jié)點(diǎn)的最右子節(jié)點(diǎn)
if ($x->left != NULL) {
return $this->search_max($x->left);
}
//否則查找其父節(jié)點(diǎn),直到當(dāng)前結(jié)點(diǎn)位于父節(jié)點(diǎn)的右邊
$p = $x->parent;
//如果x是p的左孩子,說明p是x的后繼,我們需要找的是p是x的前驅(qū)
while ($p != NULL && $x == $p->left) {
$x = $p;
$p = $p->parent;
}
return $p;
}
/**
* 查找某個(gè)$key在中序遍歷時(shí)的直接后繼節(jié)點(diǎn)
* @param $x 待查找后繼節(jié)點(diǎn)的節(jié)點(diǎn)引用
* @return 后繼節(jié)點(diǎn)引用
*/
function successor($x)
{
if ($x->right != NULL) {
return $this->search_min($x->right);
}
$p = $x->parent;
while ($p != NULL && $x == $p->right) {
$x = $p;
$p = $p->parent;
}
return $p;
}
/**
* 將$key插入樹中
* @param $key 待插入樹的數(shù)字
* @return null
*/
function Insert($key)
{
if (!is_null($this->search($key))) {
throw new Exception('結(jié)點(diǎn)' . $key . '已存在,不可插入!');
}
$root = $this->root;
$inode = new Node($key);
$current = $root;
$prenode = NULL;
//為$inode找到合適的插入位置
while ($current != NULL) {
$prenode = $current;
if ($current->key > $inode->key) {
$current = $current->left;
} else {
$current = $current->right;
}
}
$inode->parent = $prenode;
//如果$prenode == NULL, 則證明樹是空樹
if ($prenode == NULL) {
$this->root = $inode;
} else {
if ($inode->key < $prenode->key) {
$prenode->left = $inode;
} else {
$prenode->right = $inode;
}
}
//return $root;
}
/**
* 在樹中刪除$key對應(yīng)的節(jié)點(diǎn)
* @param $key 待刪除節(jié)點(diǎn)的數(shù)字
* @return null
*/
function Delete($key)
{
if (is_null($this->search($key))) {
throw new Exception('結(jié)點(diǎn)' . $key . "不存在,刪除失?。?);
}
$root = $this->root;
$dnode = $this->search($key);
if ($dnode->left == NULL || $dnode->right == NULL) { #如果待刪除結(jié)點(diǎn)無子節(jié)點(diǎn)或只有一個(gè)子節(jié)點(diǎn),則c = dnode
$c = $dnode;
} else { #如果待刪除結(jié)點(diǎn)有兩個(gè)子節(jié)點(diǎn),c置為dnode的直接后繼,以待最后將待刪除結(jié)點(diǎn)的值換為其后繼的值
$c = $this->successor($dnode);
}
//無論前面情況如何,到最后c只剩下一邊子結(jié)點(diǎn)
if ($c->left != NULL) {
$s = $c->left;
} else {
$s = $c->right;
}
if ($s != NULL) { #將c的子節(jié)點(diǎn)的父母結(jié)點(diǎn)置為c的父母結(jié)點(diǎn),此處c只可能有1個(gè)子節(jié)點(diǎn),因?yàn)槿绻鹀有兩個(gè)子節(jié)點(diǎn),則c不可能是dnode的直接后繼
$s->parent = $c->parent;
}
if ($c->parent == NULL) { #如果c的父母為空,說明c=dnode是根節(jié)點(diǎn),刪除根節(jié)點(diǎn)后直接將根節(jié)點(diǎn)置為根節(jié)點(diǎn)的子節(jié)點(diǎn),此處dnode是根節(jié)點(diǎn),且擁有兩個(gè)子節(jié)點(diǎn),則c是dnode的后繼結(jié)點(diǎn),c的父母就不會(huì)為空,就不會(huì)進(jìn)入這個(gè)if
$this->root = $s;
} else if ($c == $c->parent->left) { #如果c是其父節(jié)點(diǎn)的左右子節(jié)點(diǎn),則將c父母的左右子節(jié)點(diǎn)置為c的左右子節(jié)點(diǎn)
$c->parent->left = $s;
} else {
$c->parent->right = $s;
}
#如果c!=dnode,說明c是dnode的后繼結(jié)點(diǎn),交換c和dnode的key值
if ($c != $dnode) {
$dnode->key = $c->key;
}
#返回根節(jié)點(diǎn)
// return $root;
}
/**
* (對內(nèi))獲取樹的深度
* @param $root 根節(jié)點(diǎn)
* @return 樹的深度
*/
private function getdepth($root)
{
if ($root == NULL) {
return 0;
}
$dl = $this->getdepth($root->left);
$dr = $this->getdepth($root->right);
return ($dl > $dr ? $dl : $dr) + 1;
}
/**
* (對外)獲取樹的深度
* @param null
* @return null
*/
public function Depth()
{
return $this->getdepth($this->root);
}
}
?>
平衡二叉樹avl.php:
<?php
/**
* author:zhongjin
* description: 平衡二叉樹
*/
//結(jié)點(diǎn)
class Node
{
public $key;
public $parent;
public $left;
public $right;
public $bf; //平衡因子
public function __construct($key)
{
$this->key = $key;
$this->parent = NULL;
$this->left = NULL;
$this->right = NULL;
$this->bf = 0;
}
}
//平衡二叉樹
class Avl
{
public $root;
const LH = +1; //左高
const EH = 0; //等高
const RH = -1; //右高
/**
* 初始化樹結(jié)構(gòu)
* @param $arr 初始化樹結(jié)構(gòu)的數(shù)組
* @return null
*/
public function init($arr)
{
$this->root = new Node($arr[0]);
for ($i = 1; $i < count($arr); $i++) {
$this->Insert($arr[$i]);
}
}
/**
* (對內(nèi))中序遍歷
* @param $root (樹或子樹的)根節(jié)點(diǎn)
* @return null
*/
private function mid_order($root)
{
if ($root != NULL) {
$this->mid_order($root->left);
echo $root->key . "-" . $root->bf . " ";
$this->mid_order($root->right);
}
}
/**
* (對外)中序遍歷
* @param null
* @return null
*/
public function MidOrder()
{
$this->mid_order($this->root);
}
/**
* 將以$root為根節(jié)點(diǎn)的最小不平衡二叉樹做右旋處理
* @param $root(樹或子樹)根節(jié)點(diǎn)
* @return null
*/
private function R_Rotate($root)
{
$L = $root->left;
if (!is_NULL($root->parent)) {
$P = $root->parent;
if ($root == $P->left) {
$P->left = $L;
} else {
$P->right = $L;
}
$L->parent = $P;
} else {
$L->parent = NULL;
}
$root->parent = $L;
$root->left = $L->right;
$L->right = $root;
//這句必須啊!
if ($L->parent == NULL) {
$this->root = $L;
}
}
/**
* 將以$root為根節(jié)點(diǎn)的最小不平衡二叉樹做左旋處理
* @param $root(樹或子樹)根節(jié)點(diǎn)
* @return null
*/
private function L_Rotate($root)
{
$R = $root->right;
if (!is_NULL($root->parent)) {
$P = $root->parent;
if ($root == $P->left) {
$P->left = $R;
} else {
$P->right = $R;
}
$R->parent = $P;
} else {
$R->parent = NULL;
}
$root->parent = $R;
$root->right = $R->left;
$R->left = $root;
//這句必須?。?
if ($R->parent == NULL) {
$this->root = $R;
}
}
/**
* 對以$root所指結(jié)點(diǎn)為根節(jié)點(diǎn)的二叉樹作左平衡處理
* @param $root(樹或子樹)根節(jié)點(diǎn)
* @return null
*/
public function LeftBalance($root)
{
$L = $root->left;
$L_bf = $L->bf;
switch ($L_bf) {
//檢查root的左子樹的平衡度,并作相應(yīng)的平衡處理
case self::LH: //新結(jié)點(diǎn)插入在root的左孩子的左子樹上,要做單右旋處理
$root->bf = $L->bf = self::EH;
$this->R_Rotate($root);
break;
case self::RH: //新節(jié)點(diǎn)插入在root的左孩子的右子樹上,要做雙旋處理
$L_r = $L->right; //root左孩子的右子樹根
$L_r_bf = $L_r->bf;
//修改root及其左孩子的平衡因子
switch ($L_r_bf) {
case self::LH:
$root->bf = self::RH;
$L->bf = self::EH;
break;
case self::EH:
$root->bf = $L->bf = self::EH;
break;
case self::RH:
$root->bf = self::EH;
$L->bf = self::LH;
break;
}
$L_r->bf = self::EH;
//對root的左子樹作左平衡處理
$this->L_Rotate($L);
//對root作右平衡處理
$this->R_Rotate($root);
}
}
/**
* 對以$root所指結(jié)點(diǎn)為根節(jié)點(diǎn)的二叉樹作右平衡處理
* @param $root(樹或子樹)根節(jié)點(diǎn)
* @return null
*/
public function RightBalance($root)
{
$R = $root->right;
$R_bf = $R->bf;
switch ($R_bf) {
//檢查root的右子樹的平衡度,并作相應(yīng)的平衡處理
case self::RH: //新結(jié)點(diǎn)插入在root的右孩子的右子樹上,要做單左旋處理
$root->bf = $R->bf = self::EH;
$this->L_Rotate($root);
break;
case self::LH: //新節(jié)點(diǎn)插入在root的右孩子的左子樹上,要做雙旋處理
$R_l = $R->left; //root右孩子的左子樹根
$R_l_bf = $R_l->bf;
//修改root及其右孩子的平衡因子
switch ($R_l_bf) {
case self::RH:
$root->bf = self::LH;
$R->bf = self::EH;
break;
case self::EH:
$root->bf = $R->bf = self::EH;
break;
case self::LH:
$root->bf = self::EH;
$R->bf = self::RH;
break;
}
$R_l->bf = self::EH;
//對root的右子樹作右平衡處理
$this->R_Rotate($R);
//對root作左平衡處理
$this->L_Rotate($root);
}
}
/**
* 查找樹中是否存在$key對應(yīng)的節(jié)點(diǎn)
* @param $key 待搜索數(shù)字
* @return $key對應(yīng)的節(jié)點(diǎn)
*/
public function search($key)
{
$current = $this->root;
while ($current != NULL) {
if ($current->key == $key) {
return $current;
} elseif ($current->key > $key) {
$current = $current->left;
} else {
$current = $current->right;
}
}
return $current;
}
/**
* 查找樹中的最小關(guān)鍵字
* @param $root 根節(jié)點(diǎn)
* @return 最小關(guān)鍵字對應(yīng)的節(jié)點(diǎn)
*/
function search_min($root)
{
$current = $root;
while ($current->left != NULL) {
$current = $current->left;
}
return $current;
}
/**
* 查找樹中的最大關(guān)鍵字
* @param $root 根節(jié)點(diǎn)
* @return 最大關(guān)鍵字對應(yīng)的節(jié)點(diǎn)
*/
function search_max($root)
{
$current = $root;
while ($current->right != NULL) {
$current = $current->right;
}
return $current;
}
/**
* 查找某個(gè)$key在中序遍歷時(shí)的直接前驅(qū)節(jié)點(diǎn)
* @param $x 待查找前驅(qū)節(jié)點(diǎn)的節(jié)點(diǎn)引用
* @return 前驅(qū)節(jié)點(diǎn)引用
*/
private function predecessor($x)
{
//左子節(jié)點(diǎn)存在,直接返回左子節(jié)點(diǎn)的最右子節(jié)點(diǎn)
if ($x->left != NULL) {
return $this->search_max($x->left);
}
//否則查找其父節(jié)點(diǎn),直到當(dāng)前結(jié)點(diǎn)位于父節(jié)點(diǎn)的右邊
$p = $x->parent;
//如果x是p的左孩子,說明p是x的后繼,我們需要找的是p是x的前驅(qū)
while ($p != NULL && $x == $p->left) {
$x = $p;
$p = $p->parent;
}
return $p;
}
/**
* 查找某個(gè)$key在中序遍歷時(shí)的直接后繼節(jié)點(diǎn)
* @param $x 待查找后繼節(jié)點(diǎn)的節(jié)點(diǎn)引用
* @return 后繼節(jié)點(diǎn)引用
*/
private function successor($x)
{
if ($x->left != NULL) {
return $this->search_min($x->right);
}
$p = $x->parent;
while ($p != NULL && $x == $p->right) {
$x = $p;
$p = $p->parent;
}
return $p;
}
/**
* (對內(nèi))插入結(jié)點(diǎn),如果結(jié)點(diǎn)不存在則插入,失去平衡要做平衡處理
* @param $root 根節(jié)點(diǎn) $key 待插入樹的數(shù)字
* @return null
*/
private function insert_node(&$root, $key)
{
//找到了插入的位置,插入新節(jié)點(diǎn)
if (is_null($root)) {
$root = new Node($key);
//插入結(jié)點(diǎn)成功
return TRUE;
} else {
//在樹中已經(jīng)存在和$key相等的結(jié)點(diǎn)
if ($key == $root->key) {
//插入節(jié)點(diǎn)失敗
return FALSE;
} //在root的左子樹中繼續(xù)搜索
elseif ($key < $root->key) {
//插入左子樹失敗
if (!($this->insert_node($root->left, $key))) {
//樹未長高
return FALSE;
}
//成功插入,修改平衡因子
if (is_null($root->left->parent)) {
$root->left->parent = $root;
}
switch ($root->bf) {
//原來左右子樹等高,現(xiàn)在左子樹增高而樹增高
case self::EH:
$root->bf = self::LH;
//樹長高
return TRUE;
break;
//原來左子樹比右子樹高,需要做左平衡處理
case self::LH:
$this->LeftBalance($root);
//平衡后,樹并未長高
return FALSE;
break;
//原來右子樹比左子樹高,現(xiàn)在左右子樹等高
case self::RH:
$root->bf = self::EH;
//樹并未長高
return FALSE;
break;
}
} //在root的右子樹中繼續(xù)搜索
else {
//插入右子樹失敗
if (!$this->insert_node($root->right, $key)) {
//樹未長高
return FALSE;
}
//成功插入,修改平衡因子
if (is_null($root->right->parent)) {
$root->right->parent = $root;
}
switch ($root->bf) {
//原來左右子樹等高,現(xiàn)在右子樹增高而樹增高
case self::EH:
$root->bf = self::RH;
//樹長高
return TRUE;
break;
//原來左子樹比右子樹高,現(xiàn)在左右子樹等高
case self::LH:
$root->bf = self::EH;
return FALSE;
break;
//原來右子樹比左子樹高,要做右平衡處理
case self::RH:
$this->RightBalance($root);
//樹并未長高
return FALSE;
break;
}
}
}
}
/**
* (對外)將$key插入樹中
* @param $key 待插入樹的數(shù)字
* @return null
*/
public function Insert($key)
{
$this->insert_node($this->root, $key);
}
/**
* 獲取待刪除的節(jié)點(diǎn)(刪除的最終節(jié)點(diǎn))
* @param $key 待刪除的數(shù)字
* @return 最終被刪除的節(jié)點(diǎn)
*/
private function get_del_node($key)
{
$dnode = $this->search($key);
if ($dnode == NULL) {
throw new Exception("結(jié)點(diǎn)不存在!");
return;
}
if ($dnode->left == NULL || $dnode->right == NULL) { #如果待刪除結(jié)點(diǎn)無子節(jié)點(diǎn)或只有一個(gè)子節(jié)點(diǎn),則c = dnode
$c = $dnode;
} else { #如果待刪除結(jié)點(diǎn)有兩個(gè)子節(jié)點(diǎn),c置為dnode的直接后繼,以待最后將待刪除結(jié)點(diǎn)的值換為其后繼的值
$c = $this->successor($dnode);
}
$dnode->key = $c->key;
return $c;
}
/**
* (對內(nèi))刪除指定節(jié)點(diǎn),處理該結(jié)點(diǎn)往上結(jié)點(diǎn)的平衡因子
* @param $node 最終該被刪除的節(jié)點(diǎn)
* @return null
*/
private function del_node($node)
{
if ($node == $this->root) {
$this->root = NULL;
return;
}
$current = $node;
//現(xiàn)在的node只有兩種情況,要么只有一個(gè)子節(jié)點(diǎn),要么沒有子節(jié)點(diǎn)
$P = $current->parent;
//刪除一個(gè)結(jié)點(diǎn),第一個(gè)父節(jié)點(diǎn)的平衡都肯定會(huì)發(fā)生變化
$lower = TRUE;
while ($lower == TRUE && !is_null($P)) {
//待刪除結(jié)點(diǎn)是左節(jié)點(diǎn)
if ($current == $P->left) {
if($current == $node){
if (!is_null($current->left)) {
$P->left = $current->left;
} else {
$P->left = $current->left;
}
}
$P_bf = $P->bf;
switch ($P_bf) {
case self::LH:
$P->bf = self::EH;
$lower = TRUE;
$current = $P;
$P = $current->parent;
break;
case self::EH:
$P->bf = self::RH;
$lower = FALSE;
break;
case self::RH:
$this->RightBalance($P);
$lower = TRUE;
$current = $P->parent;
$P = $current->parent;
break;
}
} //右結(jié)點(diǎn)
else {
if($current == $node){
if (!is_null($current->left)) {
$P->right = $current->left;
} else {
$P->right = $current->left;
}
}
$P_bf = $P->bf;
switch ($P_bf) {
case self::LH:
$this->LeftBalance($P);
$lower = TRUE;
$current = $P->parent;
$P = $current->parent;
break;
case self::EH:
$P->bf = self::LH;
$lower = FALSE;
break;
case self::RH:
$P->bf = self::LH;
$lower = TRUE;
$current = $P;
$P = $current->parent;
break;
}
}
}
}
/**
* (對外)刪除指定節(jié)點(diǎn)
* @param $key 刪除節(jié)點(diǎn)的key值
* @return null
*/
public function Delete($key)
{
$del_node = $this->get_del_node($key);
$this->del_node($del_node);
}
/**
* (對內(nèi))獲取樹的深度
* @param $root 根節(jié)點(diǎn)
* @return 樹的深度
*/
private function getdepth($root)
{
if ($root == NULL) {
return 0;
}
$dl = $this->getdepth($root->left);
$dr = $this->getdepth($root->right);
return ($dl > $dr ? $dl : $dr) + 1;
}
/**
* (對外)獲取樹的深度
* @param null
* @return null
*/
public function Depth()
{
return $this->getdepth($this->root);
}
}
?>
紅黑樹rbt.php:
<?php
/**
* author:zhongjin
* description: 紅黑樹
*/
//結(jié)點(diǎn)
class Node
{
public $key;
public $parent;
public $left;
public $right;
public $IsRed; //分辨紅節(jié)點(diǎn)或黑節(jié)點(diǎn)
public function __construct($key, $IsRed = TRUE)
{
$this->key = $key;
$this->parent = NULL;
$this->left = NULL;
$this->right = NULL;
//插入結(jié)點(diǎn)默認(rèn)是紅色
$this->IsRed = $IsRed;
}
}
//紅黑樹
class Rbt
{
public $root;
/**
* 初始化樹結(jié)構(gòu)
* @param $arr 初始化樹結(jié)構(gòu)的數(shù)組
* @return null
*/
public function init($arr)
{
//根節(jié)點(diǎn)必須是黑色
$this->root = new Node($arr[0], FALSE);
for ($i = 1; $i < count($arr); $i++) {
$this->Insert($arr[$i]);
}
}
/**
* (對內(nèi))中序遍歷
* @param $root (樹或子樹的)根節(jié)點(diǎn)
* @return null
*/
private function mid_order($root)
{
if ($root != NULL) {
$this->mid_order($root->left);
echo $root->key . "-" . ($root->IsRed ? 'r' : 'b') . ' ';
$this->mid_order($root->right);
}
}
/**
* (對外)中序遍歷
* @param null
* @return null
*/
public function MidOrder()
{
$this->mid_order($this->root);
}
/**
* 查找樹中是否存在$key對應(yīng)的節(jié)點(diǎn)
* @param $key 待搜索數(shù)字
* @return $key對應(yīng)的節(jié)點(diǎn)
*/
function search($key)
{
$current = $this->root;
while ($current != NULL) {
if ($current->key == $key) {
return $current;
} elseif ($current->key > $key) {
$current = $current->left;
} else {
$current = $current->right;
}
}
//結(jié)點(diǎn)不存在
return $current;
}
/**
* 將以$root為根節(jié)點(diǎn)的最小不平衡二叉樹做右旋處理
* @param $root(樹或子樹)根節(jié)點(diǎn)
* @return null
*/
private function R_Rotate($root)
{
$L = $root->left;
if (!is_null($root->parent)) {
$P = $root->parent;
if($root == $P->left){
$P->left = $L;
}else{
$P->right = $L;
}
$L->parent = $P;
} else {
$L->parent = NULL;
}
$root->parent = $L;
$root->left = $L->right;
$L->right = $root;
//這句必須啊!
if ($L->parent == NULL) {
$this->root = $L;
}
}
/**
* 將以$root為根節(jié)點(diǎn)的最小不平衡二叉樹做左旋處理
* @param $root(樹或子樹)根節(jié)點(diǎn)
* @return null
*/
private function L_Rotate($root)
{
$R = $root->right;
if (!is_null($root->parent)) {
$P = $root->parent;
if($root == $P->right){
$P->right = $R;
}else{
$P->left = $R;
}
$R->parent = $P;
} else {
$R->parent = NULL;
}
$root->parent = $R;
$root->right = $R->left;
$R->left = $root;
//這句必須??!
if ($R->parent == NULL) {
$this->root = $R;
}
}
/**
* 查找樹中的最小關(guān)鍵字
* @param $root 根節(jié)點(diǎn)
* @return 最小關(guān)鍵字對應(yīng)的節(jié)點(diǎn)
*/
function search_min($root)
{
$current = $root;
while ($current->left != NULL) {
$current = $current->left;
}
return $current;
}
/**
* 查找樹中的最大關(guān)鍵字
* @param $root 根節(jié)點(diǎn)
* @return 最大關(guān)鍵字對應(yīng)的節(jié)點(diǎn)
*/
function search_max($root)
{
$current = $root;
while ($current->right != NULL) {
$current = $current->right;
}
return $current;
}
/**
* 查找某個(gè)$key在中序遍歷時(shí)的直接前驅(qū)節(jié)點(diǎn)
* @param $x 待查找前驅(qū)節(jié)點(diǎn)的節(jié)點(diǎn)引用
* @return 前驅(qū)節(jié)點(diǎn)引用
*/
function predecessor($x)
{
//左子節(jié)點(diǎn)存在,直接返回左子節(jié)點(diǎn)的最右子節(jié)點(diǎn)
if ($x->left != NULL) {
return $this->search_max($x->left);
}
//否則查找其父節(jié)點(diǎn),直到當(dāng)前結(jié)點(diǎn)位于父節(jié)點(diǎn)的右邊
$p = $x->parent;
//如果x是p的左孩子,說明p是x的后繼,我們需要找的是p是x的前驅(qū)
while ($p != NULL && $x == $p->left) {
$x = $p;
$p = $p->parent;
}
return $p;
}
/**
* 查找某個(gè)$key在中序遍歷時(shí)的直接后繼節(jié)點(diǎn)
* @param $x 待查找后繼節(jié)點(diǎn)的節(jié)點(diǎn)引用
* @return 后繼節(jié)點(diǎn)引用
*/
function successor($x)
{
if ($x->left != NULL) {
return $this->search_min($x->right);
}
$p = $x->parent;
while ($p != NULL && $x == $p->right) {
$x = $p;
$p = $p->parent;
}
return $p;
}
/**
* 將$key插入樹中
* @param $key 待插入樹的數(shù)字
* @return null
*/
public function Insert($key)
{
if (!is_null($this->search($key))) {
throw new Exception('結(jié)點(diǎn)' . $key . '已存在,不可插入!');
}
$root = $this->root;
$inode = new Node($key);
$current = $root;
$prenode = NULL;
//為$inode找到合適的插入位置
while ($current != NULL) {
$prenode = $current;
if ($current->key > $inode->key) {
$current = $current->left;
} else {
$current = $current->right;
}
}
$inode->parent = $prenode;
//如果$prenode == NULL, 則證明樹是空樹
if ($prenode == NULL) {
$this->root = $inode;
} else {
if ($inode->key < $prenode->key) {
$prenode->left = $inode;
} else {
$prenode->right = $inode;
}
}
//將它重新修正為一顆紅黑樹
$this->InsertFixUp($inode);
}
/**
* 對插入節(jié)點(diǎn)的位置及往上的位置進(jìn)行顏色調(diào)整
* @param $inode 插入的節(jié)點(diǎn)
* @return null
*/
private function InsertFixUp($inode)
{
//情況一:需要調(diào)整條件,父節(jié)點(diǎn)存在且父節(jié)點(diǎn)的顏色是紅色
while (($parent = $inode->parent) != NULL && $parent->IsRed == TRUE) {
//祖父結(jié)點(diǎn):
$gparent = $parent->parent;
//如果父節(jié)點(diǎn)是祖父結(jié)點(diǎn)的左子結(jié)點(diǎn),下面的else與此相反
if ($parent == $gparent->left) {
//叔叔結(jié)點(diǎn)
$uncle = $gparent->right;
//case1:叔叔結(jié)點(diǎn)也是紅色
if ($uncle != NULL && $uncle->IsRed == TRUE) {
//將父節(jié)點(diǎn)和叔叔結(jié)點(diǎn)都涂黑,將祖父結(jié)點(diǎn)涂紅
$parent->IsRed = FALSE;
$uncle->IsRed = FALSE;
$gparent->IsRed = TRUE;
//將新節(jié)點(diǎn)指向祖父節(jié)點(diǎn)(現(xiàn)在祖父結(jié)點(diǎn)變紅,可以看作新節(jié)點(diǎn)存在)
$inode = $gparent;
//繼續(xù)while循環(huán),重新判斷
continue; //經(jīng)過這一步之后,組父節(jié)點(diǎn)作為新節(jié)點(diǎn)存在(跳到case2)
}
//case2:叔叔結(jié)點(diǎn)是黑色,且當(dāng)前結(jié)點(diǎn)是右子節(jié)點(diǎn)
if ($inode == $parent->right) {
//以父節(jié)點(diǎn)作為旋轉(zhuǎn)結(jié)點(diǎn)做左旋轉(zhuǎn)處理
$this->L_Rotate($parent);
//在樹中實(shí)際上已經(jīng)轉(zhuǎn)換,但是這里的變量的指向還沒交換,
//將父節(jié)點(diǎn)和字節(jié)調(diào)換一下,為下面右旋做準(zhǔn)備
$temp = $parent;
$parent = $inode;
$inode = $temp;
}
//case3:叔叔結(jié)點(diǎn)是黑色,而且當(dāng)前結(jié)點(diǎn)是父節(jié)點(diǎn)的左子節(jié)點(diǎn)
$parent->IsRed = FALSE;
$gparent->IsRed = TRUE;
$this->R_Rotate($gparent);
} //如果父節(jié)點(diǎn)是祖父結(jié)點(diǎn)的右子結(jié)點(diǎn),與上面完全相反
else {
//叔叔結(jié)點(diǎn)
$uncle = $gparent->left;
//case1:叔叔結(jié)點(diǎn)也是紅色
if ($uncle != NULL && $uncle->IsRed == TRUE) {
//將父節(jié)點(diǎn)和叔叔結(jié)點(diǎn)都涂黑,將祖父結(jié)點(diǎn)涂紅
$parent->IsRed = FALSE;
$uncle->IsRed = FALSE;
$gparent->IsRed = TRUE;
//將新節(jié)點(diǎn)指向祖父節(jié)點(diǎn)(現(xiàn)在祖父結(jié)點(diǎn)變紅,可以看作新節(jié)點(diǎn)存在)
$inode = $gparent;
//繼續(xù)while循環(huán),重新判斷
continue; //經(jīng)過這一步之后,組父節(jié)點(diǎn)作為新節(jié)點(diǎn)存在(跳到case2)
}
//case2:叔叔結(jié)點(diǎn)是黑色,且當(dāng)前結(jié)點(diǎn)是左子節(jié)點(diǎn)
if ($inode == $parent->left) {
//以父節(jié)點(diǎn)作為旋轉(zhuǎn)結(jié)點(diǎn)做右旋轉(zhuǎn)處理
$this->R_Rotate($parent);
//在樹中實(shí)際上已經(jīng)轉(zhuǎn)換,但是這里的變量的指向還沒交換,
//將父節(jié)點(diǎn)和字節(jié)調(diào)換一下,為下面右旋做準(zhǔn)備
$temp = $parent;
$parent = $inode;
$inode = $temp;
}
//case3:叔叔結(jié)點(diǎn)是黑色,而且當(dāng)前結(jié)點(diǎn)是父節(jié)點(diǎn)的右子節(jié)點(diǎn)
$parent->IsRed = FALSE;
$gparent->IsRed = TRUE;
$this->L_Rotate($gparent);
}
}
//情況二:原樹是根節(jié)點(diǎn)(父節(jié)點(diǎn)為空),則只需將根節(jié)點(diǎn)涂黑
if ($inode == $this->root) {
$this->root->IsRed = FALSE;
return;
}
//情況三:插入節(jié)點(diǎn)的父節(jié)點(diǎn)是黑色,則什么也不用做
if ($inode->parent != NULL && $inode->parent->IsRed == FALSE) {
return;
}
}
/**
* (對外)刪除指定節(jié)點(diǎn)
* @param $key 刪除節(jié)點(diǎn)的key值
* @return null
*/
function Delete($key)
{
if (is_null($this->search($key))) {
throw new Exception('結(jié)點(diǎn)' . $key . "不存在,刪除失敗!");
}
$dnode = $this->search($key);
if ($dnode->left == NULL || $dnode->right == NULL) { #如果待刪除結(jié)點(diǎn)無子節(jié)點(diǎn)或只有一個(gè)子節(jié)點(diǎn),則c = dnode
$c = $dnode;
} else { #如果待刪除結(jié)點(diǎn)有兩個(gè)子節(jié)點(diǎn),c置為dnode的直接后繼,以待最后將待刪除結(jié)點(diǎn)的值換為其后繼的值
$c = $this->successor($dnode);
}
//為了后面顏色處理做準(zhǔn)備
$parent = $c->parent;
//無論前面情況如何,到最后c只剩下一邊子結(jié)點(diǎn)
if ($c->left != NULL) { //這里不會(huì)出現(xiàn),除非選擇的是刪除結(jié)點(diǎn)的前驅(qū)
$s = $c->left;
} else {
$s = $c->right;
}
if ($s != NULL) { #將c的子節(jié)點(diǎn)的父母結(jié)點(diǎn)置為c的父母結(jié)點(diǎn),此處c只可能有1個(gè)子節(jié)點(diǎn),因?yàn)槿绻鹀有兩個(gè)子節(jié)點(diǎn),則c不可能是dnode的直接后繼
$s->parent = $c->parent;
}
if ($c->parent == NULL) { #如果c的父母為空,說明c=dnode是根節(jié)點(diǎn),刪除根節(jié)點(diǎn)后直接將根節(jié)點(diǎn)置為根節(jié)點(diǎn)的子節(jié)點(diǎn),此處dnode是根節(jié)點(diǎn),且擁有兩個(gè)子節(jié)點(diǎn),則c是dnode的后繼結(jié)點(diǎn),c的父母就不會(huì)為空,就不會(huì)進(jìn)入這個(gè)if
$this->root = $s;
} else if ($c == $c->parent->left) { #如果c是其父節(jié)點(diǎn)的左右子節(jié)點(diǎn),則將c父母的左右子節(jié)點(diǎn)置為c的左右子節(jié)點(diǎn)
$c->parent->left = $s;
} else {
$c->parent->right = $s;
}
$dnode->key = $c->key;
$node = $s;
//c的結(jié)點(diǎn)顏色是黑色,那么會(huì)影響路徑上的黑色結(jié)點(diǎn)的數(shù)量,必須進(jìn)行調(diào)整
if ($c->IsRed == FALSE) {
$this->DeleteFixUp($node,$parent);
}
}
/**
* 刪除節(jié)點(diǎn)后對接點(diǎn)周圍的其他節(jié)點(diǎn)進(jìn)行調(diào)整
* @param $key 刪除節(jié)點(diǎn)的子節(jié)點(diǎn)和父節(jié)點(diǎn)
* @return null
*/
private function DeleteFixUp($node,$parent)
{
//如果待刪結(jié)點(diǎn)的子節(jié)點(diǎn)為紅色,直接將子節(jié)點(diǎn)涂黑
if ($node != NULL && $node->IsRed == TRUE) {
$node->IsRed = FALSE;
return;
}
//如果是根節(jié)點(diǎn),那就直接將根節(jié)點(diǎn)置為黑色即可
while (($node == NULL || $node->IsRed == FALSE) && ($node != $this->root)) {
//node是父節(jié)點(diǎn)的左子節(jié)點(diǎn),下面else與這里相反
if ($node == $parent->left) {
$brother = $parent->right;
//case1:兄弟結(jié)點(diǎn)顏色是紅色(父節(jié)點(diǎn)和兄弟孩子結(jié)點(diǎn)都是黑色)
//將父節(jié)點(diǎn)涂紅,將兄弟結(jié)點(diǎn)涂黑,然后對父節(jié)點(diǎn)進(jìn)行左旋處理(經(jīng)過這一步,情況轉(zhuǎn)換為兄弟結(jié)點(diǎn)顏色為黑色的情況)
if ($brother->IsRed == TRUE) {
$brother->IsRed = FALSE;
$parent->IsRed = TRUE;
$this->L_Rotate($parent);
//將情況轉(zhuǎn)化為其他的情況
$brother = $parent->right; //在左旋處理后,$parent->right指向的是原來兄弟結(jié)點(diǎn)的左子節(jié)點(diǎn)
}
//以下是兄弟結(jié)點(diǎn)為黑色的情況
//case2:兄弟結(jié)點(diǎn)是黑色,且兄弟結(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色
//將兄弟結(jié)點(diǎn)涂紅,將當(dāng)前結(jié)點(diǎn)指向其父節(jié)點(diǎn),將其父節(jié)點(diǎn)指向當(dāng)前結(jié)點(diǎn)的祖父結(jié)點(diǎn)。
if (($brother->left == NULL || $brother->left->IsRed == FALSE) && ($brother->right == NULL || $brother->right->IsRed == FALSE)) {
$brother->IsRed = TRUE;
$node = $parent;
$parent = $node->parent;
} else {
//case3:兄弟結(jié)點(diǎn)是黑色,兄弟結(jié)點(diǎn)的左子節(jié)點(diǎn)是紅色,右子節(jié)點(diǎn)為黑色
//將兄弟結(jié)點(diǎn)涂紅,將兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)涂黑,然后對兄弟結(jié)點(diǎn)做右旋處理(經(jīng)過這一步,情況轉(zhuǎn)換為兄弟結(jié)點(diǎn)顏色為黑色,右子節(jié)點(diǎn)為紅色的情況)
if ($brother->right == NULL || $brother->right->IsRed == FALSE) {
$brother->IsRed = TRUE;
$brother->left->IsRed = FALSE;
$this->R_Rotate($brother);
//將情況轉(zhuǎn)換為其他情況
$brother = $parent->right;
}
//case4:兄弟結(jié)點(diǎn)是黑色,且兄弟結(jié)點(diǎn)的右子節(jié)點(diǎn)為紅色,左子節(jié)點(diǎn)為任意顏色
//將兄弟節(jié)點(diǎn)涂成父節(jié)點(diǎn)的顏色,再把父節(jié)點(diǎn)涂黑,將兄弟結(jié)點(diǎn)的右子節(jié)點(diǎn)涂黑,然后對父節(jié)點(diǎn)做左旋處理
$brother->IsRed = $parent->IsRed;
$parent->IsRed = FALSE;
$brother->right->IsRed = FALSE;
$this->L_Rotate($parent);
//到了第四種情況,已經(jīng)是最基本的情況了,可以直接退出了
$node = $this->root;
break;
}
} //node是父節(jié)點(diǎn)的右子節(jié)點(diǎn)
else {
$brother = $parent->left;
//case1:兄弟結(jié)點(diǎn)顏色是紅色(父節(jié)點(diǎn)和兄弟孩子結(jié)點(diǎn)都是黑色)
//將父節(jié)點(diǎn)涂紅,將兄弟結(jié)點(diǎn)涂黑,然后對父節(jié)點(diǎn)進(jìn)行右旋處理(經(jīng)過這一步,情況轉(zhuǎn)換為兄弟結(jié)點(diǎn)顏色為黑色的情況)
if ($brother->IsRed == TRUE) {
$brother->IsRed = FALSE;
$parent->IsRed = TRUE;
$this->R_Rotate($parent);
//將情況轉(zhuǎn)化為其他的情況
$brother = $parent->left; //在右旋處理后,$parent->left指向的是原來兄弟結(jié)點(diǎn)的右子節(jié)點(diǎn)
}
//以下是兄弟結(jié)點(diǎn)為黑色的情況
//case2:兄弟結(jié)點(diǎn)是黑色,且兄弟結(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色
//將兄弟結(jié)點(diǎn)涂紅,將當(dāng)前結(jié)點(diǎn)指向其父節(jié)點(diǎn),將其父節(jié)點(diǎn)指向當(dāng)前結(jié)點(diǎn)的祖父結(jié)點(diǎn)。
if (($brother->left == NULL || $brother->left->IsRed == FALSE) && ($brother->right == NULL || $brother->right->IsRed == FALSE)) {
$brother->IsRed = TRUE;
$node = $parent;
$parent = $node->parent;
} else {
//case3:兄弟結(jié)點(diǎn)是黑色,兄弟結(jié)點(diǎn)的右子節(jié)點(diǎn)是紅色,左子節(jié)點(diǎn)為黑色
//將兄弟結(jié)點(diǎn)涂紅,將兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)涂黑,然后對兄弟結(jié)點(diǎn)做左旋處理(經(jīng)過這一步,情況轉(zhuǎn)換為兄弟結(jié)點(diǎn)顏色為黑色,右子節(jié)點(diǎn)為紅色的情況)
if ($brother->left == NULL || $brother->left->IsRed == FALSE) {
$brother->IsRed = TRUE;
$brother->right = FALSE;
$this->L_Rotate($brother);
//將情況轉(zhuǎn)換為其他情況
$brother = $parent->left;
}
//case4:兄弟結(jié)點(diǎn)是黑色,且兄弟結(jié)點(diǎn)的左子節(jié)點(diǎn)為紅色,右子節(jié)點(diǎn)為任意顏色
//將兄弟節(jié)點(diǎn)涂成父節(jié)點(diǎn)的顏色,再把父節(jié)點(diǎn)涂黑,將兄弟結(jié)點(diǎn)的右子節(jié)點(diǎn)涂黑,然后對父節(jié)點(diǎn)左左旋處理
$brother->IsRed = $parent->IsRed;
$parent->IsRed = FALSE;
$brother->left->IsRed = FALSE;
$this->R_Rotate($parent);
$node = $this->root;
break;
}
}
}
if ($node != NULL) {
$this->root->IsRed = FALSE;
}
}
/**
* (對內(nèi))獲取樹的深度
* @param $root 根節(jié)點(diǎn)
* @return 樹的深度
*/
private function getdepth($root)
{
if ($root == NULL) {
return 0;
}
$dl = $this->getdepth($root->left);
$dr = $this->getdepth($root->right);
return ($dl > $dr ? $dl : $dr) + 1;
}
/**
* (對外)獲取樹的深度
* @param null
* @return null
*/
public function Depth()
{
return $this->getdepth($this->root);
}
}
?>
更多關(guān)于PHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《PHP數(shù)據(jù)結(jié)構(gòu)與算法教程》、《php程序設(shè)計(jì)算法總結(jié)》、《php字符串(string)用法總結(jié)》、《PHP數(shù)組(Array)操作技巧大全》、《PHP常用遍歷算法與技巧總結(jié)》及《PHP數(shù)學(xué)運(yùn)算技巧總結(jié)》
希望本文所述對大家PHP程序設(shè)計(jì)有所幫助。
相關(guān)文章
PHP遞歸復(fù)制、移動(dòng)目錄的自定義函數(shù)分享
這篇文章主要介紹了PHP遞歸復(fù)制、移動(dòng)目錄的自定義函數(shù)分享,本文的特點(diǎn)是對每一句代碼都做詳盡的注釋,需要的朋友可以參考下2014-11-11
php實(shí)現(xiàn)的替換敏感字符串類實(shí)例
這篇文章主要介紹了php實(shí)現(xiàn)的替換敏感字符串類,包括了常見的非法字符串檢測、白名單、黑名單及字符替換等功能,非常實(shí)用,需要的朋友可以參考下2014-09-09
PHP計(jì)算當(dāng)前坐標(biāo)3公里內(nèi)4個(gè)角落的最大最小經(jīng)緯度實(shí)例
這篇文章主要介紹了PHP計(jì)算當(dāng)前坐標(biāo)3公里內(nèi)4個(gè)角落的最大最小經(jīng)緯度的方法,涉及PHP數(shù)學(xué)運(yùn)算的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-02-02
POSIX 風(fēng)格和兼容 Perl 風(fēng)格兩種正則表達(dá)式主要函數(shù)的類比(preg_match, preg_replace,
POSIX 風(fēng)格和兼容 Perl 風(fēng)格兩種正則表達(dá)式主要函數(shù)的類比(preg_match, preg_replace, ereg, ereg_replace) ,需要的朋友可以參考下。2010-10-10
PHP中通過fopen()函數(shù)訪問遠(yuǎn)程文件示例
這篇文章主要介紹了PHP中通過fopen()函數(shù)訪問遠(yuǎn)程文件示例,本文講解了fopen函數(shù)的作用、使用它需要的配置問題、超時(shí)問題等內(nèi)容,并給出了代碼實(shí)例,需要的朋友可以參考下2014-11-11
PHP+ajax實(shí)現(xiàn)二級聯(lián)動(dòng)菜單功能示例
這篇文章主要介紹了PHP+ajax實(shí)現(xiàn)二級聯(lián)動(dòng)菜單功能,涉及php結(jié)合ajax的數(shù)據(jù)交互與頁面元素動(dòng)態(tài)操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-08-08
php驗(yàn)證碼的制作思路和實(shí)現(xiàn)方法
這篇文章主要介紹了php驗(yàn)證碼的制作思路和實(shí)現(xiàn)方法,我們不能盲目的去實(shí)現(xiàn)php生成驗(yàn)證碼,更應(yīng)該了解php驗(yàn)證碼的基本原理,真正的掌握php驗(yàn)證碼的實(shí)現(xiàn)方法,需要的朋友可以參考下2015-11-11
MySQL時(shí)間字段究竟使用INT還是DateTime的說明
今天解析DEDECMS時(shí)發(fā)現(xiàn)deder的MYSQL時(shí)間字段,都是用INT類型,隨后又在網(wǎng)上找到這篇文章,看來如果時(shí)間字段有參與運(yùn)算,用int更好,一來檢索時(shí)不用在字段上轉(zhuǎn)換運(yùn)算,直接用于時(shí)間比較!二來如下所述效率也更高2012-02-02

