红黑树实现

本文详细介绍了红黑树的C++实现,包括节点定义、颜色枚举、树类模板以及一系列操作如插入、删除、查找等。通过递归和非递归方法实现查找,并提供了插入和删除后的自平衡修正函数,确保红黑树性质得以维护。此外,还展示了如何使用红黑树进行前序、中序和后序遍历。
摘要由CSDN通过智能技术生成
/**
 * C++ 语言: 红黑树
 *
 * @author skywang
 * @date 2013/11/07
 *
 * @update fyh
 * @date 2020/09/22
 */
#pragma once

#include <iomanip>
#include <iostream>
using namespace std;

/* 节点颜色 */
enum rbt_color
{
	rbt_red,			// 红色
	rbt_black			// 黑色
};

/* 节点类 */
template <class T>
class rbt_node
{
public:
	rbt_color m_color = rbt_red;				// 颜色
	T m_key;												// 关键字(键值)
	rbt_node *m_left = nullptr;					// 左孩子
	rbt_node *m_right = nullptr;				// 右孩子
	rbt_node *m_parent = nullptr;			// 父结点

	rbt_node(T value, rbt_color c, rbt_node *p, rbt_node *l, rbt_node *r) :
		m_key(value), m_color(c), m_parent(), m_left(l), m_right(r) {}
};

/* 树类 */
template <class T>
class rbt_tree
{
private:
	rbt_node<T> *m_root = nullptr;    // 根结点

public:
	/* 构造函数 */
	rbt_tree();

	/* 析构函数 */
	~rbt_tree();

	// 前序遍历"红黑树"
	void pre_order();

	// 中序遍历"红黑树"
	void in_order();

	// 后序遍历"红黑树"
	void post_order();

	// (递归实现)查找"红黑树"中键值为key的节点
	rbt_node<T>* search(T key);

	// (非递归实现)查找"红黑树"中键值为key的节点
	rbt_node<T>* iterative_search(T key);

	// 查找最小结点:返回最小结点的键值。
	T minimum();

	// 查找最大结点:返回最大结点的键值。
	T maximum();

	// 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。
	rbt_node<T>* successor(rbt_node<T> *x);

	// 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。
	rbt_node<T>* predecessor(rbt_node<T> *x);

	// 将结点(key为节点键值)插入到红黑树中
	void insert(T key);

	// 删除结点(key为节点键值)
	void remove(T key);

	// 销毁红黑树
	void destroy();

	// 打印红黑树
	void print();

private:
	// 错误处理
	void error(bool state, const char* text) const;

	// 前序遍历"红黑树"
	void pre_order(rbt_node<T>* tree) const;

	// 中序遍历"红黑树"
	void in_order(rbt_node<T>* tree) const;

	// 后序遍历"红黑树"
	void post_order(rbt_node<T>* tree) const;

	// (递归实现)查找"红黑树x"中键值为key的节点
	rbt_node<T>* search(rbt_node<T>* x, T key) const;

	// (非递归实现)查找"红黑树x"中键值为key的节点
	rbt_node<T>* iterative_search(rbt_node<T>* x, T key) const;

	// 查找最小结点:返回tree为根结点的红黑树的最小结点。
	rbt_node<T>* minimum(rbt_node<T>* tree);

	// 查找最大结点:返回tree为根结点的红黑树的最大结点。
	rbt_node<T>* maximum(rbt_node<T>* tree);

	// 左旋
	void left_rotate(rbt_node<T>* &root, rbt_node<T>* x);

	// 右旋
	void right_rotate(rbt_node<T>* &root, rbt_node<T>* y);

	// 插入函数
	void insert(rbt_node<T>* &root, rbt_node<T>* node);

	// 插入修正函数
	void insert_fix_up(rbt_node<T>* &root, rbt_node<T>* node);

	// 删除函数
	void remove(rbt_node<T>* &root, rbt_node<T> *node);

	// 删除修正函数
	void remove_fix_up(rbt_node<T>* &root, rbt_node<T> *node, rbt_node<T> *parent);

	// 销毁红黑树
	void destroy(rbt_node<T>* &tree);

	// 打印红黑树
	void print(rbt_node<T>* tree, T key, int direction);

	inline rbt_node<T>* rb_parent(rbt_node<T>* r) { return r->m_parent; }
	inline rbt_color rb_color(rbt_node<T>* r) { return r->m_color; }
	inline bool rb_is_red(rbt_node<T>* r) { return r->m_color == rbt_red; }
	inline bool rb_is_black(rbt_node<T>* r) { return r->m_color == rbt_black; }
	inline void rb_set_black(rbt_node<T>* r) { r->m_color = rbt_black; }
	inline void rb_set_red(rbt_node<T>* r) { r->m_color = rbt_red; }
	inline void rb_set_parent(rbt_node<T>* r, rbt_node<T>* p) { r->m_parent = p; }
	inline void rb_set_color(rbt_node<T>* r, rbt_color c) { r->m_color = c; }

	//#define rb_parent(r)   ((r)->m_parent)
	//#define rb_color(r) ((r)->m_color)
	//#define rb_is_red(r)   ((r)->color==rbt_red)
	//#define rb_is_black(r)  ((r)->color==rbt_black)
	//#define rb_set_black(r)  do { (r)->color = rbt_black; } while (0)
	//#define rb_set_red(r)  do { (r)->color = rbt_red; } while (0)
	//#define rb_set_parent(r,p)  do { (r)->m_parent = (p); } while (0)
	//#define rb_set_color(r,c)  do { (r)->m_color = (c); } while (0)
};

/*
 * 构造函数
 */
template <class T>
rbt_tree<T>::rbt_tree() :m_root(nullptr) {}

/*
 * 析构函数
 */
template <class T>
rbt_tree<T>::~rbt_tree() { destroy(); }

/*
* 错误处理
*/
template <class T>
void rbt_tree<T>::error(bool state, const char* text) const
{
	if (state == false)
	{
		std::cout << text << std::endl;
		getchar();
		exit(-1);
	}
}

/*
 * 前序遍历"红黑树"
 * 中 -> 左 -> 右
 */
template <class T>
void rbt_tree<T>::pre_order(rbt_node<T>* tree) const
{
	if (tree)
	{
		cout << tree->m_key << " ";
		pre_order(tree->m_left);
		pre_order(tree->m_right);
	}
}

template <class T>
void rbt_tree<T>::pre_order()
{
	pre_order(m_root);
}

/*
 * 中序遍历"红黑树"
 * 左 -> 中 -> 右
 */
template <class T>
void rbt_tree<T>::in_order(rbt_node<T>* tree) const
{
	if (tree)
	{
		in_order(tree->m_left);
		cout << tree->m_key << " ";
		in_order(tree->m_right);
	}
}

template <class T>
void rbt_tree<T>::in_order()
{
	in_order(m_root);
}

/*
 * 后序遍历"红黑树"
 * 左 -> 右 -> 中
 */
template <class T>
void rbt_tree<T>::post_order(rbt_node<T>* tree) const
{
	if (tree)
	{
		post_order(tree->m_left);
		post_order(tree->m_right);
		cout << tree->m_key << " ";
	}
}

template <class T>
void rbt_tree<T>::post_order()
{
	post_order(m_root);
}

/*
 * (递归实现)查找"红黑树x"中键值为key的节点
 */
template <class T>
rbt_node<T>* rbt_tree<T>::search(rbt_node<T>* x, T key) const
{
	// 直接返回
	if (x == nullptr || x->m_key == key)
		return x;

	if (key < x->m_key)
		return search(x->m_left, key);		// 小于搜索左边
	else
		return search(x->m_right, key);	// 大于搜索右边
}

template <class T>
rbt_node<T>* rbt_tree<T>::search(T key)
{
	return search(m_root, key);
}

/*
 * (非递归实现)查找"红黑树x"中键值为key的节点
 */
template <class T>
rbt_node<T>* rbt_tree<T>::iterative_search(rbt_node<T>* x, T key) const
{
	// x不为空且键值不相同
	while (x && x->m_key != key)
	{
		if (key < x->m_key)
			x = x->m_left;		// 小于就往左搜索
		else
			x = x->m_right;	// 大于就往右搜索
	}

	return x;
}

template <class T>
rbt_node<T>* rbt_tree<T>::iterative_search(T key)
{
	return iterative_search(m_root, key);
}

/*
 * 查找最小结点:返回tree为根结点的红黑树的最小结点。
 */
template <class T>
rbt_node<T>* rbt_tree<T>::minimum(rbt_node<T>* tree)
{
	// 为空就直接返回空
	if (tree == nullptr)
		return nullptr;

	// 最左的那一个就是最小的那一个
	while (tree->m_left)
		tree = tree->m_left;

	return tree;
}

template <class T>
T rbt_tree<T>::minimum()
{
	return minimum(m_root);
}

/*
 * 查找最大结点:返回tree为根结点的红黑树的最大结点。
 */
template <class T>
rbt_node<T>* rbt_tree<T>::maximum(rbt_node<T>* tree)
{
	// 传入为空那就直接返回空
	if (tree == nullptr)
		return nullptr;

	// 最右边的那一个就是最大的那一个
	while (tree->m_right)
		tree = tree->m_right;

	return tree;
}

template <class T>
T rbt_tree<T>::maximum()
{
	return maximum(m_root);
}

/*
 * 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。
 */
template <class T>
rbt_node<T>* rbt_tree<T>::successor(rbt_node<T> *x)
{
	// 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
	if (x->m_right)
		return minimum(x->m_right);

	// 如果x没有右孩子。则x有以下两种可能:
	// (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
	// (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
	rbt_node<T>* y = x->m_parent;
	while (y && x == y->m_right)
	{
		x = y;
		y = y->m_parent;
	}

	return y;
}

/*
 * 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。
 */
template <class T>
rbt_node<T>* rbt_tree<T>::predecessor(rbt_node<T> *x)
{
	// 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
	if (x->m_left)
		return maximum(x->m_left);

	// 如果x没有左孩子。则x有以下两种可能:
	// (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
	// (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
	rbt_node<T>* y = x->m_parent;
	while (y&& x == y->m_left)
	{
		x = y;
		y = y->m_parent;
	}

	return y;
}

/*
 * 对红黑树的节点(x)进行左旋转
 *
 * 左旋示意图(对节点x进行左旋):
 *      px                              px
 *     /                               /
 *    x                               y
 *   /  \      --(左旋)-->     / \                #
 *  lx   y                          x  ry
 *     /   \                       /  \
 *    ly   ry                     lx  ly
 *
 *
 */
template <class T>
void rbt_tree<T>::left_rotate(rbt_node<T>* &root, rbt_node<T>* x)
{
	// 设置x的右孩子为y
	rbt_node<T> *y = x->m_right;

	// 将 “y的左孩子” 设为 “x的右孩子”;
	// 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
	x->m_right = y->m_left;
	if (y->m_left)
		y->m_left->m_parent = x;

	// 将 “x的父亲” 设为 “y的父亲”
	y->m_parent = x->m_parent;

	if (x->m_parent == nullptr)
		root = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
	else
	{
		if (x->m_parent->m_left == x)
			x->m_parent->m_left = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
		else
			x->m_parent->m_right = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
	}

	// 将 “x” 设为 “y的左孩子”
	y->m_left = x;
	// 将 “x的父节点” 设为 “y”
	x->m_parent = y;
}

/*
 * 对红黑树的节点(y)进行右旋转
 *
 * 右旋示意图(对节点y进行左旋):
 *            py                               py
 *           /                                /
 *          y                                x
 *         /  \      --(右旋)-->      /  \                     #
 *        x   ry                           lx   y
 *       / \                                   / \                   #
 *      lx  rx                                rx  ry
 *
 */
template <class T>
void rbt_tree<T>::right_rotate(rbt_node<T>* &root, rbt_node<T>* y)
{
	// 设置x是当前节点的左孩子。
	rbt_node<T> *x = y->m_left;

	// 将 “x的右孩子” 设为 “y的左孩子”;
	// 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
	y->m_left = x->m_right;
	if (x->m_right)
		x->m_right->m_parent = y;

	// 将 “y的父亲” 设为 “x的父亲”
	x->m_parent = y->m_parent;

	if (y->m_parent == nullptr)
		root = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点
	else
	{
		if (y == y->m_parent->m_right)
			y->m_parent->m_right = x;    // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
		else
			y->m_parent->m_left = x;    // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
	}

	// 将 “y” 设为 “x的右孩子”
	x->m_right = y;

	// 将 “y的父节点” 设为 “x”
	y->m_parent = x;
}

/*
 * 红黑树插入修正函数
 *
 * 在向红黑树中插入节点之后(失去平衡),再调用该函数;
 * 目的是将它重新塑造成一颗红黑树。
 *
 * 参数说明:
 *     root 红黑树的根
 *     node 插入的结点        // 对应《算法导论》中的z
 */
template <class T>
void rbt_tree<T>::insert_fix_up(rbt_node<T>* &root, rbt_node<T>* node)
{
	/* 父节点和祖父节点 */
	rbt_node<T> *parent = nullptr, *gparent = nullptr;

	// 若“父节点存在,并且父节点的颜色是红色”
	// 红黑树的相邻节点不能都是红色的,所以要进行修复了
	while ((parent = rb_parent(node)) && rb_is_red(parent))
	{
		// 先获取祖父节点
		gparent = rb_parent(parent);

		//若“父节点”是“祖父节点的左孩子”
		if (parent == gparent->m_left)
		{
			// Case 1条件:叔叔节点是红色
			// 将父节点和叔叔节点全都设置为黑色,设置祖父节点为红色,讲当前节点指针指向祖父节点,进行上面的修复操作
			{
				// 获取叔叔节点
				rbt_node<T> *uncle = gparent->m_right;

				// 存在叔叔节点而且叔叔节点还是红色
				if (uncle && rb_is_red(uncle))
				{
					// 父节点和叔叔节点设置为黑色
					rb_set_black(uncle);
					rb_set_black(parent);

					// 祖父节点设置为红色
					rb_set_red(gparent);

					// 转到祖父节点进行修复
					node = gparent;
					continue;
				}
			}

			// Case 2条件:叔叔是黑色,且当前节点是右孩子
			if (parent->m_right == node)
			{
				// 进行左旋转,变化为Case 3
				rbt_node<T> *tmp = nullptr;
				left_rotate(root, parent);

				// 节点互换了
				tmp = parent;
				parent = node;
				node = tmp;
			}

			// Case 3条件:叔叔是黑色,且当前节点是左孩子。
			// 设置父节点为黑色,将祖父节点设置为红色,再将祖父节点进行右旋转
			rb_set_black(parent);
			rb_set_red(gparent);
			right_rotate(root, gparent);
		}
		else//若“z的父节点”是“z的祖父节点的右孩子”
		{
			// Case 1条件:叔叔节点是红色
			{
				// 获取叔叔节点
				rbt_node<T> *uncle = gparent->m_left;

				// 存在叔叔节点且为红色的
				if (uncle && rb_is_red(uncle))
				{
					// 将叔叔节点和父节点设置为黑色
					rb_set_black(uncle);
					rb_set_black(parent);

					// 将祖父节点设置为红色
					rb_set_red(gparent);

					// 设置祖父节点为当前节点,继续修复
					node = gparent;
					continue;
				}
			}

			// Case 2条件:叔叔是黑色,且当前节点是左孩子
			// 对父节点进行右旋转,将父节点设置为当前节点
			if (parent->m_left == node)
			{
				rbt_node<T> *tmp;
				right_rotate(root, parent);
				tmp = parent;
				parent = node;
				node = tmp;
			}

			// Case 3条件:叔叔是黑色,且当前节点是右孩子。
			// 将父节点设置为黑色
			rb_set_black(parent);

			// 将祖父节点设置为红色
			rb_set_red(gparent);

			// 左旋操作
			left_rotate(root, gparent);
		}
	}

	// 将根节点设为黑色
	rb_set_black(root);
}

/*
 * 将结点插入到红黑树中
 *
 * 参数说明:
 *     root 红黑树的根结点
 *     node 插入的结点        // 对应《算法导论》中的node
 */
template <class T>
void rbt_tree<T>::insert(rbt_node<T>* &root, rbt_node<T>* node)
{
	rbt_node<T> *y = nullptr;
	rbt_node<T> *x = root;

	// 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。
	// 从根节点开始查找合适的插入位置
	while (x)
	{
		// 保存上一个指针
		y = x;

		if (node->m_key < x->m_key)	// 小于放左边
			x = x->m_left;
		else                                              // 大于放右边
			x = x->m_right;
	}

	// 设置父节点
	node->m_parent = y;

	// 放置在哪一边
	if (y)
	{
		if (node->m_key < y->m_key)	// 小于放左边
			y->m_left = node;
		else                                             // 大于放右边
			y->m_right = node;
	}
	else// 直接设置为根节点
		root = node;

	// 2. 设置节点的颜色为红色
	node->m_color = rbt_red;

	// 3. 将它重新修正为一颗二叉查找树
	insert_fix_up(root, node);
}

/*
 * 将结点(key为节点键值)插入到红黑树中
 *
 * 参数说明:
 *     tree 红黑树的根结点
 *     key 插入结点的键值
 */
template <class T>
void rbt_tree<T>::insert(T key)
{
	/* 申请内存空间 */
	rbt_node<T> *z = new rbt_node<T>(key, rbt_black, nullptr, nullptr, nullptr);
	error(z, "insert new error");

	/* 插入节点 */
	insert(m_root, z);
}

/*
 * 红黑树删除修正函数
 *
 * 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;
 * 目的是将它重新塑造成一颗红黑树。
 *
 * 参数说明:
 *     root 红黑树的根
 *     node 待修正的节点
 */
template <class T>
void rbt_tree<T>::remove_fix_up(rbt_node<T>* &root, rbt_node<T> *node, rbt_node<T> *parent)
{
	rbt_node<T> *other = nullptr;

	while ((!node || rb_is_black(node)) && node != root)
	{
		if (parent->m_left == node)
		{
			other = parent->m_right;
			if (rb_is_red(other))
			{
				// Case 1: x的兄弟w是红色的
				rb_set_black(other);
				rb_set_red(parent);
				left_rotate(root, parent);
				other = parent->m_right;
			}
			if ((!other->m_left || rb_is_black(other->m_left)) &&
				(!other->m_right || rb_is_black(other->m_right)))
			{
				// Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的
				rb_set_red(other);
				node = parent;
				parent = rb_parent(node);
			}
			else
			{
				if (!other->m_right || rb_is_black(other->m_right))
				{
					// Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
					rb_set_black(other->m_left);
					rb_set_red(other);
					right_rotate(root, other);
					other = parent->m_right;
				}
				// Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
				rb_set_color(other, rb_color(parent));
				rb_set_black(parent);
				rb_set_black(other->m_right);
				left_rotate(root, parent);
				node = root;
				break;
			}
		}
		else
		{
			other = parent->m_left;
			if (rb_is_red(other))
			{
				// Case 1: x的兄弟w是红色的
				rb_set_black(other);
				rb_set_red(parent);
				right_rotate(root, parent);
				other = parent->m_left;
			}
			if ((!other->m_left || rb_is_black(other->m_left)) &&
				(!other->m_right || rb_is_black(other->m_right)))
			{
				// Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的
				rb_set_red(other);
				node = parent;
				parent = rb_parent(node);
			}
			else
			{
				if (!other->m_left || rb_is_black(other->m_left))
				{
					// Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
					rb_set_black(other->m_right);
					rb_set_red(other);
					left_rotate(root, other);
					other = parent->m_left;
				}
				// Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
				rb_set_color(other, rb_color(parent));
				rb_set_black(parent);
				rb_set_black(other->m_left);
				right_rotate(root, parent);
				node = root;
				break;
			}
		}
	}
	if (node)
		rb_set_black(node);
}

/*
 * 删除结点(node),并返回被删除的结点
 *
 * 参数说明:
 *     root 红黑树的根结点
 *     node 删除的结点
 */
template <class T>
void rbt_tree<T>::remove(rbt_node<T>* &root, rbt_node<T> *node)
{
	rbt_node<T> *child = nullptr, *parent = nullptr;
	rbt_color color = rbt_red;

	// 被删除节点的"左右孩子都不为空"的情况。
	if (node->m_left && node->m_right)
	{
		// 被删节点的后继节点。(称为"取代节点")
		// 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。
		rbt_node<T> *replace = node;

		// 获取后继节点,查找删除节点的右节点的最左节点
		replace = replace->m_right;
		while (replace->m_left != NULL)
			replace = replace->m_left;

		// "node节点"不是根节点(只有根节点不存在父节点)
		// 用替换节点覆盖删除节点
		if (rb_parent(node))
		{
			if (rb_parent(node)->left == node)		// 删除节点是左节点
				rb_parent(node)->left = replace;
			else                                                     //	删除节点是右节点
				rb_parent(node)->right = replace;
		}
		else // "node节点"是根节点,更新根节点。
			root = replace;

		// child是"取代节点"的右孩子,也是需要"调整的节点"。
		// "取代节点"肯定不存在左孩子!因为它是一个后继节点。
		child = replace->m_right;
		parent = rb_parent(replace);
		// 保存"取代节点"的颜色
		color = rb_color(replace);

		// "被删除节点"是"它的后继节点的父节点"
		if (parent == node)
			parent = replace;
		else
		{
			// child不为空
			if (child)
				rb_set_parent(child, parent);
			parent->m_left = child;

			replace->m_right = node->m_right;
			rb_set_parent(node->m_right, replace);
		}

		replace->m_parent = node->m_parent;
		replace->m_color = node->m_color;
		replace->m_left = node->m_left;
		node->m_left->parent = replace;

		if (color == rbt_black)
			remove_fix_up(root, child, parent);

		delete node;
		node = nullptr;
		return;
	}

	if (node->m_left)
		child = node->m_left;
	else
		child = node->m_right;

	// 保存父节点
	parent = node->m_parent;
	// 保存"取代节点"的颜色
	color = node->m_color;

	// 设置父节点
	if (child)
		child->m_parent = parent;

	// "node节点"不是根节点
	if (parent)
	{
		if (parent->m_left == node)
			parent->m_left = child;
		else
			parent->m_right = child;
	}
	else
		root = child;

	// 黑色就需要修复
	if (color == rbt_black)
		remove_fix_up(root, child, parent);

	delete node;
	node = nullptr;
	return;
}

/*
 * 删除红黑树中键值为key的节点
 *
 * 参数说明:
 *     tree 红黑树的根结点
 */
template <class T>
void rbt_tree<T>::remove(T key)
{
	// 查找key对应的节点(node),找到的话就删除该节点
	rbt_node<T> *node = search(m_root, key);
	if (node)
		remove(m_root, node);
}

/*
 * 销毁红黑树
 */
template <class T>
void rbt_tree<T>::destroy(rbt_node<T>* &tree)
{
	if (tree == nullptr) return;

	if (tree->m_left)		// 销毁左节点
		return destroy(tree->m_left);
	if (tree->m_right)	// 销毁右节点
		return destroy(tree->m_right);

	// 释放并清空
	delete tree;
	tree = nullptr;
}

template <class T>
void rbt_tree<T>::destroy()
{
	destroy(m_root);
}

/*
 * 打印"二叉查找树"
 *
 * key        -- 节点的键值
 * direction  --  0,表示该节点是根节点;
 *               -1,表示该节点是它的父结点的左孩子;
 *                1,表示该节点是它的父结点的右孩子。
 */
template <class T>
void rbt_tree<T>::print(rbt_node<T>* tree, T key, int direction)
{
	if (tree)
	{
		if (direction == 0)    // tree是根节点
			cout << setw(2) << tree->m_key << "(B) is root" << endl;
		else							 // tree是分支节点
			cout << setw(2) << tree->m_key << (rb_is_red(tree) ? "(R)" : "(B)") << " is " << setw(2) << key << "'s " << setw(12) << (direction == 1 ? "right child" : "left child") << endl;

		print(tree->m_left, tree->m_key, -1);
		print(tree->m_right, tree->m_key, 1);
	}
}

template <class T>
void rbt_tree<T>::print()
{
	if (m_root)
		print(m_root, m_root->m_key, 0);
}
#include "rbt.hpp"

#include <time.h>
#include <vector>

/*
https://www.jianshu.com/p/e136ec79235c

红黑树性质 :
性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL)是黑色。
性质4:每个红色结点的两个子结点一定都是黑色。
性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

红黑树并不是一个完美平衡二叉查找树

祖父节点 父节点 当前节点 兄弟节点 叶子节点

自平衡操作 : 左旋 右旋 变色
左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变
右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变
变色:结点的颜色由红变黑或由黑变红

左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树挪了。
右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树挪了。

查找 :
1.从根结点开始查找,把根结点设置为当前结点;
2.若当前结点为空,返回null;
3.若当前结点不为空,用当前结点的key跟查找key作比较;
4.若当前结点key等于查找key,那么该key就是查找目标,返回当前结点;
5.若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤2;
6.若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤2;

插入 :
1.从根结点开始查找;
2.若根结点为空,那么插入结点作为根结点,结束。
3.若根结点不为空,那么把根结点作为当前结点;
4.若当前结点为null,返回当前结点的父结点,结束。
5.若当前结点key等于查找key,那么该key所在结点就是插入结点,更新结点的值,结束。
6.若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤4;
7.若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤4;

插入结点是应该是什么颜色呢?答案是红色

红黑树的生长是自底向上的

插入情景1:红黑树为空树
最简单的一种情景,直接把插入结点作为根结点就行,但注意,根据红黑树性质2:根节点是黑色。还需要把插入结点设为黑色。
处理:
把插入结点作为根结点,并把结点设置为黑色。

插入情景2:插入结点的Key已存在
插入结点的Key已存在,既然红黑树总保持平衡,在插入前红黑树已经是平衡的,那么把插入结点设置为将要替代结点的颜色,再把结点的值更新就完成插入。
处理:
把I设为当前结点的颜色
更新当前结点的值为插入结点的值

插入情景3:插入结点的父结点为黑结点
由于插入的结点是红色的,当插入结点的黑色时,并不会影响红黑树的平衡,直接插入即可,无需做自平衡。
处理:直接插入。

插入情景4:插入结点的父结点为红结点
再次回想下红黑树的性质2(根结点是黑色)。如果插入的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。这点很重要,因为后续的旋转操作肯定需要祖父结点的参与。

插入情景4.1:叔叔结点存在并且为红结点
从红黑树性质4可以知道,祖父结点肯定为黑结点,因为不可以同时存在两个相连的红结点。那么此时该插入子树的红黑层数的情况是:黑红红。显然最简单的处理方式是把其改为:红黑红
处理:
将P和S设置为黑色
将PP设置为红色
把PP设置为当前插入结点

插入情景4.2:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点

插入情景4.2.1:插入结点是其父结点的左子结点
处理:
将P设为黑色
将PP设为红色
对PP进行右旋

插入情景4.2.2:插入结点是其父结点的右子结点
这种情景显然可以转换为情景4.2.1,如图12所示,不做过多说明了。
处理:
对P进行左旋
把P设置为插入结点,得到情景4.2.1
进行情景4.2.1的处理

插入情景4.3:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点
该情景对应情景4.2,只是方向反转

插入情景4.3.1:插入结点是其父结点的右子结点
处理:
将P设为黑色
将PP设为红色
对PP进行左旋

插入情景4.3.2:插入结点是其父结点的左子结点
处理:
对P进行右旋
把P设置为插入结点,得到情景4.3.1
进行情景4.3.1的处理

二叉树删除结点找替代结点有3种情情景:
情景1:若删除结点无子结点,直接删除
情景2:若删除结点只有一个子结点,用子结点替换删除结点
情景3:若删除结点有两个子结点,用后继结点(大于删除结点的最小结点)替换删除结点

删除情景1:替换结点是红色结点
我们把替换结点换到了删除结点的位置时,由于替换结点时红色,删除也了不会影响红黑树的平衡,只要把替换结点的颜色设为删除的结点的颜色即可重新平衡。
处理:颜色变为删除结点的颜色

删除情景2:替换结点是黑结点
当替换结点是黑色时,我们就不得不进行自平衡处理了。我们必须还得考虑替换结点是其父结点的左子结点还是右子结点,来做不同的旋转操作,使树重新平衡。

删除情景2.1:替换结点是其父结点的左子结点

删除情景2.1.1:替换结点的兄弟结点是红结点
若兄弟结点是红结点,那么根据性质4,兄弟结点的父结点和子结点肯定为黑色,不会有其他子情景,我们按图21处理,得到删除情景2.1.2.3(后续讲解,这里先记住,此时R仍然是替代结点,它的新的兄弟结点SL和兄弟结点的子结点都是黑色)。
处理:
将S设为黑色
将P设为红色
对P进行左旋,得到情景2.1.2.3
进行情景2.1.2.3的处理

删除情景2.1.2:替换结点的兄弟结点是黑结点
当兄弟结点为黑时,其父结点和子结点的具体颜色也无法确定(如果也不考虑自底向上的情况,子结点非红即为叶子结点Nil,Nil结点为黑结点),此时又得考虑多种子情景。

删除情景2.1.2.1:替换结点的兄弟结点的右子结点是红结点,左子结点任意颜色
即将删除的左子树的一个黑色结点,显然左子树的黑色结点少1了,然而右子树又又红色结点,那么我们直接向右子树“借”个红结点来补充黑结点就好啦,此时肯定需要用旋转处理了。如图22所示。
处理:
将S的颜色设为P的颜色
将P设为黑色
将SR设为黑色
对P进行左旋

删除情景2.1.2.2:替换结点的兄弟结点的右子结点为黑结点,左子结点为红结点
兄弟结点所在的子树有红结点,我们总是可以向兄弟子树借个红结点过来,显然该情景可以转换为情景2.1.2.1。图如23所示。
处理:
将S设为红色
将SL设为黑色
对S进行右旋,得到情景2.1.2.1
进行情景2.1.2.1的处理

删除情景2.1.2.3:替换结点的兄弟结点的子结点都为黑结点
好了,此次兄弟子树都没红结点“借”了,兄弟帮忙不了,找父母呗,这种情景我们把兄弟结点设为红色,再把父结点当作替代结点,自底向上处理,去找父结点的兄弟结点去“借”。但为什么需要把兄弟结点设为红色呢?显然是为了在P所在的子树中保证平衡(R即将删除,少了一个黑色结点,子树也需要少一个),后续的平衡工作交给父辈们考虑了,还是那句,当每棵子树都保持平衡时,最终整棵总是平衡的。
处理:
将S设为红色
把P作为新的替换结点
重新进行删除结点情景处理

删除情景2.2:替换结点是其父结点的右子结点
好啦,右边的操作也是方向相反,不做过多说明了,相信理解了删除情景2.1后,肯定可以理解2.2。

删除情景2.2.1:替换结点的兄弟结点是红结点
处理:
将S设为黑色
将P设为红色
对P进行右旋,得到情景2.2.2.3
进行情景2.2.2.3的处理

删除情景2.2.2:替换结点的兄弟结点是黑结点

删除情景2.2.2.1:替换结点的兄弟结点的左子结点是红结点,右子结点任意颜色
处理:
将S的颜色设为P的颜色
将P设为黑色
将SL设为黑色
对P进行右旋

删除情景2.2.2.2:替换结点的兄弟结点的左子结点为黑结点,右子结点为红结点
处理:
将S设为红色
将SR设为黑色
对S进行左旋,得到情景2.2.2.1
进行情景2.2.2.1的处理

删除情景2.2.2.3:替换结点的兄弟结点的子结点都为黑结点
处理:
将S设为红色
把P作为新的替换结点
重新进行删除结点情景处理

*/

int main(int argc, char* argv[])
{
	/* 随机化种子 */
	srand((unsigned int)time(nullptr));

	/* 随机化数据 */
	std::vector<int> random_data;
	for (int i = 0; i < 30; i++) random_data.push_back(rand() % 100);

	/* 显示数据 */
	std::cout << "[+] 原始数据 : " << std::endl;
	for (int i = 0; i < 30; i++) std::cout << random_data[i] << "  ";
	std::cout << std::endl;

	/* 插入红黑树 */
	rbt_tree<int> g;
	for (int i = 0; i < 30; i++) g.insert(random_data[i]);
	std::cout << "[+] 红黑树结构 : " << std::endl;

	/* 显示结构 */
	g.print();

	getchar();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值