C++手撕红黑树

目录

一、什么是红黑树

二、红黑树的约定

三、红黑树vsAVL

1.调整平衡的实现机制不同

2.红黑树的插入效率更高

3.AVL查找效率高

四、红黑树的实现

1.找到插入的位置

2.控制平衡

(1)当父节点为黑

(2)判断父节点再祖父节点的位置

(3)叔叔节点存在且为红

(4)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点的左侧

(5)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点的右侧

五 代码


一、什么是红黑树

红黑树的表意就是一颗每个节点带有颜色的二叉搜索树,并通过对节点颜色的控制,使该二叉搜索树达到尽量平衡的状态。所谓尽量平衡的状态就是:红黑树确保没有一条路径比其他路径长两倍。和AVL树不同的是,AVL树是一棵平衡树,而红黑树可能平衡也可能不平衡(因为是尽量平衡的状态)

二、红黑树的约定

要实现一棵红黑树,即要红黑树确保没有一条路径比其他路径长两倍。通过对节点颜色的约定来实现这一目标。

1.根节点是黑色的

2.如果一个节点是红色的,则它的两个孩子都是黑色的

3.对于每个节点,从该节点到其他后代节点的简单路径上,均包含相同的黑色节点。

实现了这三条颜色规则的二叉搜索树,即也实现了没有一条路径比其他路径长两倍,即实现了一棵红黑树。

注意性质三所说的叶子节点是空节点,空节点都看成是黑色节点。

三、红黑树vsAVL

1.调整平衡的实现机制不同

红黑树根据节点颜色(同一父节点出发到叶子节点,所有路径上的黑色节点数目一样),一些约定和旋转实现。

AVL根据树的平衡因子(所有节点的左右子树高度差的绝对值不超过1)和旋转决定。

2.红黑树的插入效率更高

红黑树是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,红黑树并不追求“完全平衡”,它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能

而AVL是严格平衡树(高度平衡的二叉搜索树),因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多。所以红黑树的插入效率更高

3.AVL查找效率高

如果你的应用中,查询的次数远远大于插入和删除,那么选择AVL树,如果查询和插入删除次数几乎差不多,应选择红黑树。即,有时仅为了排序(建立-遍历-删除),不查找或查找次数很少,R-B树合算一些。

四、红黑树的实现

实现一棵红黑树,本质是实现它的插入函数,使插入函数可以实现红黑树的颜色约定,它的实现分为两步,即先找到插入的位置,再控制平衡。插入函数返回值设计为bool,插入成功返回true,失败返回false。控制平衡时,需要关注四个节点,即新插入的节点,它的父节点,它的叔叔节点,它的祖父节点。

1.找到插入的位置

当为第一个节点的时候,颜色设为黑,直接插入。

当插入的不是第一个节点时,颜色设为红,需要根据二叉搜索树的性质找到位置。并实现三叉链。

		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = Black;
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		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);
		cur->_col= Red;
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

2.控制平衡

(1)当父节点为黑

当父节点为黑的时候,由于插入的子节点的颜色为红,对三个约定没有任何影响,因此不需要调整平衡。

(2)判断父节点再祖父节点的位置

通过判断父节点在祖父节点的位置,来定义叔叔节点的位置,以及之后的旋转方向的判断。

while (parent&& parent->_col == Red)
{
	Node* grandfather = parent->_parent;
	if (parent == grandfather->_right)
	{
		Node* uncle = grandfather->_right;
		//
	}
	else
	{
		Node* uncle = grandfather->_left;
		//
	}
}

首先需要使用大循环,这个循环是为情况1而准备的,情况2和3直接跳出循环即可,因为只有情况1对上层循环有影响。

下面我们以父节点在祖父节点的左侧为例,右侧同理。

(3)叔叔节点存在且为红

解决方案:将父节点和叔叔节点设为黑,将祖父节点设为红。然后将祖父节点作为新节点继续向上平衡。如果祖父节点是根节点,那么需要再将其置为黑。

注意:这种情况和其它情况不同的是,需要将祖父节点作为新插入的节点继续向上遍历,这说明需要一个循环。而其他类型的情况直接break跳出这个循环即可。

if (uncle&& uncle->_col == Red)
{
				parent->_col = uncle->_col = Black;
				grandfather->_col = Red;
				cur = grandfather;
				parent = cur->_parent;
}

这种情况只需要控制颜色即可,但是要继续向上循环。

(4)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点的左侧

解决方案:对祖父节点右旋操作,并将祖父节点置为红,父节点置为黑。

if (cur == parent->_left)
{
	RotateR(grandfather);
	parent->_col = Black;
	grandfather->_col = Red;
}
(5)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点的右侧

解决方案:进行双旋,即对父节点进行左单选,祖父节点进行右单旋。将子节点置为黑,将祖父系欸点置为红。

else
{
	RotateL(parent);
    RotateR(grandfather);
	cur->_col = Black;
	grandfather->_col = Red;
}
(6)最后置黑

每一次插入都对根节点置为黑操作,因为第一种情况可能导致根节点不是黑。同时对根节点置黑也并不违反三条规定。

3.测试代码

当我们处理完父节点在祖父节点的左侧后,处理父节点在祖父节点的右侧。

全部处理之后,我们的插入代码就完成了,接下来要对整个树进行测试,即对三个约定来进行测试:

1.当根节点为红时,返回false

2.判断一个节点和其父节点的颜色否都为红,若都为红返回false。
3.以最左的一条路径上的根节点数量为基准,通过递归遍历每一条路径上的黑色节点的数量,当每条路径遍历节点到空时,将两者进行比较,如果最终结果不相等则返回false。

bool IsBalance()
{
	if (_root->_col == RED)
	{
		return false;
	}
	int refNum = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == Black)
		{
			++refNum;
		}
		cur = cur->_left;
	}
	return Check(_root, 0, refNum);
}
bool Check(Node* root, int blackNum, const int refNum)
{
	if (root == nullptr)
	{
		if (refNum != blackNum)
		{
			cout << "存在黑色节点数量不相等的路径" << endl;
			return false;
		}
		return true;
	}
	
	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout << root->_kv.first << "存在连续红色节点" << endl;
		return false;
	}
	if (root->_col == Black)
	{
		blackNum++;
	}
	return Check(root->_left, blackNum, refNum)
		&& Check(root->_right, blackNum, refNum);
}

五 代码

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;

enum Color
{
	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;
	Color _col;
	RBTreeNode(const pair<K,V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_col(Red)
		,_kv(kv)
	{}
};

template <class K,class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	void RotateR(Node* parent)
	{
		Node* sub = parent->_left;
		Node* subR = sub->_right;

		parent->_left = subR;
		if (subR)
			subR->_parent = parent;
		
		if (parent == _root)
		{
			_root = sub;
		}
		else
		{
			Node* ppNode = parent->_parent;
			if (ppNode->_left == parent)
			{
				ppNode->_left = sub;
			}
			else
			{
				ppNode->_right = sub;
			}
			sub->_parent = ppNode;
		}
		sub->_right = parent;
		parent->_parent = sub;
	}

	void RotateL(Node* parent)
	{
		Node* sub = parent->_right;
		Node* subL = sub->_left;

		parent->_right = subL;
		if (subL)
			subL->_parent = parent;
		if (parent == _root)
		{
			_root = sub;
		}
		else
		{
			Node* ppNode = parent->_parent;
			if (ppNode->_left = parent)
			{
				ppNode->_left = sub;
			}
			else
			{
				ppNode->_right = sub;
			}
			sub->_parent = parent;
		}
		sub->_left = parent;
		parent->_parent = sub;
	}
	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = Black;
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(kv);
		cur->_col = Red;
		if (kv.first < parent->_kv.first)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		while (parent&& parent->_col == Red)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				if (uncle&& uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = Black;
						grandfather->_col = Red;
					}
					break;
				}
				_root->_col = Black;
			}
			else
			{
				Node* uncle = grandfather->_left;
				
				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = Black;
						grandfather->_col = Red;
					}
					break;
				}
				_root->_col = Black;
			}
		}
	}
	bool IsBalance()
	{
		if (_root->_col == Red)
		{
			return false;
		}
		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == Black)
			{
				++refNum;
			}
			cur = cur->_left;
		}
		return Check(_root, 0, refNum);
	}
	bool Check(Node* root, int blackNum, const int refNum)
	{
		if (root == nullptr)
		{
			if (refNum != blackNum)
			{
				cout << "存在黑色节点数量不相等的路径" << endl;
				return false;
			}
			return true;
		}
		
		if (root->_col == Red && root->_parent->_col == Red)
		{
			cout << root->_kv.first << "存在连续红色节点" << endl;
			return false;
		}
		if (root->_col == Black)
		{
			blackNum++;
		}
		return Check(root->_left, blackNum, refNum)
			&& Check(root->_right, blackNum, refNum);
	}
	
private:

	Node* _root = nullptr;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值