在解決A*尋路算法時,需要一個能支持大量插入和刪除(查找)操作的數據結構
鏈表和數組都有缺陷,這時就想到了樹,將樹設置爲左子樹小於自身,右子樹大於自身,這時查找,插入,刪除都比較快捷,但是如果插入數據特例例如遞增的數據
就會導致效率比鏈表還差
理所當然想到自我平衡的樹 AVL樹和紅黑樹(沒看到2-3樹)
然後再思索了一下,發現維護AVL樹的代價比較高,感覺到插入的時候,旋轉AVL數的次數較多,於是旋轉了紅黑樹。
//參考書籍:算法導論
紅黑樹的定義
紅黑樹是每個節點都帶有顏色屬性的二叉查找樹,顏色或紅色或黑色。在二叉查找樹強制一般要求以外,對於任何有效的紅黑樹我們增加了如下的額外要求:
性質1. 節點是紅色或黑色。
性質2. 根節點是黑色。
性質3 每個葉節點(NIL節點,空節點)是黑色的。
性質4 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
性質5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。
紅黑樹的插入:首先 插入的數據必然是葉子節點(原因:只要該節點不爲空,則往左右子樹中查找,最後結束必然是在葉子節點)
最簡單二叉樹的插入(不是紅黑樹) //部分代碼是在編輯頁敲的,可能有問題
tree* new(int i)
{
tree *now = NULL;
now = (tree*)malloc(sizeof(tree));
memset(now,0,sizeof(tree));
now->date = i;
now->leftchild = p.nil;
now->rightchild = p.nil;
return now;}
void Insert(root_nil p, int i)
{
if(NULL == now)//爲空樹
{
p.root = new(i);
}
tree *now = p.root;
tree *parent = now;
while(now != p.nil)
{
parent = now;
if(now->date() < i)
{
now = now->rightchild;
}else
{
now = now->leftchild;
}
}
now = new(i);
now->parent = parent;
if(parent->date() < i)
{
parent->rightchild = now;
}else
{
parent->leftchild = now;
}
}
然後是插入,要保證黑色節點不變 所以將插入的節點都先設置爲紅色 //有可能破壞條件4,但其他條件不影響
分類討論 插入節點的父節點的顏色
若父節點是黑色,此紅黑樹的平衡不改變
若父節點是紅色則需要進行操作
//以下討論皆基於父節點是紅色
//(父節點是紅色)插入的節點是葉子節點,假設存在兄弟節點 根據紅黑樹的定義 兄弟節點必然爲黑色,爲了滿足紅黑樹條件5,那麼兄弟節點必然爲哨兵(nil)
//那麼兄弟節點不存在(反證法)儘量不要對nil進行操作,所以選擇去尋找叔節點的顏色
//情況1之後可能會出現兄弟節點存在並且爲黑色 這時其實就是情況2所以討論誰都是一樣的
因爲父節點是紅色所以爺爺節點必然爲黑色,叔節點的顏色未知
繼續分類討論
若叔節點的顏色爲紅 :此時條件爲 父紅 爺黑 叔紅 情況1
此時若將爺節點的黑色下移給父節點和叔節點,黑色節點個數還是穩定,但是要考慮爺節點的父節點有可能是紅色
所以 z = z->parent->parent; 然後繼續循環
叔節點爲黑:此時爲父紅 爺黑 叔黑 並且子節點是父節點的右孩子情況2
左旋 然後變成情況3
此時爲父紅 爺黑 叔黑 並且子節點是父節點的左孩子情況3
總結:繪製狀態圖
然後是紅黑樹的刪除
要理解紅黑樹的刪除,首先要理解二叉樹的刪除(請關注ββ 以及 13的移動)
紅黑樹刪除時也是這樣做的
只是根據刪除的節點顏色以及替換節點的顏色然後再保持平衡
//刪除節點爲紅 (刪紅) 替換節點爲紅(替紅)
可以分爲四種情況
1.刪紅替紅 //不影響樹我將數值替換,然後刪除替換節點
2.刪紅替黑 //我將數值替換,然後刪除替換節點
3.刪黑替紅 //我如果只是將數值替換 ,然後刪除替換節點即可
4.刪黑替黑 //我將數值替換,然後刪除替換節點
可以發現1,3情況樹的平衡不發生改變2,4情況 樹的替換節點以下少了一個黑色節點
我選擇在右子樹中尋找最小值
rebtree* Tree_MiniMum(root_nil T,rebtree* x)
{
while(x->leftchild != T.nil)
{
x = x->leftchild;
}
return x;
}
//2,4情況下 替換節點都是黑色
//然後討論其父兄節點 ββ區域少了一個黑色節點
父黑兄黑
父紅兄紅 //此情況不可能存在 原本是紅黑樹
父黑兄紅
父紅兄黑
1父黑兄紅
2.父黑兄黑
//完全無從下手,那就去看看兄弟的孩子節點 nil->rightchild = NULL nil->leftchild = NULL nil->color = Black
2.1 w.rightchild == Black && w.leftchild == Black
突然發現只要兄弟節點的左右孩子都爲黑色,那麼無論父節點是什麼顏色效果一樣
即父顏色隨意兄黑兄的左右孩子都爲黑,那麼可以將該狀態上移(ββ上移)
2.2然後兄弟節點的右孩子爲黑,左孩子是紅時
依然發現只要兄弟節點的右孩子爲黑,左孩子是紅時,那麼無論父節點是什麼顏色效果一樣,現在的情況就是2.3狀態
2.3兄弟節點的右孩子紅時
至此討論結束,所有情況完結
總結:
刪除狀態圖
模板類代碼
#ifndef RBTREE_H
#define RBTREE_H
#include <stdio.h>
#include <stdlib.h>
enum ColorType
{
Red = 0,
Black
};
template<class T> class rbt_root_nil;
template<class T>
class rbtree{
public:
rbtree(rbt_root_nil<line> *p);
void SetDate(T Date);
public:
ColorType color;
rbtree<T> *rightchild;
rbtree<T> *leftchild;
rbtree<T> *parent;
T date;
};
template<class T>
class rbt_root_nil
{
public:
rbt_root_nil();
~rbt_root_nil();
rbtree<T>* newRbtreeNode(T i);
rbtree<T>* FindMin();
void LeftRotate(rbtree<T> *x);
void RightRotate(rbtree<T> *y);
void RB_InsertFixup(rbtree<T> *nowNode);
void Insertrbtree(T i);
void Insertrbtree(rbtree<T> *nowNode);
void Rb_TransPlant(rbtree<T> *u,rbtree<T> *v);
rbtree<T>* Tree_MiniMum(rbtree<T>* x);
void Rb_Delete_Fixup(rbtree<T>* x);
rbtree<T>* FindRBtreeByNum(T delnum);
void Deleterbtree(rbtree<T>* z);
void Deleter(T delnum);
void rbt_root_nil<T>::DelNotFree(rbtree<T>* z);
public:
rbtree<T>* root;
rbtree<T>* nil;
};
template<class T>
rbtree<T>::rbtree(rbt_root_nil<line> *p)
{
color = Red;
rightchild = p->nil;
leftchild = p->nil;
parent = p->nil;
}
template<class T>
void rbtree<T>::SetDate(T Date)
{
date = Date;
}
//編譯不通過
/*
template<class T>
rbtree<T>::rbtree(T &t)
{
color = Red;
rightchild = nil;
leftchild = nil;
parent = nil;
date = t;
}*/
template<class T>
rbt_root_nil<T>::rbt_root_nil()
{
nil = (rbtree<T>*)malloc(sizeof(rbtree<T>));
if(NULL == nil)
{
return;
}
nil->color = Black;
nil->leftchild = NULL;
nil->rightchild = NULL;
nil->parent = NULL; //所有未滿節點都是哨兵的父節點,所以將哨兵父節點設爲空
root = nil; //存在不添加就結束的可能性
return ;
}
template<class T>
rbt_root_nil<T>::~rbt_root_nil()
{
//釋放內存
if(root != NULL && root != nil) //先判斷二叉樹是否爲空爲空這root == nil 時兩個指針指向同一個內存空間只能釋放一次
{
delete(root);
root = NULL;
}
if(nil != NULL)
{
delete(nil);
nil = NULL;
}
return ;
}
template<class T>
rbtree<T>* rbt_root_nil<T>::newRbtreeNode(T i)
{
rbtree<T>* use = (rbtree<T>*)malloc(sizeof(rbtree<T>));
if(use == NULL)
{
return NULL;
}
use->date = i;
use->color = Red;
return use;
}
template<class T>
void rbt_root_nil<T>::LeftRotate(rbtree<T> *x)
{
rbtree<T>* y = x->rightchild;
x->rightchild = y->leftchild;
if(y->leftchild != nil)
{
y->leftchild->parent = x;
}
y->parent = x->parent;
if(x->parent == nil)
{
root = y;
}else
{
if(x == x->parent->leftchild)
{
x->parent->leftchild = y;
}else
{
x->parent->rightchild = y;
}
}
y->leftchild = x;
x->parent = y;
}
template<class T>
void rbt_root_nil<T>::RightRotate(rbtree<T> *y)
{
rbtree<T>* x = y->leftchild;
y->leftchild = x->rightchild;
if(x->rightchild != nil)
{
x->rightchild->parent = y;
}
x->parent = y->parent;
if(y->parent == nil) //如果y是根節點,旋轉後x爲根節點
{
root = x;
}else
{
if(y == y->parent->rightchild) //不然就根據左右孩子來講x接上
{
y->parent->rightchild = x;
}else
{
y->parent->leftchild = x;
}
}
x->rightchild = y;
y->parent = x;
}
template<class T>
void rbt_root_nil<T>::RB_InsertFixup(rbtree<T> *nowNode)
{
rbtree<T>* use = NULL;
while(Red == nowNode->parent->color) //父節點爲紅色,當前節點本就是紅色,不符合紅黑樹,所以進行修改
{
if(nowNode->parent == nowNode->parent->parent->leftchild) //判斷父節點是否是爺爺節點的左孩子
{
use = nowNode->parent->parent->rightchild;
if(use->color == Red) //如果父節點和叔節點都是紅色節點,那麼直接把爺爺黑色節點下移,保證下部分的節點符合紅黑樹
{
nowNode->parent->color = Black; //父節點變色
use->color = Black; //叔節點變色
nowNode->parent->parent->color = Red; //爺爺節點變色 //此時可能出現爺爺節點和爺爺的父節點都是紅色,所以再循環時,將爺爺節點設置爲當前節點
nowNode = nowNode->parent->parent;
}else //父節點爲紅,叔節點爲黑 發生在變色過後 將右邊多的一個黑色變成根節點然後將原節點變色後往另半棵樹移動
{
if (nowNode == nowNode->parent->rightchild) //判斷當前節點是否是父節點的右孩子
{
nowNode = nowNode->parent;
LeftRotate(nowNode); //直接將父節點進行左旋
}
nowNode->parent->color = Black;
nowNode->parent->parent->color = Red;
RightRotate(nowNode->parent->parent);
}
}
else//父節點是否是爺爺節點的右孩子
{
use = nowNode->parent->parent->leftchild;
if(use->color == Red) //如果父節點和叔節點都是紅色節點,那麼直接把爺爺黑色節點下移,保證下部分的節點符合紅黑樹
{
nowNode->parent->color = Black; //父節點變色
use->color = Black; //叔節點變色
nowNode->parent->parent->color = Red; //爺爺節點變色 //此時可能出現爺爺節點和爺爺的父節點都是紅色,所以再循環時,將爺爺節點設置爲當前節點
nowNode = nowNode->parent->parent;
}else //父節點爲紅,叔節點爲黑 此時叔節點爲哨兵
{
if (nowNode == nowNode->parent->leftchild) //判斷當前節點是否是父節點的右孩子
{
nowNode = nowNode->parent;
RightRotate(nowNode); //直接將父節點進行右旋
}
nowNode->parent->color = Black;
nowNode->parent->parent->color = Red;
LeftRotate(nowNode->parent->parent); //
}
}
}
root->color = Black;
}
template<class T>
void rbt_root_nil<T>::Insertrbtree(rbtree<T> *nowNode)
{
rbtree<T>* y = nil;
rbtree<T>* x = root;
while(x != nil) //從根開始尋找,插入永遠是插入樹的葉子節點
{
y = x;
if(nowNode->date < x->date)
{
x = x->leftchild;
}else
{
x = x->rightchild;
}
}
nowNode->parent = y;
if(y == nil)
{
root = nowNode; //如果root == NULL 將現在設置爲根
}else
{
if(nowNode->date < y->date) //根據與當前葉子節點的對比獲得所在位置
{
y->leftchild = nowNode;
}else
{
y->rightchild = nowNode;
}
}
nowNode->leftchild = nil; //設定左右孩子
nowNode->rightchild = nil;
RB_InsertFixup(nowNode); //插入後決定是否要變更樹
}
template<class T>
void rbt_root_nil<T>::Insertrbtree(T i)
{
rbtree<T>* nowNode = newRbtreeNode(i);
Insertrbtree(nowNode);
}
template<class T>
void rbt_root_nil<T>::Rb_TransPlant(rbtree<T> *u,rbtree<T> *v)
{
if(u->parent == nil)
{
root = v;
}else if (u == u->parent->leftchild)
{
u->parent->leftchild = v;
}else
u->parent->rightchild = v;
v->parent = u->parent;
}
template<class T>
rbtree<T>* rbt_root_nil<T>::Tree_MiniMum(rbtree<T>* x)
{
while(x->leftchild != nil)
{
x = x->leftchild;
}
return x;
}
template<class T>
void rbt_root_nil<T>::Rb_Delete_Fixup(rbtree<T>* x)
{
rbtree<T>* w = NULL;
while(x != root && x->color == Black)
{
if(x == x->parent->leftchild)
{
w = x->parent->rightchild;
if(w->color == Red)
{
w->color = Black;
x->parent->color = Red;
LeftRotate(x->parent);
w = x->parent->rightchild;
}
//
if(w == nil)
{
printf("error");
}
if(w->leftchild->color == Black && w->rightchild->color == Black)
{
w->color = Red;
x = x->parent;
}else
{
if (w->rightchild->color == Black)
{
w->leftchild->color = Black;
w->color = Red;
RightRotate(w);
w = x->parent->rightchild;
}
w->color = x->parent->color;
x->parent->color = Black;
w->rightchild->color = Black;
LeftRotate(x->parent);
x = root;
}
}
else
{
w = x->parent->leftchild;
//printf("%d",w->key);
if(w->color == Red)
{
w->color = Black;
x->parent->color = Red;
RightRotate(x->parent);
w = x->parent->leftchild;
}
if(w->leftchild->color == Black && w->rightchild->color == Black)
{
w->color = Red;
x = x->parent;
}else
{
if (w->leftchild->color == Black)
{
w->rightchild->color = Black;
w->color = Red;
LeftRotate(w);
w = x->parent->leftchild;
}
w->color = x->parent->color;
x->parent->color = Black;
w->leftchild->color = Black;
RightRotate(x->parent);
x = root;
}
}
}
x->color = Black;
}
template<class T>
rbtree<T>* rbt_root_nil<T>::FindRBtreeByNum(T delnum)
{
rbtree<T>* y = nil;
rbtree<T>* x = root;
while(x != nil)
{
y = x;
if(delnum == x->date)
{
break;
}
if(delnum < x->date)
{
x = x->leftchild;
}else
{
x = x->rightchild;
}
}
if(y == nil) //該元素不存在
{
return NULL;
}
return y;
}
template<class T>
void rbt_root_nil<T>::Deleterbtree(rbtree<T>* z)
{
if(z == NULL)
{
return ;
}
rbtree<T>* y = z;
rbtree<T>* x;
ColorType yOriginalColor = y->color;
if(z->leftchild == nil)
{
x = z->rightchild;
Rb_TransPlant(z,z->rightchild);
}else
{
if(z->rightchild == nil)
{
x = z->leftchild;
Rb_TransPlant(z,z->leftchild);
}
else
{
y = Tree_MiniMum(z->rightchild);
yOriginalColor = y->color;
x = y->rightchild;
if(y->parent == z)
{
x->parent = y;
}
else
{
Rb_TransPlant(y,y->rightchild);
y->rightchild = z->rightchild;
y->rightchild->parent = y;
}
Rb_TransPlant(z,y);
y->leftchild = z->leftchild;
y->leftchild->parent = y;
y->color = z->color;
}
}
if(yOriginalColor == Black)
{
Rb_Delete_Fixup(x);
}
}
template<class T>
void rbt_root_nil<T>::Deleter(T delnum)
{
rbtree<T>* z = FindRBtreeByNum(delnum);
Deleterbtree(z);
free(z);
}
template<class T>
void rbt_root_nil<T>::DelNotFree(rbtree<T>* z)
{
Deleterbtree(z); //移除但不釋放內存
}
template <class T>
rbtree<T>* rbt_root_nil<T>::FindMin()
{
return Tree_MiniMum(root);
}
#endif // rbtree