数据结构-----红黑树的插入操作
紅黑樹是一棵二叉搜索樹;樹種每一個節點的顏色不是黑色就是紅色。本篇中只實現用節點的顏色來描述紅黑樹,性質如下:
RB1:根節點和所有外部節點都是黑色;
RB2:在根至外部節點路徑上,沒有連續兩個節點是紅色;
RB3:在所有根至外部節點的路徑上,黑色節點的數目都相同;
實現紅黑樹,首先需要定義樹節點,節點包括:
1:節點顏色,黑色或者白色,用整型或者布爾類型表示,也可以預設兩個宏;
2:節點權值,用來保存節點的數據;
3:左右子樹指針;
首先是紅黑樹的定義,本例中紅黑樹繼承自BST(二叉搜索樹),即:
#define BLACK 1 #define RED 0template<class T> class RBTree : public binarySearchTree<T, int> { public:RBTree() : binarySearchTree() {}~RBTree() {}void insert(const T& key) {insert(root, key);}void remove(const T& key) {remove(root, key);}private:binaryTreeNode<pair<const T, int>>*insert(binaryTreeNode<pair<const T, int>>*&, const T&);binaryTreeNode<pair<const T, int>>*remove(binaryTreeNode<pair<const T, int>>*&, const T&);binaryTreeNode<pair<const T, int>>*maximun(binaryTreeNode<pair<const T, int>>*);binaryTreeNode<pair<const T, int>>*minimun(binaryTreeNode<pair<const T, int>>*);int color(binaryTreeNode<pair<const T, int>>*);}和AVL樹類似,插入刪除操作也是利用遞歸實現,所以在私有成員中存在實現遞歸調用的重載函數。
模板參數T為節點權值的類型,int為顏色類型。
insert插入函數:在以該節點為根節點的子樹中插入新權值,返回插入后該子樹的根節點。
remove刪除操作:在以該節點為根節點的子樹中刪除特定權值的節點,返回刪除后子樹的根節點。
maximun函數:返回以該節點為根節點的子樹中權值最大的節點。
minimun函數:返回以該節點為根節點的子樹中權值最小的節點。
color函數:返回函數的顏色。
插入操作步驟如下:
步驟1:找到待插入的位置,即逐個比較權值,若該節點權值小于待插入權值,則在右子樹中插入;若該節點權值大于待插入權值,則在左子樹中插入。
步驟2:申請新節點,若樹為空,則新節點為黑色;若樹非空,則新節點為紅色。
步驟3:改變相關節點的顏色,調整樹平衡。
申請新節點的代碼如下:
template<class T> binaryTreeNode<pair<const T, int>>* RBTree<T>::insert(binaryTreeNode<pair<const T, int>>* &pnode, const T& key) {if(pnode == NULL){if(root == NULL)pnode = new binaryTreeNode<pair<const T, int>>*(pair<const T, int>(key, BLACK));elsepnode = new binaryTreeNode<pair<const T, int>>*(pair<const T, int>(key, RED)); }/*...*/ }接下來就是執行比較插入的過程,涉及到對節點顏色進行修改的內容,我們先了解一下需要哪些調整。
插入操作的樹平衡被破壞無非是存在兩個連續的紅色節點,違反了RB2。
同時,若違反RB2,則新插入節點和其父節點都應為紅色,那么祖父節點必須為黑色(因為插入之前是平衡的)。
被破壞的情況有以下幾種,假設子樹的根節點為pnode,插入的節點為ptNode:
第一種情況:pnode的左孩子和右孩子都為紅色,新節點是pnode孩子的孩子。
插入節點為pnode->leftChild->leftChild或pnode->leftChild->rightChild color(pnode->leftChild) == RED && color(pnode->rightChild) == RED 或者 插入節點為pnode->rightChild->leftChild或pnode->rightChild->rightChild color(pnode->rightChild) == RED && color(pnode->leftChild) == RED 解決方案:1:將根節點的左右子樹的顏色涂上黑色;
2:若pnode不是整個紅黑樹的根節點,則將pnode涂上紅色
3:返回pnode;
代碼實現:
template<class T> binaryTreeNode<pair<const T,int>>* RBTree<T>::rModify(binaryTreeNode<pair<const T, int>>* pnode) {pnode->leftChild->element.second = BLACK;pnode->rightChild->element.second = BLACK;if(pnode != root)pnode->element.second = RED;return pnode; }第二種情況:pnode的左孩子為紅色,右孩子為黑色(也可以是外部節點),新節點是pnode左孩子的孩子。
插入節點為pnode->leftChild->leftChild或pnode->leftChild->rightChild color(pnode->leftChild) == RED && color(pnode->rightChild) == BLACK 解決方案:1:若插入的是pnode左孩子的左孩子,則進行右旋操作;若插入的是pnode左孩子的右孩子,則進行先左旋后右旋操作;此時pnode為新根節點。
2:將新的根節點涂黑,將新根節點右孩子涂紅。
3:返回根節點。
代碼實現:
template<class T> binaryTreeNode<pair<const T,int>>* RBTree<T>::LbModify(binaryTreeNode<pair<const T, int>>* pnode) {pnode->element.second = BLACK;pnode->rightChild->element.second = RED;return pnode; }函數調用: if(color(pnode->rightChild) == BLACK && color(pnode->leftChild) == RED) {if(key > pnode->leftChild->element.first)pnode = leftRightRotation(pnode);elsepnode = rightRotation(pnode);pnode = LbModify(pnode); }第三種情況:pnode的右孩子為紅色,左孩子為黑色,新節點為pnode右孩子的孩子。 插入節點為pnode->rightChild->leftChild或pnode->rightChild->rightChild color(pnode->rightChild) == RED && color(pnode->leftChild) == BLACK
解決方案:
1:若新節點是pnode右孩子的右孩子,進行左旋操作;若新節點是pnode右孩子的左孩子,進行先右旋后左旋操作,此時pnode為新的根節點。
2:將新的根節點涂黑,新根節點的左孩子涂紅。
3:返回新根節點。
代碼實現:
template<class T> binaryTreeNode<pair<const T,int>>* RBTree<T>::RbModify(binaryTreeNode<pair<const T, int>>* pnode) {pnode->element.second = BLACK;pnode->leftChild->element.second = RED;return pnode; }調用代碼: if(color(pnode->leftChild) == BLACK && color(pnode->rightChild) == RED) {if(key > pnode->rightChild->element.first)pnode = leftRotation(pnode);elsepnode = rightLeftRotation(pnode);pnode = RbModify(pnode); }插入函數代碼: template<class T> binaryTreeNode<pair<const T, int>>* RBTree<T>::insert(binaryTreeNode<pair<const T, int>>* &pnode, const T& key) {if(pnode == NULL) //申請新節點,空則為黑色,非空則為紅色。{if(root == NULL)pnode = new binaryTreeNode<pair<const T, int>>*(pair<const T, int>(key, BLACK));elsepnode = new binaryTreeNode<pair<const T, int>>*(pair<const T, int>(key, RED)); }else{if(key < pnode->element.first){//如果key小,則在左子樹中插入pnode->leftChild = insert(pnode->leftChild, key);//左子樹中插入后,若左孩子的孩子是紅色,則需要進一步判斷。if(pnode->leftChild->leftChild != NULL && color(pnode->leftChild->leftChild) == RED)||(pnode->leftChild->rightChild != NULL && color(pnode->leftChild->rightChild) == RED){//若左孩子是紅色,則需要調整if(color(pnode->leftChild) == RED){if(color(pnode->rightChild) == RED) //若右孩子是黑色,為第一種情況pnode = rModify(pnode);else //否則,為第二種或者第三種{if(key > pnode->leftChild->element.first)pnode = leftRightRotation(pnode);elsepnode = rightRotation(pnode);pnode = LbModify(pnode);}}}}//與上述對稱else if(key > pnode->element.first){pnode->rightChild = insert(pnode->rightChild, key);if(pnode->rightChild->leftChild != NULL && color(pnode->rightChild->leftChild) == RED) ||(pnode->rightChild->rightChild != NULL && color(pnode->rightChild->rightChild) == RED){if(color(pnode->rightChild) == RED){if(color(pnode->leftChild) == RED)pnode = rModify(pnode);else{if(key > pnode->rightChild->element.first)pnode = leftRotation(pnode);elsepnode = rightLeftRotation(pnode);pnode = RbModify(pnode);}}}}}return pnode; }
總結
以上是生活随笔為你收集整理的数据结构-----红黑树的插入操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构-----图的拓扑排序和关键路径
- 下一篇: 数据结构-----最大堆的实现