二叉搜索树-红黑树
2017-11-14 15:14
232 查看
前面介绍了AVL树,虽然AVL树将二叉树的高度差保证在1,但是实现的太过复杂,因为要不断调整平衡因子。故而要来介绍另外一个用途比较广的结构-红黑树。
1、每个节点非红即黑
2、根节点为黑色
3、不能有连续的红节点
4、每条路径上的黑色节点数相等
5、空节点为黑色
先来想一个问题,红黑树的定义保证它最长路径不会超过最短路径的二倍,那么来想想为什么?
![](https://img-blog.csdn.net/20171114124653397?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzY1MjgxMTQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
至于旋转的过程,红黑树和AVL树都是一样的。
插入时需要注意的是,第四条规则:每条路径上的黑色节点数相等,所以我们可以在初始化节点的时候直接默认是红色的节点,这样就可以避免冲突这条规则。
下面这段代码在实现普通搜索树,AVL树,红黑树都是一样的。
一共分三种情况:
一:叔叔存在且为红
![](https://img-blog.csdn.net/20171114142004437?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzY1MjgxMTQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
如果是这种情况,直接将父节点和叔叔节点置为红色,也不需要进行旋转,至于为什么将祖父节点置为红色?
1、当前节点的祖父节点是根节点(程序的最后直接将整棵树的根置黑)
2、当前节点的祖父节点不是根
讨论不是根,如图:
![](https://img-blog.csdn.net/20171114142806092?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzY1MjgxMTQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
二:叔叔不存在/存在为黑(cur为p的左)
![](https://img-blog.csdn.net/20171114143757675?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzY1MjgxMTQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
这里的叔叔只要不是存在且为红,就不影响旋转。
以p为根进行一次右单旋(镜像式的左旋类似),然后将p置为黑,g置为红
,因为以g为根的子树上的路径黑色节点个数并没有增加,故不用将旋转后的p在置为红继续向上调整。
三:叔叔不存在/存在为黑(cur为p的右)
![](https://img-blog.csdn.net/20171114150456206?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzY1MjgxMTQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
仔细想一下,这里的第三种是先左旋转化为第二种,再进行左旋后,当前节点的cur和parent对应到第二种的结构中刚好是相反的,所以再进行左旋前/后,要将cur和parent两个节点进行swap,才能转化为第二种结构进行处理。
整个调整过程应该是一个循环来控制:
再进行完动态调整后,最后直接了当将整棵树的根节点变为黑色,从而满足第一条性质。
如果对于旋转部分还不太了解可以先了解AVL树的实现部分,两部分的旋转逻辑一模一样
附上整个简单实现的代码:
红黑树
先来看来红黑树的特性:1、每个节点非红即黑
2、根节点为黑色
3、不能有连续的红节点
4、每条路径上的黑色节点数相等
5、空节点为黑色
先来想一个问题,红黑树的定义保证它最长路径不会超过最短路径的二倍,那么来想想为什么?
节点的结构
因为搜索结构在实际运用当中都是采用这种Key,Value模型,所以这里就先这样实现,后面对RBTree进行封装时,会做一些小的调整。enum COLOUR{RED,BLACK}; template <class K,class V> struct RBTreeNode { RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent;//因为要涉及到调平衡,所以定义为三叉链 K _key; V _value; COLOUR _colour;//枚举类型的颜色属性,每一个节点非红即黑 };
插入
基本的插入和之前的普通搜索树,AVL树都是一样的,先找到待插入位置,然后进行插入即可,主要的不同就是在动态调整。至于旋转的过程,红黑树和AVL树都是一样的。
插入时需要注意的是,第四条规则:每条路径上的黑色节点数相等,所以我们可以在初始化节点的时候直接默认是红色的节点,这样就可以避免冲突这条规则。
下面这段代码在实现普通搜索树,AVL树,红黑树都是一样的。
if (_root == NULL)//先判断空树的场景 { Node* _root = new Node(key, value); _root->_colour = BLACK; return true; } Node* cur = _root; Node* parent = NULL; while (cur) { if (key < cur->_key) { parent = cur->_parent; cur = cur->_left; } else if (key > cur->_key) { parent = cur; cur = cur->_right; } else { assert(false); } } //走到这里,找到待插入点 cur = new Node(key, value); if (key < cur->_key) { parent->_left = cur; cur->_parent = parent; } else if (key > cur->_key) { parent->_right = cur; cur->_parent = parent; } //走到这里说明节点插入进入了,核心的思想就是下面要进行的动态调整
动态调整
下面图示中cur代表当前节点,parent/p代表父节点,uncle/u代表叔叔节点,grandfather/g代表祖父节点。一共分三种情况:
一:叔叔存在且为红
如果是这种情况,直接将父节点和叔叔节点置为红色,也不需要进行旋转,至于为什么将祖父节点置为红色?
1、当前节点的祖父节点是根节点(程序的最后直接将整棵树的根置黑)
2、当前节点的祖父节点不是根
讨论不是根,如图:
二:叔叔不存在/存在为黑(cur为p的左)
这里的叔叔只要不是存在且为红,就不影响旋转。
以p为根进行一次右单旋(镜像式的左旋类似),然后将p置为黑,g置为红
,因为以g为根的子树上的路径黑色节点个数并没有增加,故不用将旋转后的p在置为红继续向上调整。
三:叔叔不存在/存在为黑(cur为p的右)
仔细想一下,这里的第三种是先左旋转化为第二种,再进行左旋后,当前节点的cur和parent对应到第二种的结构中刚好是相反的,所以再进行左旋前/后,要将cur和parent两个节点进行swap,才能转化为第二种结构进行处理。
整个调整过程应该是一个循环来控制:
while(parent && parent->_color == RED);
再进行完动态调整后,最后直接了当将整棵树的根节点变为黑色,从而满足第一条性质。
如果对于旋转部分还不太了解可以先了解AVL树的实现部分,两部分的旋转逻辑一模一样
附上整个简单实现的代码:
#pragma once #include<iostream> #include<assert.h> enum COLOUR{RED,BLACK}; template <class K,class V> struct RBTreeNode { RBTreeNode(const K& key, const V& value) : _left(NULL) , _right(NULL) , _parent(NULL) , _key(key) , _value(value) , _colour(RED) {} RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; K _key; V _value; COLOUR _colour; }; template <class K,class V> class RBTree { typedef RBTreeNode<K, V> Node; public: RBTree() : _root(NULL) {} bool Insert(const K& key, const V& value) { if (_root == NULL) { Node* _root = new Node(key, value); _root->_colour = BLACK; return true; } Node* cur = _root; Node* parent = NULL; while (cur) { if (key < cur->_key) { parent = cur->_parent; cur = cur->_left; } else if (key > cur->_key) { parent = cur; cur = cur->_right; } else { assert(false); } } //走到这里,找到待插入点 cur = new Node(key, value); if (key < cur->_key) { parent->_left = cur; cur->_parent = parent; } else if (key > cur->_key) { parent->_right = cur; cur->_parent = parent; } //动态调整节点 while (parent && parent->_colour == RED ) { Node* grandfather = parent->_parent; if (parent == grandfather->_left) { Node* uncle = grandfather->_right; if (uncle && uncle->_colour == RED) { parent->_colour = uncle->_colour = BLACK; grandfather->_colour = RED; cur = grandfather; parent = cur->_parent; } else { if (cur == parent->_right) { //BUG 第一次旋转后改变了parent和cur的位置,导致第二次旋转后颜色设置错误 std::swap(parent, cur); RotateL(parent); } RotateR(grandfather); grandfather->_colour = RED; parent->_colour = BLACK; break; } } else { Node* uncle = grandfather->_left; if (uncle && uncle->_colour == RED) { parent->_colour = uncle->_colour = BLACK; grandfather->_colour = RED; cur = grandfather; parent = cur->_parent; } else { if (cur == parent->_left) { std::swap(parent, cur); RotateR(parent); } RotateL(grandfather); grandfather->_colour = RED; parent->_colour = BLACK; break; } } } _root->_colour = BLACK; return true; } bool CheckBalance() { size_t k = 0; if (NULL == _root) return true; if (RED == _root->_colour) { std::cout << "根节点为红" << " "; return false; } size_t BlankCount = 0; Node* cur = _root; while (cur) { if (BLACK == cur->_colour) { BlankCount++; cur = cur->_left; } } _CheckBalance(_root,BlankCount,k); } void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_right; Node* pparent = parent->_parent; parent->_right = subR->_left; if (subRL) { subRL->_parent = parent; } subR->_left = parent; parent->_parent = subR; while (NULL == pparent) { _root = subR; subR->_parent = NULL; } if (parent == pparent->_left) pparent->_left = subR; else if (parent == pparent->_right) pparent->_right = subR; subR->_parent = pparent; parent = subR; } void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; Node* pparent = parent->_parent; parent->_left = subLR; if (subLR) { subLR->_parent = parent; } subL->_right = parent; parent->_parent = subL; while (NULL == pparent) { _root = subL; subL->_parent = NULL; } if (parent == pparent->_left) parent->_left = subL; else if (parent == pparent->_right) parent->_right = subL; subL->_parent = pparent; parent = subL; } bool _CheckBalance(Node* root, size_t BlankCount, size_t k) { if (NULL == root) return true; if (BLACK == root->_colour) k++; Node* parent = root->_parent; if (root->_colour == RED && parent->_colour == RED) { std::cout << "相邻的红节点" << " "; return false; } if (root->_left == NULL && root->_right == NULL) { if (k != BlankCount) { std::cout << "黑色数量不相等" << " "; return false; } return true; } return (_CheckBalance(root->_left, BlankCount, k) && _CheckBalance(root->_right, BlankCount, k)); } private: Node* _root; };
相关文章推荐
- 树——(1)综述:二叉树,线索二叉树,二叉搜索树,B-/B+树,AVL树,红黑树
- 数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树
- 关于树的总结从二叉树->二叉搜索树->平衡二叉树->红黑树->B树与B+树
- 红黑树实现(二叉搜索树)
- 二叉搜索树和红黑树概述以及模板实现(1)
- 红黑树、二叉搜索树的实现和性能比较(c++实现红黑树)
- 二叉搜索树和红黑树概述以及模板实现(2)--红黑树
- 数据结构-树(多叉树、二叉树、二叉搜索树、平衡二叉树、字典树、红黑树、线段树)
- PAT 1135. Is It A Red-Black Tree (30) 二叉搜索树建立 + 红黑树判断
- PHP实现绘制二叉树图形显示功能详解【包括二叉搜索树、平衡树及红黑树】
- 二叉搜索树,AVL,红黑树,B树,哈希表,位图的比较
- 非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树
- 二叉搜索树—RBTree(红黑树)
- 红黑树(二叉搜索树的一种)
- 一个链式二叉搜索树接口(为下一篇红黑树的内容做铺垫)
- [面试]红黑树性质,BST(二叉搜索树)、AVL(平衡二叉搜索树)、RBT(红黑树)、B-Tree(B-树和B+树)
- 数据结构思维导图系列之 二叉搜索树删除思维导图 红黑树删除思维导图
- 二叉搜索树升级版之红黑树
- 二叉搜索树—RBTree(红黑树)
- 红黑树算法原理(从二叉搜索树讲起)