一,红黑树的插入
#include<iostream>
#include<iomanip>
#include<Windows.h>
using namespace std;
/** 红黑树是每个节点带有颜色属性的二叉查找树,颜色为红色或黑色,并有如下额外的要求:
* 性质1 节点是红色或者黑色。
* 性质2 根是黑色。
* 性质3 所有叶子都是黑色(叶子是NIL节点)。
* 性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
* 性质5 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
*/
enum COLOR
{
RED,
BLACK
};
typedef struct node
{
node(int value,COLOR color):value(value),color(color),pLeftChild(NULL),pRightChild(NULL),pParent(NULL){}
int value;
COLOR color;
node* pLeftChild;
node* pRightChild;
node* pParent;
}Node,*PNode;
PNode g_tree_root = NULL;;
PNode GrandParent(PNode pn)
{
return pn->pParent->pParent;
}
PNode Uncle(PNode pn)
{
PNode pGandN = GrandParent(pn);
if(pn->pParent == pGandN->pLeftChild)
return pGandN->pRightChild;
else
return pGandN->pLeftChild;
}
void Rotate_left(PNode pn)
{
PNode tmpP = pn->pParent;
PNode tmpR = pn->pRightChild;
pn->pParent = tmpR;
pn->pRightChild = tmpR->pLeftChild;
if(tmpR->pLeftChild)
tmpR->pLeftChild->pParent = pn;
tmpR->pParent = tmpP;
tmpR->pLeftChild = pn;
if(tmpP && tmpP->pLeftChild == pn) tmpP->pLeftChild = tmpR;
if(tmpP && tmpP->pRightChild == pn) tmpP->pRightChild = tmpR;
if(pn == g_tree_root)
g_tree_root = tmpR;
}
void Rotate_right(PNode pn)
{
PNode tmpP = pn->pParent;
PNode tmpL = pn->pLeftChild;
pn->pParent = tmpL;
pn->pLeftChild = tmpL->pRightChild;
if(tmpL->pRightChild)
tmpL->pRightChild->pParent = pn;
tmpL->pParent = tmpP;
tmpL->pRightChild = pn;
if(tmpP && tmpP->pLeftChild == pn) tmpP->pLeftChild = tmpL;
if(tmpP && tmpP->pRightChild == pn) tmpP->pRightChild = tmpL;
if(pn == g_tree_root)
g_tree_root = tmpL;
}
void Insert_case1(PNode pn);
/** 情形5
* 父节点P是红色而叔父节点U是黑色或者缺少,新节点N是其父节点的左子结点,而P又是祖父节点G的左子节点。
* 解决:针对G进行一次右旋。
同时,情形5存在镜像情形,即父节点P是红色而叔父节点U是黑色或者缺少,
新节点N是其父节点的右子结点,而P又是祖父节点G的右子节点,则镜像情形进行左旋。
*/
void Insert_case5(PNode pn)
{
PNode pGrandPa = GrandParent(pn);
pn->pParent->color = BLACK;
pGrandPa->color = RED;
if(pn == pn->pParent->pLeftChild)
Rotate_right(pGrandPa);
else
Rotate_left(pGrandPa);
}
/** 情形4
* 父节点P是红色而叔父节点U是黑色或者缺少,并且新节点N是其父节点P的右子节点而父节点P又是祖父节点G的左子节点。
* 解决:针对P进行一次左旋,左旋后P变成了N的左子节点,对P按照情形5进行调整。
* 同时,情形4存在镜像情形,即父节点P是红色而叔父节点U是黑色或者缺少,
并且新节点N是其父节点P的左子节点而父节点P又是祖父节点G的右子节点,则镜像情形进行右旋。
*/
void Insert_case4(PNode pn)
{
PNode pGrandPa = GrandParent(pn);
if(pn == pn->pParent->pRightChild && pn->pParent == pGrandPa->pLeftChild){
Rotate_left(pn->pParent);
pn = pn->pLeftChild;
} else if(pn == pn->pParent->pLeftChild && pn->pParent == pGrandPa->pRightChild){
Rotate_right(pn->pParent);
pn = pn->pRightChild;
}
Insert_case5(pn);
}
/** 情形3
* 父节点P和叔父节点U都是红色的。
* 解决:将P和U重绘为黑色,将祖父节点G重绘为红色。
* 同时为了解决G的父节点也可能是红色的问题,将G作为当前节点从情形1开始调整。
*/
void Insert_case3(PNode pn)
{
PNode pUncle = Uncle(pn);
if(pUncle && pUncle->color == RED){
pn->pParent->color = BLACK;
pUncle->color = BLACK;
PNode g = GrandParent(pn);
g->color = RED;
Insert_case1(g);
}
else
Insert_case4(pn);
}
/** 情形2
* 新节点的父节点P是黑色。
* 解决:因为新增红色节点,性质并未受到威胁,无需处理。
*/
void Insert_case2(PNode pn)
{
if(pn->pParent->color == BLACK)
return;
Insert_case3(pn);
}
/** 情形1
* 新节点位于树的根上。
* 解决: 把它重绘为黑色。
*/
void Insert_case1(PNode pn)
{
pn->color = RED;
if(pn->pParent == NULL)
pn->color = BLACK;
else
Insert_case2(pn);
}
/** 插入
* 以二叉查找树的方法增加节点并标记它为红色。
*/
void Insert(int value)
{
PNode pn = new Node(value,RED);
if(g_tree_root == NULL)
g_tree_root = pn;
else
{
PNode tmp = g_tree_root;
do
{
if(value > tmp->value)
{
if(tmp->pRightChild == NULL)
{
tmp->pRightChild = pn;
pn->pParent = tmp;
break;
}
tmp = tmp->pRightChild;
}
else
{
if(tmp->pLeftChild == NULL)
{
tmp->pLeftChild = pn;
pn->pParent = tmp;
break;
}
tmp = tmp->pLeftChild;
}
}while(1);
}
Insert_case1(pn);
}
void ReleaseTree(PNode pn)
{
if(!pn) return ;
if(pn->pLeftChild)
ReleaseTree(pn->pLeftChild);
if(pn->pRightChild)
ReleaseTree(pn->pRightChild);
delete pn;
}
void PutInArray(PNode arry[], PNode pn,int index)
{
if(index >= 31)
return;
arry[index] = pn;
if(pn->pLeftChild)
PutInArray(arry,pn->pLeftChild,index*2+1);
if(pn->pRightChild)
PutInArray(arry,pn->pRightChild,index*2+2);
}
void PrintSpace(int n)
{
while(n--)
cout<<" ";
}
BOOL SetConsoleColor(WORD wAttribute)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if(hConsole == INVALID_HANDLE_VALUE)
return FALSE;
return SetConsoleTextAttribute(hConsole, wAttribute);
}
/**
* PrintTree只打印树的头5层
*/
void PrintTree()
{
SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
cout<<"======================================================="<<endl;
PNode arrayN[31] = {0};
PutInArray(arrayN, g_tree_root, 0);
int firstNode = 0, frontspaces = 62, innerspaces = 126;
for(int i=0;i<31;i++)
{
if(i == firstNode)
{
frontspaces = frontspaces/2 - 1;
innerspaces = innerspaces/2 - 1;
firstNode = firstNode*2 + 1;
cout<<endl;
PrintSpace(frontspaces);
}
else
{
PrintSpace(innerspaces);
}
if(arrayN[i] == 0)
cout<<" ";
else
{
if(arrayN[i]->color == RED)
SetConsoleColor(FOREGROUND_RED);
else
SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
cout<<setw(2)<<arrayN[i]->value;
}
}
cout<<endl;
}
int main()
{
int a;
srand(time(0));
int szINTS[100];
/*int szM1[20] = {1,2,4,11,20,13,19,15,18,12,10,7,5,3,6,17,16,8,14,9};
for(int i=0;i<20;i++)
{
Insert(szM1[i]);
PrintTree();
}*/
for(int i=0;i<100;i++)
szINTS[i] = i+1;
for(int i=20;i>0;i--)
{
int iTmp = rand()%i;
Insert(szINTS[iTmp]);
swap(szINTS[iTmp], szINTS[i-1]);
}
PrintTree();
ReleaseTree(g_tree_root);
g_tree_root = NULL;
return 0;
}
二,红黑树的删除
PNode Sibling(PNode pn)
{
if(pn == pn->pParent->pLeftChild)
return pn->pParent->pRightChild;
else
return pn->pParent->pLeftChild;
}
/**
* 不考虑NIL节点时,对颜色的判定方式。
*/
BOOL IsBlack(PNode pn)
{
return (!pn || pn->color == BLACK);
}
BOOL IsRed(PNode pn)
{
return pn && pn->color == RED;
}
void Delete_case1(PNode pn);
/** 情形6
* 当前节点N的sibling节点S为黑色,S的右子节点为红色,而N是其父节点P的左子节点。
* 解决:令S的颜色等于N的父节点P的颜色。S的右子结点重绘为黑色,对P进行左旋。
* 镜像情形:当前节点N的sibling节点S为黑色,S的左子节点为红色,而N是其父节点P的右子节点。
* 则令S的颜色等于N的父节点P的颜色。S的左子结点重绘为黑色,对P进行右旋。
*/
void Delete_case6(PNode pn)
{
PNode pS = Sibling(pn);
pS->color = pn->pParent->color;
pn->pParent->color = BLACK;
if(pn == pn->pParent->pLeftChild){
pS->pRightChild->color = BLACK;
Rotate_left(pn->pParent);
}else{
pS->pLeftChild->color = BLACK;
Rotate_right(pn->pParent);
}
}
/** 情形5
* 当前节点N的sibling节点S,S的左子节点SL是红色,S的右子结点SR是黑色,且N是其父节点P的左子结点。
* 解决: 将红色子节点SL与S的颜色互换,对S进行右旋。
* 镜像情形:当前节点N的sibling节点S,S的右子节点SR是红色,S的左子结点SL是黑色,且N是其父节点P的右子结点。
* 则将红色子节点SR与S的颜色互换,对S进行左旋。
*/
void Delete_case5(PNode pn)
{
PNode pS = Sibling(pn);
if(pS->color == BLACK){
if(pn == pn->pParent->pLeftChild && IsRed(pS->pLeftChild) && IsBlack(pS->pRightChild)){
pS->color = RED;
pS->pLeftChild->color = BLACK;
Rotate_right(pS);
}
else if(pn == pn->pParent->pRightChild && IsBlack(pS->pLeftChild) && IsRed(pS->pRightChild)){
pS->color = RED;
pS->pRightChild->color = BLACK;
Rotate_left(pS);
}
}
Delete_case6(pn);
}
/** 情形4
* 若当前节点为N,N的父节点P为红色,N的Sibling节点S为黑色,且S无子节点或有2个黑色子节点。
* 解决:S与P交换颜色
*/
void Delete_case4(PNode pn)
{
PNode pS = Sibling(pn);
if(pn->pParent->color == RED && pS->color == BLACK && IsBlack(pS->pLeftChild) && IsBlack(pS->pRightChild)){
pS->color = RED;
pn->pParent->color = BLACK;
}
else
Delete_case5(pn);
}
/** 情形3
* 若当前节点N,N的父节点P为黑色,N的Sibling节点S为黑色,且S无子节点或有2个黑色子节点。
* 解决:重绘S为红色,并令P从Delete_case1开始重新处理。
* 目的:若只考虑N,则不可能出现S有2个黑色子节点的条件。但P从case1开始调整时,可能出现此情况,
即整体的调整可能需要从整个左子树或右子树中删除一个黑色节点来达到平衡。
*/
void Delete_case3(PNode pn)
{
PNode pS = Sibling(pn);
if(pn->pParent->color == BLACK && pS->color == BLACK && IsBlack(pS->pLeftChild) && IsBlack(pS->pRightChild)){
pS->color = RED;
Delete_case1(pn->pParent);
}
else
Delete_case4(pn);
}
/** 情形2
* 若当前节点N的兄弟节点S(Sibling(N)获得的节点)是红色。N是其父节点P的左子结点。
* 解决:P与S交换颜色,对P进行左旋。
* 镜像情形:当前节点N的兄弟节点S(Sibling(N)获得的节点)是红色。N是其父节点P的右子结点。
* 则P与S交换颜色,对P进行右旋。
*/
void Delete_case2(PNode pn)
{
PNode pS = Sibling(pn);
if(pS->color == RED){
pn->pParent->color = RED;
pS->color = BLACK;
if(pn == pn->pParent->pLeftChild)
Rotate_left(pn->pParent);
else
Rotate_right(pn->pParent);
}
Delete_case3(pn);
}
/** 情形1
* 若当前节点是根节点,则无需处理。
*/
void Delete_case1(PNode pn)
{
if(pn->pParent != NULL)
Delete_case2(pn);
}
PNode Find_max(PNode pn)
{
if(!pn->pRightChild)
return pn;
else
return Find_max(pn->pRightChild);
}
PNode Find_min(PNode pn)
{
if(!pn->pLeftChild)
return pn;
else
return Find_min(pn->pLeftChild);
}
PNode Find_from_tree(int value)
{
PNode tmp = g_tree_root;
do{
if(tmp == NULL)
return NULL;
else if(value == tmp->value)
return tmp;
else if(value > tmp->value)
tmp = tmp->pRightChild;
else
tmp = tmp->pLeftChild;
}while(1);
}
/** delete (这里与网上处理不同,不使用NIL节点,理解起来更方便一点)
* 将要删除的节点转移到其左子树的最大值的节点,或者右子树的最小值的节点,将这个节点命名为M。
* M的子节点肯定<=1(不然就不可能是最大值或最小值),若M存在子节点则令这个子节点为C。
* 1,若M为红色,C不存在。此时,直接删除M即可。
* (X)若M为红色,C存在且C为红色,违反性质4,这个情况不可能存在。同理,若M为红色,C存在且C为黑色,违反性质5,情况不可能存在。
* 2,若M为黑色,C存在且C为红色,则用C替换M且把C重绘成黑色。
* (X)若M为黑色,C存在且C为黑色,违反性质5,情况不可能存在。
* 3,若M为黑色,C不存在,则情况较复杂,需要特殊处理,即Delete_case1开始。
*/
void Delete(int value)
{
PNode pn = Find_from_tree(value);
if(!pn) return;
PNode tmp;
if(pn->pLeftChild)
tmp = Find_max(pn->pLeftChild);
else if(pn->pRightChild)
tmp = Find_min(pn->pRightChild);
else
tmp = pn;
swap(tmp->value,pn->value);
if(tmp->color == RED) { //1
}
else if(tmp->pLeftChild || tmp->pRightChild) { //2
PNode pChild = tmp->pLeftChild ? tmp->pLeftChild : tmp->pRightChild;
swap(pChild->value, tmp->value);
tmp = pChild;
}
else //3
Delete_case1(tmp);
PNode tmpP = tmp->pParent;
if(tmpP && tmpP->pLeftChild == tmp) tmpP->pLeftChild = NULL;
if(tmpP && tmpP->pRightChild == tmp) tmpP->pRightChild = NULL;
if(tmp == g_tree_root) g_tree_root = NULL;
delete tmp;
}
int main()
{
int a;
srand(time(0));
int szINTS[100];
/*int szM1[20] = {1,2,4,11,20,13,19,15,18,12,10,7,5,3,6,17,16,8,14,9};
for(int i=0;i<20;i++)
{
Insert(szM1[i]);
PrintTree();
}*/
for(int i=0;i<100;i++)
szINTS[i] = i+1;
for(int i=20;i>0;i--)
{
int iTmp = rand()%i;
Insert(szINTS[iTmp]);
swap(szINTS[iTmp], szINTS[i-1]);
}
PrintTree();
int szDEL[100];
for(int i=0;i<100;i++)
szDEL[i] = i + 1;
for(int i=20;i>1;i--)
{
int iTmp = rand()%i;
Delete(szDEL[iTmp]);
swap(szDEL[iTmp], szDEL[i-1]);
PrintTree();
}
ReleaseTree(g_tree_root);
g_tree_root = NULL;
return 0;
}