C++ RBTree

目录

概念

性质

节点的定义

树的结构

 Insert

1.  pparent->_left == parent

1.1 uncle && uncle->_col = RED

1.2   !(uncle && uncle->_col == RED)

1.2.1  parent->_left == cur

1.2.2  parent->_right== cur

2.  pparent->_right== parent

 rotateR

rotateL

void InOrder()

bool isbalance()

总结


概念

  • 一种搜索二叉树,确保没有一条路径会大于最小路径的两倍

性质

  • 每个节点不是黑就是红
  • 根节点是黑
  • 红色节点的孩子都为黑色
  • 每个节点的往下的每条路径的黑色节点数目相同
  • 叶子节点(NIL)为nullptr节点都是黑色

节点的定义

enum colour
{
	RED
	,BLACK
};
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	colour _col;

	RBTreeNode(const pair<K, V>& kv)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};

树的结构

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> node;
public:
private:
	node* _root = nullptr;
};

 Insert

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new node(kv);
		_root->_col = BLACK;
		return true;
	}

	node* cur = _root;
	node* parent = cur;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}

	cur = new node(kv);
	if (parent->_kv.first < kv.first)
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	cur->_parent = parent;
	//改颜色,旋转
}

操作 

  • 先插入节点
  • 默认插入的颜色为红色
  • 父亲为红色就要改变
  • 按照uncle是否为红色讨论

注意 

  • 父亲不能为空
  • 父亲对于爷爷的左右会影响旋转的方向
while (parent && parent->_col == RED)
{
	node* pparent = parent->_parent;
	if (pparent->_left == parent)
	{
		node* uncle = pparent->_right;
		if (uncle && uncle->_col == RED)
		{
			//①
		}
		else
		{
			if (parent->_left == cur)
			{
				//②
			}
			else
			{
				//③
			}
			break;
		}
	}
	else
	{
		node* uncle = pparent->_left;
		if (uncle && uncle->_col == RED)
		{
			//④
		}
		else
		{
			if (parent->_right == cur)
			{
				//⑤
			}
			else
			{
				//⑥
			}
			break;
		}
	}
	_root->_col = BLACK;
}

1.  pparent->_left == parent

1.1 uncle && uncle->_col = RED

  • 只有在uncle为红的情况下,从跟开始的路径的黑色节点数才会增加1

①处代码

parent->_col = BLACK;
uncle->_col = BLACK;
pparent->_col = RED;

cur = pparent;
parent = pparent->_parent;

 解释

  • 肯定是parent变黑,而不是新插入的节点;如果是插入的节点变黑,那么整个路径都不对了,每次插入就一定要调整,所以开始就初始化为红色;其实也不难理解,父亲是红色,cur(newnode)也是红色,但是pparent一定是黑色,如果这时候有uncle且为红色,那么把爷爷的黑色给给parent和uncle,自己变成红色;那么此时,对于爷爷的后代节点的路径黑色节点的数目不变,若pparent就是_root那么爷爷再变成黑色(放到最后处理)

1.2   !(uncle && uncle->_col == RED)

1.2.1  parent->_left == cur

旋转解释及注意事项

②处代码

rotateR(pparent);

//cur->_col = RED;
parent->_col = BLACK;
pparent->_col = RED; //这个之前一定是黑
  • 简单来说就是,旋转之后parent位置颜色变成黑色,cur 和 pparent 变成红色,pparent在旋转之前一定是黑色,因为parent是红色
1.2.2  parent->_right== cur

  • 和AVLTree的旋转一模一样

③处代码

rotateL(parent);
rotateR(pparent);

cur->_col = BLACK;//这竟然写错了
pparent->_col = RED;

2.  pparent->_right== parent

  • 2和1可以没什么区别

  else部分代码

else
{
	node* uncle = pparent->_left;
	if (uncle && uncle->_col == RED)
	{
		uncle->_col = BLACK;
		parent->_col = BLACK;
		pparent->_col = RED;
	}
	else
	{
		if (parent->_right == cur)
		{
			rotateL(pparent);

			parent->_col = BLACK;
			pparent->_col = RED;
		}
		else
		{
			rotateR(parent);
			rotateL(pparent);

			cur->_col = BLACK;
			pparent->_col = RED;
		}
		break;
	}
}

 rotateR

void rotateR(node* parent)
{
	node* subL = parent->_left;
	node* subLR = subL->_right;

	parent->_left = subLR;
	if (subLR)
		subLR->_parent = parent;

	node* pparent = parent->_parent;

	subL->_right = parent;
	parent->_parent = subL;

	if (parent == _root) //其实就是pparent为空,所以下面不用担心空指针解引用
	{
		_root = subL;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = subL;
		}
		else
		{
			pparent->_right = subL;
		}
	}
	subL->_parent == pparent;
}

rotateL

void rotateL(node* parent)
{
	node* subR = parent->_right;
	node* subRL = subR->_left;

	parent->_right = subRL;
	if (subRL)
		subRL->_parent = parent;

	node* pparent = parent->_parent;

	subR->_left = parent;
	parent->_parent = subR;

	if (parent == _root)
	{
		_root = subR;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = subR;
		}
		else
		{
			pparent->_right = subR;
		}
	}
	subR->_parent = pparent;
}
void InOrder()
{
	_InOrder(_root);
	cout << endl;
}

 左右旋详解

void InOrder()

public:
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	void _InOrder(node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << "->" << root->_col << endl;
		_InOrder(root->_right);
	}

bool isbalance()

public:
	bool isbalance()
	{
		if (_root->_col == RED)
		{
			return false;
		}
		return _isbalance(_root, 0, 0);
	}
private:
	bool _isbalance(node* root, int numofblack, int prev)
	{
		if (root == nullptr)
		{
			if (prev == 0)
			{
				prev = numofblack;
			}
			else
			{
				if (prev != numofblack)
				{
					return false;
				}
			}
			return true;
		}

		if (root->_col == BLACK) //nullprt在上面就被刷掉了
			numofblack++;

		if (root->_parent && root->_parent->_col == RED && root->_col == RED)
		{
			return false;
		}
		//中序效率是不是有点低,但是从numofblack++来看好像只能这样
		return _isbalance(root->_left, numofblack, prev) && _isbalance(root->_right, numofblack, prev);
	}

思路

  • 检查每条路径下黑色节点数目是否一样
  • 检查相邻的是否都为红色,从cur回找parent即可

 解释

  • prev的作用是保存第一条路径的长度,且只被赋值一次

总结

  • RBTree的想法比较抽象,不然AVLTree来的易懂
  • RBTree的颜色改变比较简单,AVLTree双旋的时候_bf调节比较复杂,其时也不复杂
  • 最需重要理解的点:RBTree的5条性质,可以保证最长路径小于最短路径的两倍

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值