红黑树remove操作

一、红黑树的删除理论

如果删除的是1个红色节点(父亲和孩子肯定是黑色),不影响该路径上黑色节点的数量,也不会导致删除后的红黑树出现连续两个红色节点的情况,所以不做任何调整工作

如果删除的是1个黑色节点,那经过被删除节点的路径就会比其他路径上的黑色节点少一个,需要把被删除节点的孩子补上来,分2种情况:

  • 如果补上来的孩子是红色节点,直接把这个孩子涂成黑色, 调整完成

  • 如果补上来的孩子是黑色节点,那在当前的路径上没办法再补一个黑色节点出来了,只能从兄弟那里借调黑色节点,再分4种情况

情况1:删除的黑色节点在左边,右边兄弟是黑色,兄弟的右孩子是红色(以及镜像的情况)

在这里插入图片描述

我们现在要删除B节点

B以及B的孩子NULL是黑色的,删除B后,要从兄弟借黑色节点,首先兄弟本身就得是黑色。其次,兄弟的孩子也得是红色。我们把兄弟的孩子的红色涂成黑色,把C的黑色拿到左边来(就是右边拿一个黑色到左边,右边一个红色变成黑色

父节点A有可能是红也有可能是黑,我们这么调整:

在这里插入图片描述

C.color = A.color        // C要换到A的位置,就要变成A的颜色
A.color = BLACK          // A用来补全左子树黑色节点,所以改成黑色
C.right.color = BLACK    // 由于右子树被借走了一个黑色的C,所以把红色的D改成黑色,这样能确保右子树黑色节点数量不变
leftRorate(A)            // 从右边借节点给左边(C变成了A,右边的C被借走了)

调整前后,并没有改变局部黑色节点的数量(左边路径2黑,右边路径3黑,由于是局部树,所有左右路径黑色节点可能不相等),所以对于全局来说也调整完成

情况2:删除的黑色节点在左边,右边兄弟是黑色,兄弟的右孩子不红,左孩子是红色(以及镜像的情况)

在这里插入图片描述
我们要删除B节点,兄弟节点是黑色的,但是红色节点不在兄弟的右孩子,在兄弟的左孩子

如果我们直接以A节点进行左旋转的话,就会变成这样:
在这里插入图片描述
我们从右边借了一个黑色到左边,此时左边子树又恢复成删除前的2个黑色节点,但是右边少了一个黑色。像情况1中,旋转后红色节点依然在右子树,就可以通过修改红色节点颜色的方法,补全右子树黑色节点。但此时红色节点到左子树中了,没有办法补上右边子树的黑色节点

情况2是这样解决的:交换右边C、D节点颜色后,进行右旋转,就转换成情况1(主要思想就是借完了,还要留一个红色放在右子树,用于变色)

在这里插入图片描述

D.color = BLACK     // 修改成情况1
C.color = RED       // 修改成情况1
rightRotate(C)      // 把红色节点D转到右侧,按照情况1处理后,红色节点依然在右子树,改成黑色后即可保持黑色数量

如果兄弟的2个孩子都是红色,也是归结于情况1,只需要保证兄弟的右孩子(删除节点在左侧,左旋转用到右孩子)是红色,就不用关心兄弟的左孩子了

情况3:删除的黑色节点在左边,右边兄弟是黑色,兄弟的孩子都是黑色(以及镜像的情况)

在这里插入图片描述
我们要删除B,但是B的兄弟C节点及其孩子都是黑色的,因为我们的黑色数量是不能变的,兄弟借不了,如果兄弟借给你黑色,那么他就没有红色来补黑色了

最差的情况是:只能向上一直回溯,让所有路径都少一个黑色

我们是这样调整的:直接把兄弟节点改成红色,这样经过兄弟节点的路径就都少了一个黑色,但是现在只有B和C支路少了一个黑色,其他的路径黑色都完整,这不行,调整还没完

我们接着向上回溯,如果祖先是红色,涂成黑色即可,这样就能使得B和C支路的黑色节点又多一个,刚好等于其他支路的黑色节点数。如果祖先是黑色,就让祖先找兄弟借,这就又归结到情况1、2、3、4

总是思路就是:删除了一个黑色节点,我们需要想办法从兄弟那边借一个黑色节点到被删除黑色节点的支路,兄弟那边用红色变黑色,补齐黑色数量。如果兄弟那边没有红色可以变色,就向上回溯,让祖先找兄弟借,祖先如果借不到,最差的情况是回溯到根节点的过程中,让所有支路都有一个黑色变成红色,保持红黑树的性质

在这里插入图片描述

情况4:被删除节点的兄弟是红色,不能借

根据红黑树的性质,兄弟是红色,兄弟的孩子都是黑色,父亲也是黑色

在这里插入图片描述

C和A的颜色对调,然后以A为轴左旋转,这样做的目的是给待删除节点B找一个黑色的兄弟(有黑色的兄弟,才有借黑色节点的可能),这样就又归结到上面三种情况了,因为此时待删除节点的兄弟的黑色

在这里插入图片描述

总结上述四种情况:能借的场景是删除左子树节点时,右边兄弟是黑色的,且有红色的孩子;删除右子树节点时,左边兄弟是黑色的,且有红色的孩子;这都是为了保证借(旋转)到删除节点一边后,红色节点仍然在删除节点对面的分支上,通过变黑,即可回到和原来相同数量的黑色节点

二、红黑树的删除代码

void remove(const T& val) {
	if (root_ == nullptr) {
		return;
	}
	Node* cur = root_;
	while (cur != nullptr) {
		if (cur->data_ > val) {
			cur = cur->left_;
		}
		else if (cur->data_ < val) {
			cur = cur->right_;
		}
		else {
			break;
		}
	}
	// 没找到val
	if (cur == nullptr) {
		return;
	}
	// 删除cur
	// cur有两个孩子,删除前驱节点,情况3
	if (cur->left_ != nullptr && cur->right_ != nullptr) {
		// pre寻找cur的前驱
		Node* pre = cur->left_;
		while (pre->right_ != nullptr) {
			pre = pre->right_;
		}
		cur->data_ = pre->data_;  // 用前驱节点的data覆盖cur的data
		cur = pre;                // 改为删除前驱节点
	}
	// 删除cur指向的节点,此时cur最多只有一个孩子,情况1、2
	Node* child = cur->left_ == nullptr ? cur->right_ : cur->left_;
	if (child != nullptr) {
		// cur的孩子不空,把孩子节点挂到待删节点cur的父亲下面
		child->parent_ = cur->parent_;
		if (cur->parent_ == nullptr) {
			// cur的父亲为空,说明cur就是root
			root_ = child;
		}
		else {
			// cur的父亲不空,需要把cur的孩子挂到cur父亲下
			if (cur->parent_->left_ == cur) {
				cur->parent_->left_ = child;
			}
			else {
				cur->parent_->right_ = child;
			}
		}
		Color c = color(cur);
		delete cur;
		if (c == Color::BLACK) {
			// 删除的BLACK,需要调整
			fixAfterRemove(child);
		}
	}
	else {
		// 删除的cur就是叶节点
		if (parent(cur) == nullptr) {
			// 同时cur也是root
			delete cur;
			root_ = nullptr;
			return;
		}
		else {
			// cur就是叶子节点,由于删除了没有补上来的孩子,所以先调整,后删除
			if (color(cur) == Color::BLACK) {
				fixAfterRemove(cur);
			}
			if (left(parent(cur)) == cur) {
				cur->parent_->left_ = nullptr;
			}
			else {
				cur->parent_->right_ = nullptr;
			}
			delete cur;
		}
	}
}

void fixAfterRemove(Node* node) {
	// 传入的node就是补上来的child
	while (node != root_ && color(node) == Color::BLACK) {
		if (left(parent(node)) == node) {
			// 删除的黑色节点在左子树
			Node* bro = right(parent(node));
			// 情况4:兄弟是RED
			if (color(bro) == Color::RED) {
				setColor(parent(node), Color::RED);
				setColor(bro, Color::BLACK);
				leftRotate(parent(node));
				// 更新bro节点,到这里,补上删除节点的孩子已经有BLACK兄弟了,可以归结到情况1、2、3了
				bro = right(parent(node));
			}
			// 兄弟是黑色了,根据兄弟孩子的颜色再进行分类
			// 情况3:兄弟的孩子都是黑色
			if (color(left(bro)) == Color::BLACK && color(right(bro)) == Color::BLACK) {
				setColor(bro, Color::RED);
				node = parent(node);           // 往父节点走,继续归结到情况1、2、3、4
			}
			else {
				// 只剩情况1、2,兄弟是BLACK,有1个或2个RED孩子
				if (color(right(bro)) != Color::RED) {
					// 情况2:删除左子树的节点,兄弟是BLACK,兄弟的右孩子不红,左孩子是红色,需要旋转到一侧
					setColor(left(bro), Color::BLACK);
					setColor(bro, Color::RED);
					rightRotate(bro);
					bro = right(parent(node));
				}
				// 最后就是情况1了,兄弟是BLACK,兄弟的右孩子是RED
				setColor(bro, color(parent(node)));
				setColor(parent(node), Color::BLACK);
				setColor(right(bro), Color::BLACK);
				leftRotate(parent(node));
				break;
			}
		}
		else {
			// 删除的黑色节点在右子树
			Node* bro = left(parent(node));
			// 情况4:兄弟是RED
			if (color(bro) == Color::RED) {
				setColor(parent(node), Color::RED);
				setColor(bro, Color::BLACK);
				rightRotate(parent(node));
				// 更新bro节点,到这里,补上删除节点的孩子已经有BLACK兄弟了,可以归结到情况1、2、3了
				bro = left(parent(node));
			}
			// 兄弟是黑色了,根据兄弟孩子的颜色再进行分类
			// 情况3:兄弟的孩子都是黑色
			if (color(left(bro)) == Color::BLACK && color(right(bro)) == Color::BLACK) {
				setColor(bro, Color::RED);
				node = parent(node);           // 往父节点走,继续归结到情况1、2、3、4
			}
			else {
				// 只剩情况1、2,兄弟是BLACK,有1个或2个RED孩子
				if (color(left(bro)) != Color::RED) {
					// 情况2:删除右子树的节点,兄弟是BLACK,兄弟的左孩子不红,右孩子是红色,需要旋转到一侧
					setColor(right(bro), Color::BLACK);
					setColor(bro, Color::RED);
					leftRotate(bro);
					bro = left(parent(node));
				}
				// 最后就是情况1了,兄弟是BLACK,兄弟的左孩子是RED
				setColor(bro, color(parent(node)));
				setColor(parent(node), Color::BLACK);
				setColor(left(bro), Color::BLACK);
				rightRotate(parent(node));
				break;
			}
		}
	}
	// node是RED,涂成BLACK
	setColor(node, Color::BLACK);
}

我们演示一下,红黑树依次删除9、10、5、3
在这里插入图片描述
删除9后,会把红色10补上来,补上来的是红色节点,不用调整,直接将红色10涂成黑色10即可
在这里插入图片描述

10是叶节点,先不删除,以10为轴开始调整,兄弟是黑色,兄弟的孩子都是空,也是黑色,无法借黑色节点,属于情况3。于是将兄弟节点7涂成红色,然后向上继续调整,发现8是红色,涂成黑色8,结束调整,最后delete节点即可

在这里插入图片描述
要删除的是5,是叶节点,先不删除,以5为轴开始调整,兄弟8是黑色,但是有一个左孩子7是红色,按照情况2处理,7、8互换颜色,以8为轴进行右旋转。这就是情况1了,7变成6的颜色顶替6的位置,7变成黑色,给删除的一侧补齐,8变成黑色,给兄弟这一侧补齐

在这里插入图片描述
要删除的是3,是叶节点,先不删除,以3为轴开始调整,兄弟1是黑色,孩子是空也是黑色,属于情况3,将兄弟1涂红向上回溯。兄弟7以及两个孩子都是黑色,属于情况3,将兄弟7涂红向上回溯。到达root,停止回溯,将root涂黑即可

操作AVL红黑树
平衡树
增删查时间复杂度O(logn)O(logn)
insert最多旋转的次数22
remove最多旋转的次数O(logn)3
  • 数据量较小的情况下,两者性能相当,AVL甚至要快一点
  • 插入、删除次数比较多的时候,综合下来应该选择红黑树
  • 由于AVL树是绝对平衡树,所以当查询操作较多的时候,可以选择使用AVL树

完整的红黑树代码如下:

template<typename T>
class RBTree {
public:
	RBTree()
		: root_(nullptr)
	{}

	~RBTree() {
		if (root_ == nullptr) {
			return;
		}
		queue<Node*> q;
		q.push(root_);
		while (!q.empty()) {
			Node* node = q.front();
			q.pop();
			if (node->left_ != nullptr) {
				q.push(node->left_);
			}
			if (node->right_ != nullptr) {
				q.push(node->right_);
			}
			delete node;
		}
	}

	void insert(const T& val) {
		if (root_ == nullptr) {
			root_ = new Node(val);
			return;
		}
		Node* parent = nullptr;
		Node* cur = root_;
		while (cur != nullptr) {
			if (cur->data_ > val) {
				parent = cur;
				cur = cur->right_;
			}
			else if (cur->data_ < val) {
				parent = cur;
				cur = cur->right_;
			}
			else {
				return;
			}
		}
		// 设置当前节点的parent和color
		Node* node = new Node(val, parent, nullptr, nullptr, Color::RED);
		if (parent->data_ > val) {
			parent->left_ = node;
		}
		else {
			parent->right_ = node;
		}

		// 插入节点的父节点也是红色,则出现连续的红色,需要调整
		if (Color::RED == color(parent)) {
			fixAfterInsert(node);
		}
	}

	void remove(const T& val) {
		if (root_ == nullptr) {
			return;
		}
		Node* cur = root_;
		while (cur != nullptr) {
			if (cur->data_ > val) {
				cur = cur->left_;
			}
			else if (cur->data_ < val) {
				cur = cur->right_;
			}
			else {
				break;
			}
		}
		// 没找到val
		if (cur == nullptr) {
			return;
		}
		// 删除cur
		// cur有两个孩子,删除前驱节点,情况3
		if (cur->left_ != nullptr && cur->right_ != nullptr) {
			// pre寻找cur的前驱
			Node* pre = cur->left_;
			while (pre->right_ != nullptr) {
				pre = pre->right_;
			}
			cur->data_ = pre->data_;  // 用前驱节点的data覆盖cur的data
			cur = pre;                // 改为删除前驱节点
		}
		// 删除cur指向的节点,此时cur最多只有一个孩子,情况1、2
		Node* child = cur->left_ == nullptr ? cur->right_ : cur->left_;
		if (child != nullptr) {
			// cur的孩子不空,把孩子节点挂到待删节点cur的父亲下面
			child->parent_ = cur->parent_;
			if (cur->parent_ == nullptr) {
				// cur的父亲为空,说明cur就是root
				root_ = child;
			}
			else {
				// cur的父亲不空,需要把cur的孩子挂到cur父亲下
				if (cur->parent_->left_ == cur) {
					cur->parent_->left_ = child;
				}
				else {
					cur->parent_->right_ = child;
				}
			}
			Color c = color(cur);
			delete cur;
			if (c == Color::BLACK) {
				// 删除的BLACK,需要调整
				fixAfterRemove(child);
			}
		}
		else {
			// 删除的cur就是叶节点
			if (parent(cur) == nullptr) {
				// 同时cur也是root
				delete cur;
				root_ = nullptr;
				return;
			}
			else {
				// cur就是叶子节点
				if (color(cur) == Color::BLACK) {
					fixAfterRemove(cur);
				}
				if (left(parent(cur)) == cur) {
					cur->parent_->left_ = nullptr;
				}
				else {
					cur->parent_->right_ = nullptr;
				}
				delete cur;
			}
		}
	}

private:
	enum class Color {
		RED,
		BLACK
	};

	struct Node {
		Node(T data = T(), Node* parent = nullptr, Node* left = nullptr, Node* right = nullptr, Color color = Color::BLACK)
			: data_(data)
			, color_(color)
			, parent_(parent)
			, left_(left)
			, right_(right)
		{}

		T data_;
		Color color_;
		Node* left_;
		Node* right_;
		Node* parent_;  // 用于向上访问
	};

	Color color(Node* node) {
		return node == nullptr ? Color::BLACK : node->color_;
	}

	void setColor(Node* node, Color color) {
		node->color_ = color;
	}

	Node* left(Node* node) {
		return node->left_;
	}

	Node* right(Node* node) {
		return node->right_;
	}

	Node* parent(Node* node) {
		return node->parent_;
	}

	void leftRotate(Node* node) {
		Node* child = node->right_;
		// 1
		child->parent_ = node->parent_;
		if (node->parent_ == nullptr) {
			// node的父节点为空,说明node为root,此时child转上来,child成为root
			root_ = child;
		}
		else {
			// node不是root
			if (node->parent_->left_ == node) {
				// node是左孩子  2
				node->parent_->left_ = child;
			}
			else {
				// node是右孩子
				node->parent_->right_ = child;
			}
		}

		// 3
		node->right_ = child->left_;
		if (child->left_ != nullptr) {
			// 4
			child->left_->parent_ = node;
		}
		// 5
		child->left_ = node;
		// 6
		node->parent_ = child;
	}

	// 以node为轴进行右旋
	void rightRotate(Node* node) {
		Node* child = node->left_;
		child->parent_ = node->parent_;        // 1
		if (node->parent_ == nullptr) {
			// node就是root
			root_ = child;
		}
		else {
			// node不是root
			if (node->parent_->left_ == node) {
				// node是左孩子
				node->parent_->left_ = child;   // 2
			}
			else {
				// node是右孩子
				node->parent_->right_ = child;
			}
		}

		node->left_ = child->right_;           // 3
		if (child->right_ != nullptr) {
			child->right_->parent_ = node;     // 4
		}
		child->right_ = node;                  // 5
		node->parent_ = child;                 // 6
	}

	// 红黑树的插入调整
	void fixAfterInsert(Node* node) {
		// 父节点的颜色是红色,当前节点也是红色,不断调整
		while (color(parent(node)) == Color::RED) {
			// 父节点是爷爷的左孩子,插入的节点node在左子树
			if (parent(node) == left(parent(parent(node)))) {
				// 父亲是爷爷的左孩子,叔叔是爷爷的右孩子
				Node* uncle = right(parent(parent(node)));
				// 情况1:叔叔是红色(无论在父亲左侧或右侧)
				if (Color::RED == color(uncle)) {
					// 因为叔叔和父亲原来都是RED,爷爷原来必然是BLACK,上两代互换颜色
					setColor(parent(node), Color::BLACK);
					setColor(uncle, Color::BLACK);
					setColor(parent(parent(node)), Color::RED);
					// 上两代互换颜色后,爷爷变成RED,由于爷爷以前是BLACK,也许爷爷的父亲是RED,现在爷爷变RED后,可能连续出现两个RED,需要继续向上调整
					node = parent(parent(node));
				}
				else {
					// 情况2/3:叔叔是BLACK
					// 先处理叔叔是BLACK,插到叶子节点右侧的情况3
					if (node == right(parent(node))) {
						// 让node指向父亲,以node为轴旋转后,node才能成为情况2中刚插入的节点,进而统一情况2、3
						node = parent(node);
						leftRotate(node);
					}
					// 统一成情况2,叔叔是BLACK,插到叶子节点左侧
					setColor(parent(node), Color::BLACK);
					setColor(parent(parent(node)), Color::RED);
					rightRotate(parent(parent(node)));
					// 情况2、3保证了局部没有出现连续的RED,且保证了修改的局部支路中黑色节点数量没变,即全局也没有改变
					// 情况1只是修改颜色,情况2、3最多旋转两次,至此红黑树调整完成
					break;
				}
			}
			else {
				// 父节点是爷爷的右孩子,插入的节点node在右子树
				// 父亲是爷爷的右孩子,叔叔是爷爷的左孩子
				Node* uncle = left(parent(parent(node)));
				// 情况1:叔叔是红色(无论在父亲左侧或右侧)
				if (Color::RED == color(uncle)) {
					// 因为叔叔和父亲原来都是RED,爷爷原来必然是BLACK,上两代互换颜色
					setColor(parent(node), Color::BLACK);
					setColor(uncle, Color::BLACK);
					setColor(parent(parent(node)), Color::RED);
					// 上两代互换颜色后,爷爷变成RED,由于爷爷以前是BLACK,也许爷爷的父亲是RED,现在爷爷变RED后,可能连续出现两个RED,需要继续向上调整
					node = parent(parent(node));
				}
				else {
					// 情况2/3:叔叔是BLACK
					// 先处理叔叔是BLACK,插到叶子节点左侧的情况3
					if (node == left(parent(node))) {
						// 让node指向父亲,以node为轴旋转后,node才能成为情况2中刚插入的节点,进而统一情况2、3
						node = parent(node);
						rightRotate(node);
					}
					// 统一成情况2,叔叔是BLACK,插到叶子节点右侧
					setColor(parent(node), Color::BLACK);
					setColor(parent(parent(node)), Color::RED);
					leftRotate(parent(parent(node)));
					// 情况2、3保证了局部没有出现连续的RED,且保证了修改的局部支路中黑色节点数量没变,即全局也没有改变
					// 情况1只是修改颜色,情况2、3最多旋转两次,至此红黑树调整完成
					break;
				}
			}
		}

		// 此处需要将root_置为黑色
		setColor(root_, Color::BLACK);
	}

	void fixAfterRemove(Node* node) {
		// 传入的node就是补上来的child
		while (node != root_ && color(node) == Color::BLACK) {
			if (left(parent(node)) == node) {
				// 删除的黑色节点在左子树
				Node* bro = right(parent(node));
				// 情况4:兄弟是RED
				if (color(bro) == Color::RED) {
					setColor(parent(node), Color::RED);
					setColor(bro, Color::BLACK);
					leftRotate(parent(node));
					// 更新bro节点,到这里,补上删除节点的孩子已经有BLACK兄弟了,可以归结到情况1、2、3了
					bro = right(parent(node));
				}
				// 兄弟是黑色了,根据兄弟孩子的颜色再进行分类
				// 情况3:兄弟的孩子都是黑色
				if (color(left(bro)) == Color::BLACK && color(right(bro)) == Color::BLACK) {
					setColor(bro, Color::RED);
					node = parent(node);           // 往父节点走,继续归结到情况1、2、3、4
				}
				else {
					// 只剩情况1、2,兄弟是BLACK,有1个或2个RED孩子
					if (color(right(bro)) != Color::RED) {
						// 情况2:删除左子树的节点,兄弟是BLACK,兄弟的右孩子不红,左孩子是红色,需要旋转到一侧
						setColor(left(bro), Color::BLACK);
						setColor(bro, Color::RED);
						rightRotate(bro);
						bro = right(parent(node));
					}
					// 最后就是情况1了,兄弟是BLACK,兄弟的右孩子是RED
					setColor(bro, color(parent(node)));
					setColor(parent(node), Color::BLACK);
					setColor(right(bro), Color::BLACK);
					leftRotate(parent(node));
					break;
				}
			}
			else {
				// 删除的黑色节点在右子树
				Node* bro = left(parent(node));
				// 情况4:兄弟是RED
				if (color(bro) == Color::RED) {
					setColor(parent(node), Color::RED);
					setColor(bro, Color::BLACK);
					rightRotate(parent(node));
					// 更新bro节点,到这里,补上删除节点的孩子已经有BLACK兄弟了,可以归结到情况1、2、3了
					bro = left(parent(node));
				}
				// 兄弟是黑色了,根据兄弟孩子的颜色再进行分类
				// 情况3:兄弟的孩子都是黑色
				if (color(left(bro)) == Color::BLACK && color(right(bro)) == Color::BLACK) {
					setColor(bro, Color::RED);
					node = parent(node);           // 往父节点走,继续归结到情况1、2、3、4
				}
				else {
					// 只剩情况1、2,兄弟是BLACK,有1个或2个RED孩子
					if (color(left(bro)) != Color::RED) {
						// 情况2:删除右子树的节点,兄弟是BLACK,兄弟的左孩子不红,右孩子是红色,需要旋转到一侧
						setColor(right(bro), Color::BLACK);
						setColor(bro, Color::RED);
						leftRotate(bro);
						bro = left(parent(node));
					}
					// 最后就是情况1了,兄弟是BLACK,兄弟的左孩子是RED
					setColor(bro, color(parent(node)));
					setColor(parent(node), Color::BLACK);
					setColor(left(bro), Color::BLACK);
					rightRotate(parent(node));
					break;
				}
			}
		}
		// node是RED,涂成BLACK
		setColor(node, Color::BLACK);
	}

	Node* root_;
};
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
红黑树remove操作是为了从树中删除某个节点,同时保持树的平衡状态。下面是红黑树remove的具体过程: 1. 首先,找到需要删除的节点,并保存其子树结构以便后续操作。 2. 如果待删除节点的左右子节点都为NIL节点(即叶子节点),则直接删除该节点。 3. 如果待删除节点的左子节点为NIL节点,右子节点不为NIL节点,那么用右子节点代替待删除节点,并删除右子节点。 4. 如果待删除节点的右子节点为NIL节点,左子节点不为NIL节点,那么用左子节点代替待删除节点,并删除左子节点。 5. 如果待删除节点的左右子节点都不为NIL节点,则需要找到其后继节点(即比待删除节点大的最小节点)来代替它。后继节点一定在右子树中,可以通过一直找其左子节点来找到后继节点。 6. 如果待删除节点的后继节点为其右子节点,那么用后继节点替代待删除节点,并仅用其右子节点作为后继节点的子节点。 7. 如果待删除节点的后继节点不是其右子节点,则需要将后继节点替代待删除节点,并将后继节点的右子节点替代它。同时,将后继节点的原右子节点调整为后继节点的父节点的左子节点。 8. 在上述操作完成后,需要进行旋转和重新染色操作,以确保树的平衡性和颜色属性。 通过以上步骤,红黑树remove操作可以保证树的结构和性质仍然满足红黑树的要求,即每个节点要么是红色,要么是黑色,根节点和叶子节点(NIL节点)为黑色,并且任何一条路径上的黑色节点数量相同。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bugcoder-9905

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

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

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

打赏作者

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

抵扣说明:

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

余额充值