_10-红黑树的插入

题意:

本道题测试通过不同的用例帮助你完成红黑树的插入,有几点需要注意:

  1. 节点的颜色以枚举RBTREECOLOR来表示,共有REDBLACKROOT三种颜色,ROOT是为了方便编码特地规定的虚拟根节点的颜色。请注意看模板代码。
  2. 依习惯,整个树挂在虚拟根节点-1的右节点上。-1节点的颜色为ROOT,可以方便判断根节点。
  3. 以红黑树定义来说,如果节点的左右子树为空,应当视为挂载了黑色节点,《算法导论》中采用一个特别的NIL节点来替代NULL本题不采用这一解法,还是为空指针,请注意处理空结点的颜色问题。
  4. 节点包含parent指针,请注意维护。

请你填写插入函数,

void insert_rb_node( BTreeNode* root, int value );

 思路:

大体分为节点的插入修复。

先再复述一下红黑树的性质:

        一、每个节点非红即黑。

        二、根节点是黑色。

        三、每个叶节点是黑色。

        四、红节点的叶子节点必为黑。

        五、对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

①插入。其过程与二叉搜索树相。需要注意两点:一是将插入的节点设置为RED,二是一定在叶子节点插入。

②修复。因为插入的节点我们设置为红色,若父节点为黑色,我们则不需要修复,即只需考虑父节点为红色的情况。我们分为下列四种情况:(将插入的节点设置为z、与z同一个父节点的兄弟节点称叔节点)

        一、z是根节点。

        二、z的叔节点是红色。

        三、z的叔节点是黑色,且z局部呈现三角形。

        四、z的叔节点是黑色,且z局部呈现直线。

        对于情况一,我们只需将其由红色改为黑色即可。

        对于情况二,我们选择将其父节点和叔节点颜色变为黑色,其祖父节点变为红色,并将z替换为z的祖父节点,再放到循环里去解决。

        对于情况三,我们选择将z的父节点进行右旋(左旋),并将z替换为z的父节点。这样做把情况三归结为情况四。

        对于情况四,我们选择将z的父节点变为黑色,z的祖父节点变为红色,并对z的祖父节点进行左旋(右旋)。

        ps:进行左旋还是右旋需要判断z的父节点属于z的祖父节点的左节点还是右节点,详情看后续代码。

 

③旋转。由于红黑树的树结构没有高度,所以它旋转时不用考虑高度,其余与二叉搜索树左右旋操作相似。

源码:

//
//enum RBTREECOLOR{
//    RED,
//    BLACK,
//    ROOT, // 专门定义一个root颜色方便处理
//};
//
//struct BTreeNode{
//    int value{0};
//    RBTREECOLOR color{RED};
//    BTreeNode* parent{nullptr};
//    BTreeNode* left_child{nullptr};
//    BTreeNode* right_child{nullptr};
//    BTreeNode( int value ){
//        this->value = value;
//    }
//    BTreeNode( int value, RBTREECOLOR color ){
//        this->value = value;
//        this->color = color;
//    }
//    BTreeNode(){
//        this->value = 0;
//    }
//};
void rotate_ll(BTreeNode* node) {
	if (!node) return;
	BTreeNode* t = node->left_child;
	node->left_child = t->right_child;
	if (t->right_child)
		t->right_child->parent = node;
	t->parent = node->parent;
	if (node->parent->right_child == node)
		node->parent->right_child = t;
	else
		node->parent->left_child = t;
	t->right_child = node;
	node->parent = t;
}
void rotate_rr(BTreeNode* node) {
	if (!node)return;
	BTreeNode* t = node->right_child;
	node->right_child = t->left_child;
	if (t->left_child)
		t->left_child->parent = node;
	t->parent = node->parent;
	if (node->parent->right_child == node)
		node->parent->right_child = t;
	else
		node->parent->left_child = t;
	t->left_child = node;
	node->parent = t;
}
void rotate_rl(BTreeNode* node) {
	BTreeNode* t = node->right_child;
	rotate_ll(t);
	rotate_rr(node);
}
void rotate_lr(BTreeNode* node) {
	BTreeNode* t = node->left_child;
	rotate_rr(t);
	rotate_ll(node);
}
void rb_insert_fixup(BTreeNode* z) {
	/*
	修复颜色,因为插入的点,我们总是将其先染成红色,
	所以如果父节点为黑色则不需要修复,如果父节点为红色,则需要修复。
	有四种情况:
		一、z是根节点
		二、z的叔节点y是红色
		三、z的叔节点y是黑色,且z是一个右孩子
		四、z的叔节点y是黑色,且z是一个左孩子
	*/
	if (z->parent == nullptr) {  //情况一
		z->color = BLACK;
		return;
	}
	while (z->parent->color == RED) {
		if (z->parent == z->parent->parent->left_child) {  //如果z的父节点是祖父节点的左节点
			BTreeNode* y = z->parent->parent->right_child;  //定义y为z的叔节点
			if (y&&y->color == RED) {  //情况二
				z->parent->color = BLACK;
				y->color = BLACK;
				z->parent->parent->color = RED;
				z = z->parent->parent;
			}
			else if (z == z->parent->right_child) {  //情况三
				z = z->parent;
				rotate_rr(z);
			}
			else {  //情况四
				z->parent->color = BLACK;
				z->parent->parent->color = RED;
				rotate_ll(z->parent->parent);
			}
		}
		else {  //如果z的父节点是祖父节点的右节点
			BTreeNode* y = z->parent->parent->left_child;
			if (y&&y->color == RED) {  //情况二
				z->parent->color = BLACK;
				z->parent->parent->left_child->color = BLACK;
				z->parent->parent->color = RED;
				z = z->parent->parent;
			}
			else if (z->parent == z->parent->parent->left_child) {  //情况三
				z = z->parent;
				rotate_ll(z);
			}
			else {  //情况四
				z->parent->color = BLACK;
				z->parent->parent->color = RED;
				rotate_rr(z->parent->parent);
			}
		}
	}
	while (z->parent->value != -1)
		z = z->parent;
	z->color = BLACK;
}
void insert_rb_node( BTreeNode* root, int value ){
BTreeNode* n = new BTreeNode(value);  //定义待插入节点n
	BTreeNode* y = nullptr;
	while (root) {  //二叉搜索寻找插入的位置
		y = root;
		if (root->value > value)
			root = root->left_child;
		else
			root = root->right_child;
	}
	n->parent = y;  //找到合适的插入位置,设置新节点的父节点
	if (!y)  //如果是空树
		root = n;
	else if (y->value > n->value)  //在y的左节点
		y->left_child = n;
	else                           //在y的右节点
		y->right_child = n;
	n->left_child = n->right_child = nullptr;
	rb_insert_fixup(n);
}

### 三、C++ STL 中红黑树节点颜色判断与旋转操作的代码逻辑 在 C++ STL 的红黑树实现中,为了维护红黑树的五条基本性质[^3],每当插入或删除一个节点时,都需要进行颜色调整和/或旋转操作。这些操作的核心在于对当前节点及其父节点、叔节点和祖父节点的颜色判断,并根据具体情况进行左旋、右旋或双旋操作。 #### 节点结构与颜色定义 红黑树节点通常包含以下字段:指向父节点、左子节点、右子节点的指针以及颜色标识(RED 或 BLACK)。典型的节点结构如下: ```cpp struct _Rb_tree_node_base { _Rb_tree_color_type _M_color; // 颜色类型,取值为红色或黑色 _Rb_tree_node_base* _M_parent; _Rb_tree_node_base* _M_left; _Rb_tree_node_base* _M_right; }; ``` 其中 `_M_color` 用于标识节点颜色,其枚举定义如下: ```cpp enum _Rb_tree_color_type { _S_red, _S_black }; ``` 这种结构使得每个节点都具备了参与旋转和颜色调整的基本信息[^1]。 #### 插入后颜色判断与旋转逻辑 当插入一个新节点时,默认将其颜色设为红色。如果父节点也为红色,则破坏了红黑树的第四条性质(红色节点不能有红色子节点)[^3],此时需要进行修复操作。 修复过程主要涉及以下几种情况: 1. **叔叔节点为红色** 此时只需将父节点和叔叔节点染黑,祖父节点染红,并将当前节点上移至祖父节点继续检查。 2. **叔叔节点为黑色且当前节点是右孩子** 先对父节点进行左旋,使当前节点变为左孩子,转换为第三种情况。 3. **叔叔节点为黑色且当前节点是左孩子** 对祖父节点进行右旋,交换父节点与祖父节点的颜色,最终满足红黑树性质。 以下是简化后的插入修复函数逻辑: ```cpp void _Rb_tree_rebalance_insert(_Rb_tree_node_base* node, _Rb_tree_node_base*& root) { while (node != root && node->_M_parent->_M_color == _S_red) { _Rb_tree_node_base* parent = node->_M_parent; _Rb_tree_node_base* grandparent = parent->_M_parent; if (parent == grandparent->_M_left) { _Rb_tree_node_base* uncle = grandparent->_M_right; if (uncle && uncle->_M_color == _S_red) { // Case 1: Uncle is red parent->_M_color = _S_black; uncle->_M_color = _S_black; grandparent->_M_color = _S_red; node = grandparent; } else { if (node == parent->_M_right) { // Case 2: Node is right child node = parent; _Rb_tree_rotate_left(node, root); } // Case 3: Node is left child parent->_M_color = _S_black; grandparent->_M_color = _S_red; _Rb_tree_rotate_right(grandparent, root); } } else { // Symmetric case for right side _Rb_tree_node_base* uncle = grandparent->_M_left; if (uncle && uncle->_M_color == _S_red) { parent->_M_color = _S_black; uncle->_M_color = _S_black; grandparent->_M_color = _S_red; node = grandparent; } else { if (node == parent->_M_left) { node = parent; _Rb_tree_rotate_right(node, root); } parent->_M_color = _S_black; grandparent->_M_color = _S_red; _Rb_tree_rotate_left(grandparent, root); } } } root->_M_color = _S_black; // Ensure root is always black } ``` #### 旋转操作实现 旋转操作是红黑树结构调整的基础,分为左旋和右旋两种方式: - **左旋**:以某个节点为轴,将其右子节点提升为新的父节点。 - **右旋**:以某个节点为轴,将其左子节点提升为新的父节点。 以下是左旋操作的实现示例: ```cpp void _Rb_tree_rotate_left(_Rb_tree_node_base* x, _Rb_tree_node_base*& root) { _Rb_tree_node_base* y = x->_M_right; x->_M_right = y->_M_left; if (y->_M_left != nullptr) y->_M_left->_M_parent = x; y->_M_parent = x->_M_parent; if (x == root) root = y; else if (x == x->_M_parent->_M_left) x->_M_parent->_M_left = y; else x->_M_parent->_M_right = y; y->_M_left = x; x->_M_parent = y; } ``` 右旋操作逻辑类似,仅方向相反。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

别搜了,自己做

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

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

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

打赏作者

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

抵扣说明:

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

余额充值