红黑树跟AVL树一样,也是平衡二叉树,其查找、增加、删除效率为O(lgN),红黑树使用非常广泛,C++STL里面的map,set都是用红黑树实现的。
以下就是一棵红黑树:
《算法导论》上定义红黑树需满足以下五个性质:
1.每个结点是黑色或是红色。
2.根结点是黑色。
3.所有叶子节点是黑色(也就是上面Nil结点)
4.如果一个节点是红色的,则它的两个子结点都是黑色的。(也就是说父子节点不能同时是红色节点)
5.对每个节点,从该结点到到叶子节点的路径上,均包含相同数目的黑色节点。
需要说明一下的是,以上Nil结点是”哨兵节点“,将所有子节点为空的指针都以此Nil节点来代替,而且根节点的父节点也是Nil节点。对于第五条,任一节点到Nil节点的黑色结点个数(包含本身及Nil节点)都相同,也就是所谓的黑高相同。
先来定义红黑树的节点,红黑树需要一个指示颜色的属性,用一个位bool值来表示就可以了,同时,红黑树还需要根据父节点的颜色来修复红黑树性质,因此需要指向父节点的指针。
1 struct RBTreeNode { 2 RBTreeNode* pParent; 3 RBTreeNode* pLeft; 4 RBTreeNode* pRight; 5 bool isRed; 6 int nData; 7 };
同时,定义红黑树的颜色值,以及Nil节点:
1 #define RBTreeNodeBlack false 2 #define RBTreeNodeRed true 3 #define RBTreeNodePtr RBTreeNode* 4 #define RBTreeNilNodePtr &RBTreeNilNode
本节讨论红黑树的插入,因为红黑树也是二叉搜索树,所有红黑树的插入也遵循二叉搜索树插入的过程,只是最后为了保证红黑树的性质,需要在插入后对树进行修正,就像对AVL树执行插入后再再平衡一样。
先来考虑一个问题,新插入一个节点时,这个新插入的节点是用红色的好还是黑色好?根据红黑树性质5,每个节点有相同黑高,如果将新插入节点的颜色设为黑色,则树会立即不满足红黑树性质,因为经过这个新插入节点的路径其黑高都会比其它路径多出一来,
所以,要将新插入的节点颜色设为红色,但是对于根节点是个例外。另外,当新插入一个红色节点时,红黑树性质的123都可以得到维持,只有可能破坏性质4和性质5,以下分几种情况来分别讨论新节点插入。
(以下节点当其左右节点为空时,都为Nil节点,并且Nil节点算在节点的黑高里面,此处为了画图简单并未将Nil节点画)
约定新插入节点为N,N的父节点为P,N的祖父节点为G,N的叔节点为U。
一、树为空
这是最简单的情况,直接将新插入节点标为黑色即可
二、父节点是黑色
这是最简单的情况,这个时候,因为父节点是黑色,而新插入节点是红色,所有新插入节点并没有增加经过此节点的所有路径黑高,依然满足红黑树所有性质,无需做修正处理。
(N为右节点时也是一样的)
三、父节点是红色
当父节点是红色时,破坏了红黑树的性质4,父子节点不能同时为红色,也正因为性质4,所以祖父节点一定是黑色,不然在插入前不满足红黑树性质4.而叔节点颜色可红可黑对于叔节点颜色按照红和黑两种情况来分开 讨论
1、当叔节点颜色是红色时(N是左节点或右节点都适合此情况)
按上图所示,这个时候,需要将P,U和颜色和G的颜色交换,即可从局部上修正了性质4,并保持性质5,但是这个时候G的颜色由黑变红,可能G的父节点也是红色, 这个时候,就要把G节点按照处理N结点的情况再处理一次。也就是向上传递修复。
2.当U节点是黑色,且N是左节点时
此时,先对G节点执行右旋转操作(不熟悉旋转操作的请看我的上一篇博客AVL树,一定要非常熟悉旋转操作才能理解红黑树),然后交换P,G节点的颜色,即可完成修复,本子树根节点G修复前是黑色,修复后,新的子树根节点P仍然是黑色,至此,修复完毕,无需向上传递。
3.当U节点是黑色,且N是右节点时
由以上由与前一种情况相比,唯一的区别是N是右子节点,所以先对P节点执行左旋转,即可转化成前一种讨论过的情况,然后按前一种情况来处理即可。
好了,红黑树节点的所有情况都在上面了,总的来看并不复杂,树为空和父节点为黑色这两种情况很简单,无需多言,至于下面的几种情况,总结起来就是,如何处理取决于U节点颜色
为红时则交换PU和G的颜色,然后再对G处理。
为黑时分N为左右节点情,为左节点时右旋转G,然后交换P与G的颜色,为右子节点时先左旋转P,然后再按左节点情况进行处理。
红黑树插入代码如下:(代码中的情况一二三四五,以图中的(1)(2)(3)(4)(5)对照)
1 RBTreeNode* RBTreeInsert(RBTreeNode* pRoot, int nData) { 2 if (pRoot == RBTreeNilNodePtr) { 3 pRoot = MakeNewRBTreeNode(RBTreeNilNodePtr, nData); 4 pRoot->isRed = RBTreeNodeBlack; 5 return pRoot; 6 } 7 RBTreeNode* pCursor = pRoot; 8 RBTreeNode* pParent = pRoot->pParent; 9 while (pCursor != RBTreeNilNodePtr) { 10 pParent = pCursor; 11 if (nData > pCursor->nData) { 12 pCursor = pCursor->pRight; 13 } 14 else if (nData < pCursor->nData) { 15 pCursor = pCursor->pLeft; 16 } 17 else 18 break; 19 } 20 if (pCursor == RBTreeNilNodePtr && pParent != RBTreeNilNodePtr) { 21 RBTreeNode* pNode = MakeNewRBTreeNode(pParent, nData); 22 if (nData > pParent->nData) 23 pParent->pRight = pNode; 24 else 25 pParent->pLeft = pNode; 26 27 FixRBTreeNodeColor_Insert(&pRoot, pNode); 28 } 29 return pRoot; 30 } 31 32 void FixRBTreeNodeColor_Insert(RBTreeNode** pRoot, RBTreeNode* pNode) { 33 if (!pNode->isRed) 34 return; 35 36 RBTreeNode* pParent = pNode->pParent; 37 if (pParent == RBTreeNilNodePtr) { 38 //第一种情况 39 pNode->isRed = RBTreeNodeBlack; 40 return; 41 } 42 else if (!pParent->isRed || pParent->pParent == RBTreeNilNodePtr)//pParent->pParent == RBTreeNilNodePtr为第二种情况 43 return; 44 RBTreeNode* pGrandParent = pParent->pParent; 45 if (pParent == pGrandParent->pLeft) { 46 RBTreeNode* pUncle = pGrandParent->pRight; 47 if (pUncle != RBTreeNilNodePtr) { 48 if (pUncle->isRed) { 49 //情况3:不需要旋转操作,直接交换颜色,然后往上递归检查 50 pParent->isRed = pUncle->isRed = RBTreeNodeBlack; 51 pGrandParent->isRed = RBTreeNodeRed; 52 return FixRBTreeNodeColor_Insert(pRoot, pGrandParent); 53 } 54 } 55 if (pNode == pParent->pRight) { 56 //情况5,先左旋pParent转换成情况4 57 RBTreeLeftRotate(pRoot, pParent); 58 } 59 //情况4 60 pGrandParent = RBTreeRightRotate(pRoot, pGrandParent); 61 SwapRBTreeNodeColor(pGrandParent, pGrandParent->pRight); 62 } 63 else { 64 //上面if分支的镜像 65 RBTreeNode* pUncle = pGrandParent->pLeft; 66 if (pUncle != RBTreeNilNodePtr) { 67 if (pUncle->isRed) { 68 //情况3:不需要旋转操作,直接交换颜色,然后往上递归检查 69 pParent->isRed = pUncle->isRed = RBTreeNodeBlack; 70 pGrandParent->isRed = RBTreeNodeRed; 71 return FixRBTreeNodeColor_Insert(pRoot, pGrandParent); 72 } 73 } 74 if (pNode == pParent->pLeft) { 75 //情况5,先右旋pParent转换成情况4 76 RBTreeRightRotate(pRoot, pParent); 77 } 78 //情况4 79 pGrandParent = RBTreeLeftRotate(pRoot, pGrandParent); 80 SwapRBTreeNodeColor(pGrandParent, pGrandParent->pLeft); 81 } 82 }