红黑树详解

概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是 R e d Red Red B l a c k Black Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的性质

在这里插入图片描述

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

其中的性质3和性质4保证了其最长路径中节点个数不会超过最短路径节点个数的两倍。

模拟实现

节点的定义

红黑树的节点中多了颜色,但是只有红和黑两种,所以可以使用枚举来表示:

enum Color
{
	BLACK,
	RED
};


template<typename K, typename V>
struct RBNode
{
	RBNode(const pair<K, V>& kv)
		: _kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}

	RBNode<K, V>* _left;
	RBNode<K, V>* _right;
	RBNode<K, V>* _parent;
	pair<K, V> _kv;
	Color _col;
};

其中新节点的颜色默认是红色的,因为性质3比性质4更好修正。

插入

与AVL树一样分为两步:

  1. 按照二叉搜索的树规则插入新节点
// 空树的插入
if (_root == nullptr)
{
	_root = new Node(kv);
	_root->_col = BLACK;
	return true;
}

// 非空树的插入
Node* parent = nullptr;
Node* cur = _root;

// 寻找插入的位置
while (cur)
{
	parent = cur;

	if (kv > cur->_kv)
	{
		cur = cur->_right;
	}
	else if (kv < cur->_kv)
	{
		cur = cur->_left;
	}
	else	// 找到重复元素,插入失败
	{
		return false;
	}
}

// 开始插入
cur = new Node(kv);
if (kv > parent->_kv)
{
	cur->_parent = parent;
	parent->_right = cur;
}
else
{
	cur->_parent = parent;
	parent->_left = cur;
}
  1. 根据新插入的节点判断是否符合红黑树的性质

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

此时cur和parent一定为红,并且grandparent一定为黑(如果为红,说明之前的插入就有问题),唯一的变数就是uncle:

在这里插入图片描述

代码:

// 如果父节点是根节点,那么它的颜色一定是要黑色,不然在前面插入的时候已经出错了
// 所以进入循环,就说明一定存在祖父节点
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;
		}
		else	// 叔叔不存在或者叔叔为黑
		{
			if (cur == parent->_left)	// 右单旋
			{
				//       g
				//      / \
				//     p   u
				//    /
				//   c 
				RotateR(grandfather);
				parent->_col = BLACK;
				grandfather->_col = RED;
			}
			else	// 左右双旋
			{
				//        g
				//       / \
				//      p   u
				// 		 \					   
				//        c
				//RotateL(parent);
				//RotateR(grandfather);
				RotateLR(grandfather);
				cur->_col = BLACK;
				grandfather->_col = RED;
			}
				

			break;
		}
	}
	else if (parent == grandfather->_right)
	{
		Node* uncle = grandfather->_left;
		if (uncle && uncle->_col == RED)	// 叔叔存在且为红,继续往上更新
		{
			parent->_col = uncle->_col = BLACK;
			grandfather->_col = RED;
			cur = grandfather;
		}
		else	// 叔叔不存在或者叔叔为黑
		{
			if (cur == parent->_right)	// 左单旋
			{
				//       g
				//      / \
				//     u   p
				//          \
				//           c
				RotateL(grandfather);
				parent->_col = BLACK;
				grandfather->_col = RED;
			}
			else	// 右左双旋
			{
				//       g
				//      / \
				//     u   p
				//        /
				//       c
				//RotateR(parent);
				//RotateL(grandfather);
				RotateRL(grandfather);
				cur->_col =	 BLACK;
				grandfather->_col = RED;
			}

			break;
		}
	}
}

_root->_col = BLACK;


// 左单旋
void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	Node* pparent = parent->_parent;

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

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

	subR->_parent = pparent;
	if (pparent)
	{
		if (pparent->_left == parent)
			pparent->_left = subR;
		else
			pparent->_right = subR;
	}
	else
		_root = 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;

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

	subL->_parent = pparent;
	if (pparent)
	{
		if (pparent->_left == parent)
			pparent->_left = subL;
		else
			pparent->_right = subL;
	}
	else
		_root = subL;
}


// 左右双旋
void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	RotateL(subL);
	RotateR(parent);	
}

// 右左双旋
void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	RotateR(subR);
	RotateL(parent);
}

红黑树的验证

我们可以根据性质4来判断是否平衡:对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点

// 根据黑色节点数量判断是否满足红黑树条件
bool IsBalance()
{
	if (_root && _root->_col == RED)
	{
		puts("root的颜色为红!!!");
		return false;
	}

	int balanceNum = 0;
	return Check(_root, balanceNum, 0);	
}


// 前序遍历检查每天路径上的黑色节点数量
bool Check(Node* root, int& balanceNum, int blackNum)
{
	if (root == nullptr)
	{
		if (balanceNum == 0)
		{
			balanceNum = blackNum;
			return true;
		}
		else
		{
			if (blackNum == balanceNum)
				return true;
			else 
				return false;
		}
	}

	if (root->_col == BLACK)
	{
		blackNum++;
	}

	return Check(root->_left, balanceNum, blackNum)
		&& Check(root->_right, balanceNum, blackNum);
}

红黑树的删除

可以参考这两篇文章:

红黑树删除一

红黑树删除二

性能总结

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是 O ( l o g 2 N ) O(log_2 N) O(log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_featherbrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值