红黑树
来源:互联网 发布:虎鲸vs大白鲨 知乎 编辑:程序博客网 时间:2024/06/06 00:54
- 红黑树概念
1.红黑树首先是一个二叉查找树,他的每个节点都被标有颜色(红色或者黑色)红黑树满足以下五个性质
1.每个节点的颜色只能是黑色或者是红色
2.根节点是黑色的
3.如果一个节点的颜色是红色,则他的两个孩子都是黑的,也就是说一条路径上不能出现两个相邻的红色节点。
4.对每个节点来说 ,从该节点到其子孙叶子节点的所有路径上,黑色节点的个数相同。
5.每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
- 红黑树的插入操作
红黑树必须满足上述的5个性质,但是插入节点后可能会导致失衡,此时需要做一定的调整。下面总结了导致失衡的情况,以及相应的调整措施。
情形1:parent是grendParent的左孩子,并且uncle节点存在且为红色。
调整方式:将parent和uncle节点改为黑色,并将grendParent改为红色,然后继续向上调整。
Node *grandParent = parent->_pParent; if (parent == grandParent->_pLeft) { Node *uncle = grandParent->_pRight; if (uncle && RED == uncle->_color) { parent->_color = BLACK; uncle->_color = BLACK; grandParent->_color = RED; pNew = grandParent; parent = grandParent->_pParent; }
情形2: parent是grendParent的左孩子,插入节点是parent的左孩子,并且uncle节点不存在或者为黑色。
调整方式:将parent与grandParent的颜色互换,并进行右旋。
情形3:parent是grendParent的左孩子,插入节点是parent的右孩子,并且uncle节点不存在或者为黑色。
调整方式:先进行左旋,再将parent与grandParent的颜色互换,并进行右旋。
else//uncle节点不存在或者为黑 { if (pNew == parent->_pRight) { lRotate(parent); std::swap(parent, pNew); } parent->_color = BLACK; grandParent->_color = RED; rRotate(grandParent); }
情形4:parent是grendParent的右孩子,并且uncle节点存在且为红色。
调整方式:将parent和uncle节点改为黑色,并将grendParent改为红色,然后继续向上调整。
else//parent 为grandParent的右孩子 { Node *uncle = grandParent->_pLeft; if (uncle && RED == uncle->_color) { parent->_color = BLACK; uncle->_color = BLACK; grandParent->_color = RED; pNew = grandParent; parent = grandParent->_pParent; }
情形5: parent是grendParent的做右孩子,插入节点是parent的右孩子,并且uncle节点不存在或者为黑色。
调整方式:将parent与grandParent的颜色互换,并进行左旋。
情形6:parent是grendParent的右孩子,插入节点是parent的左孩子,并且uncle节点不存在或者为黑色。
调整方式:先进行右旋,再将parent与grandParent的颜色互换,并进行左旋。
综上其实可以分为parent是grendParent的左孩子或者右孩子两大类,两种情况又分别有三种情况,所以完整的插入代码如下
bool Insert(const K &key, const V &value) { Node *pNew = new Node(key, value); if (NULL == _pRoot) { _pRoot = pNew; _pRoot->_color = BLACK; return true; } //寻找插入位置 Node *parent = NULL; Node *pCur = _pRoot; while (pCur) { if (key < pCur->_key) { parent = pCur; pCur = pCur->_pLeft; } else if (key > pCur->_key) { parent = pCur; pCur = pCur->_pRight; } else return false; } //插入节点 if (key < parent->_key) parent->_pLeft = pNew; else parent->_pRight = pNew; pNew->_pParent = parent; //判断是否违反性质进行调整 while (parent && parent != _pRoot && RED == parent->_color) { Node *grandParent = parent->_pParent; if (parent == grandParent->_pLeft) { Node *uncle = grandParent->_pRight; if (uncle && RED == uncle->_color) { parent->_color = BLACK; uncle->_color = BLACK; grandParent->_color = RED; pNew = grandParent; parent = grandParent->_pParent; } else//uncle节点不存在或者为黑 { if (pNew == parent->_pRight) { lRotate(parent); std::swap(parent, pNew); } parent->_color = BLACK; grandParent->_color = RED; rRotate(grandParent); } } else//parent 为grandParent的右孩子 { Node *uncle = grandParent->_pLeft; if (uncle && RED == uncle->_color) { parent->_color = BLACK; uncle->_color = BLACK; grandParent->_color = RED; pNew = grandParent; parent = grandParent->_pParent; } else//uncle节点不存在或者为黑 { if (pNew == parent->_pLeft) { rRotate(parent); std::swap(parent, pNew); } parent->_color = BLACK; grandParent->_color = RED; lRotate(grandParent); } } } _pRoot->_color = BLACK; return true; }
左旋及右旋代码
void lRotate(Node *parent) { Node *subR = parent->_pRight; Node *subRL = subR->_pLeft; parent->_pRight = subRL; if (subRL) subRL->_pParent = parent; subR->_pLeft = parent; Node *pparent = parent->_pParent; parent->_pParent = subR; subR->_pParent = pparent; if (NULL == pparent) { _pRoot = subR; } else { if (pparent->_pLeft == parent) pparent->_pLeft = subR; else pparent->_pRight = subR; } } void rRotate(Node *parent) { Node *subL = parent->_pLeft; Node *subLR = subL->_pRight; parent->_pLeft = subLR; if (subLR) subLR->_pParent = parent; subL->_pRight = parent; Node *pparent = parent->_pParent; parent->_pParent = subL; subL->_pParent = pparent; if (NULL == pparent) { _pRoot = subL; } else { if (pparent->_pLeft == parent) pparent->_pLeft = subL; else pparent->_pRight = subL; } }
- 测试代码
插入操作完成后为了验证我们的操作是否正确,我们可以自己写一个验证代码来检测一下。
bool Test() { if (NULL == _pRoot) return true; if (BLACK != _pRoot->_color) { cout << "不满足性质1:根的颜色为黑色" << endl; return false; } size_t numofBlackN = 0; Node *pCur = _pRoot; while (pCur) { if (BLACK == pCur->_color) numofBlackN++; pCur = pCur->_pLeft;//统计最左边分支的黑色节点个数。 } return _test(_pRoot, numofBlackN, 0); }bool _test(Node *pRoot, size_t numofBlackN, size_t index) { if (NULL == pRoot) return true; if (RED == pRoot->_color) { if (pRoot->_pLeft && RED == pRoot->_pLeft->_color || pRoot->_pRight && RED == pRoot->_pRight->_color) { cout << "违反性质3:两个红色节点相邻" << endl; return false; } } if (BLACK == pRoot->_color) index++; if (NULL == pRoot->_pLeft && NULL == pRoot->_pRight) { if (numofBlackN != index) { cout << "违反性质4:每条路径黑色节点个数不相同" << endl; return false; } } return _test(pRoot->_pLeft, numofBlackN, index) && _test(pRoot->_pRight, numofBlackN, index); }
上述代码只能测试是否满足红黑树的5个性质,红黑树也是一个二叉搜索树,我们要检验二叉搜索树的正确性,可以采用中序遍历,看看是否正确。
void inorder() { _inorder(_pRoot); }void _inorder(Node *pRoot) { if (NULL == pRoot) return; _inorder(pRoot->_pLeft); cout << pRoot->_key << "->"; _inorder(pRoot->_pRight); }
自此我们的红黑树插入操作就完成了。
- 完整代码请戳:
https://coding.net/u/Hyacinth_Dy/p/MyCode/git/blob/master/%E7%BA%A2%E9%BB%91%E6%A0%91%E5%AE%8C%E6%95%B4%E7%89%88