【数据结构】红黑树

一.红黑树的概念与性质

1.概念

红黑树是二叉搜索数的一种, 相比于AVL树(二叉平衡搜索树)红黑树通过减少旋转的次数来进一步优化了查找效率, 在每个节点上增加一个存储位表示节点的颜色, Red or Black, 通过对任何一条从根到叶子的路径上各个节点着色方式的限制, 红黑树确保没有一条路径会比其他路经长超出两倍, 故是接近平衡的, 红黑树通过控制五条规则来确保它的近平衡性, 从而既不频繁旋转, 又保证效率在logN级别

2.性质

1).每个节点不是红色就是黑色

2).根节点总是黑色

3).每条路径的NIL节点都是黑色的

4).每条路径的黑色节点数量必须相同

5).每条路径不能用两个连续的红色节点

二.红黑树insert模拟实现

1.为何插入的新节点都是红色的

插入新节点为红色, 然后再根据规则向上调整, 只调整当前分支即可, 如果插入新节点为黑色, 根据红黑树的要求每条路径黑色节点都相同, 则需要调整所有路径, 故为了便于实现, 默认插入的新节点都为红色

2.红黑树插入节点的三种情况

父亲为黑不需要处理

情况一: 父节点为红, 叔叔节点为红

情况二: 父节点为红, 叔叔节点为空/黑, 新插入节点一边倒, 单旋

情况三: 父节点为红, 叔叔节点为空/黑, 新插入节点在中间, 双旋

3.代码

#define _CRT_SECURE_NO_WARNINGS 1

enum Color
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _col;
	pair<K, V> _kv;
	//构造
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_col(RED)//默认添加的新节点为红色
		,_kv(kv)
	{}
};

template<class K, class V>
class RBTree
{
public:
	typedef RBTreeNode<K, V> Node;
	bool insert(const pair<K, V>& val)
	{
		if (root == nullptr)
		{
			//如果此时为空树
			root = new Node(val);
			//将根节点修正为黑色
			root->_col = BLACK;
			return true;
		}
		Node* cur = root;
		Node* parent = nullptr;//cur的父节点
		while (cur)
		{
			if (cur->_kv.first > val.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_kv.first < val.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else//(cur->_kv.first == val.first)
			{
				//如果插入的节点是重复值, 则插入失败
				return false;
			}
		}
		cur = new Node(val);
		if (parent->_kv.first > cur->_kv.first)
		{
			parent->_left = cur;
		}
		else if (parent->_kv.first < cur->_kv.first)
		{
			parent->_right = cur;
		}
		cur->_parent = parent;
		//以上为插入节点
		//-------------------------------------------------------
		//以下为调整为红黑树
		//因为默认插入的节点为红色,所以如果出现了两个连续为红的节点就需要处理
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			Node* uncle = nullptr;
			//确定叔叔节点的位置
			if (grandfather->_left == parent)
			{
				uncle = grandfather->_right;
			}
			else//grandfather->_right == parent
			{
				uncle = grandfather->_left;
			}
			//将分为三种情况
			//1.父节点为红,叔叔节点存在且为红(变色 + 向上迭代)
			//2/3.父节点为红,叔叔节点不存在或者存在且为黑(旋转 + 变色)
			if (uncle && uncle->_col == RED)//情况一
			{
				//父变黑,叔叔变黑,祖父变红->向上迭代
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->_parent;
			}
			else//情况二/三
			{
				//情况二
				//     g
				//   p   u
				// c
				if (uncle == grandfather->_right && cur == parent->_left)
				{
					//右单旋
					RotateR(grandfather);
					//
					parent->_col = BLACK;
					grandfather->_col = RED;
					break;
				}
				//     g
				//   u   p
				//         c 
				else if (uncle == grandfather->_left && cur == parent->_right)
				{
					//左单旋
					RotateL(grandfather);
					//
					parent->_col = BLACK;
					grandfather->_col = RED;
					break;

				}
				//情况三
				//     g
				//   u   p
				//     c
				else if (uncle == grandfather->_left && cur == parent->_left)
				{
					//左双旋
					RotateRL(grandfather);
					//
					grandfather->_col = RED;
					cur->_col = BLACK;
					break;

				}
				//     g
				//   p   u
				//     c
				else if (uncle == grandfather->_right && cur == parent->_right)
				{
					//右双旋
					RotateLR(grandfather);
					//
					grandfather->_col = RED;
					cur->_col = BLACK;
					break;

				}
				else
				{
					cout << "不存在这种情况" << endl;
					exit(-1);
				}
			}
		}
		root->_col = BLACK;
		return true;
	}

	void inorder()
	{
		_inorder(root);
	}
    //检查是否为红黑树
	bool isRBTree()
	{
		if (root->_col == RED)
		{
			cout << "出错: 根节点为红" << endl;
			return false;
		}
		//判断是否有连续红节点,且每条路径的黑节点是否相等
		int benchmark = 0;//算出最左路径的黑节点个数
		Node* cur = root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++benchmark;
			}
			cur = cur->_left;
		}
		return _isRBTree(root, 0, benchmark);
	}

private:
	//四种旋转
	void RotateL(Node* prev)
	{
		Node* subR = prev->_right;
		Node* subRL = subR->_left;
		Node* ppNode = prev->_parent;

		prev->_right = subRL;
		if (subRL)
		{
			subRL->_parent = prev;
		}

		subR->_left = prev;
		prev->_parent = subR;

		if (root == prev)
		{
			root = subR;
		}
		else
		{
			if (ppNode->_left == prev)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
		}
		subR->_parent = ppNode;
	}

	void RotateR(Node* prev)
	{
		Node* subL = prev->_left;
		Node* subLR = subL->_right;
		Node* ppNode = prev->_parent;

		subL->_right = prev;
		prev->_parent = subL;

		prev->_left = subLR;
		if (subLR)
		{
			subLR->_parent = prev;
		}

		if (root == prev)
		{
			root = subL;
		}
		else
		{
			if (ppNode->_left == prev)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
		}
		subL->_parent = ppNode;
	}

	void RotateRL(Node* prev)
	{
		//先右旋, 再左旋
		RotateR(prev->_right);
		RotateL(prev);
	}

	void RotateLR(Node* prev)
	{
		//先左旋, 再右旋
		RotateL(prev->_left);
		RotateR(prev);
	}

	void _inorder(Node* root)
	{
		if (root)
		{
			_inorder(root->_left);
			cout << root->_kv.first << "--" << root->_kv.second << endl;
			_inorder(root->_right);
		}
	}
    //检查是否为红黑树
	bool _isRBTree(Node* root, int blackNum, int benchmark)
	{
		if (root == nullptr)//走到空节点
		{
			if (benchmark == blackNum)
			{
				//for debug
				//cout << blackNum << endl;
				return true;
			}
			else
			{
				//for debug
				//cout << blackNum << endl;
				cout << "不是所有路径的黑色节点个数都相同" << endl;
				return false;
			}
		}
		if (root->_col == BLACK)
		{
			++blackNum;
		}
		//判断是否有连续的红节点
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "出现了连续的红色节点" << endl;
			return false;
		}
		return _isRBTree(root->_left, blackNum, benchmark)
			&& _isRBTree(root->_right, blackNum, benchmark);
	}

	Node* root = nullptr;
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值