【数据结构】向STL看齐-模拟实现红黑树

目标:回顾模板分离编译,掌握平衡树的左旋和右旋,掌握红黑树插入过程调整逻辑,即旋转和变色。

红黑树的特点:

1、任意一个结点要么是红色,要么是黑色
2、根结点是黑色
3、一条路径上不能出现两个连续的红色结点
4、从根节点到任一叶子节点的黑色结点个数相同
5、NUL结点默认为黑色结点(可忽略)

使用模板分离编译实现红黑树的基本操作:拷贝构造,赋值重载,插入、判空、查找key是否存在、个数、中序打印等。

RBTree.hpp

#ifndef _RBTREE_H_
#define _RBTREE_H_


#include <iostream>
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; // 指向父亲
    K _key;           // 结点的key值
    V _val;           // 结点对应的value
    Color _color;     // 结点的颜色

    // 构造函数
    RBTreeNode(const K &key, const V &val, Color color = RED)
		: _left(NULL)
		, _right(NULL)
		, _parent(NULL)
        , _key(key)
        , _val(val)
        , _color(color)
    {}
};


// 红黑树
template<class K, class V>
class RBTree
{
public:
	typedef RBTreeNode<K, V> Node;
public:
	RBTree()
		:_root(NULL)
		, _size(0)
	{}
	explicit RBTree(const RBTree & rb);     // 拷贝构造
	RBTree& operator= (const RBTree &rb);   // 赋值运算符重载
	bool Insert(const K &key, const V &val);// 红黑树的插入
	bool IsRBTree();					    // 是否是红黑树
	void Inorder();				            // 中序打印
	bool Find(const K& key);                // 查找key是否存在
	size_t Size();                          // 结点个数
	bool IsEmpty();                         // 判断是否为空
	~RBTree();                              // 析构函数


private:
	Node* _Copy(const Node* pNode, Node* parent); // 拷贝一棵树
	void _Inorder(Node *pNode);       // 递归中序遍历
	void RotateLeft(Node* parent);    // 左旋
	void RotateRight(Node* parent);   // 右旋
	void _Clear(Node*& pNode);        // 清空
	// 判断是不是红黑树
	bool _IsRBTree(Node* pNode, const size_t blackCount, size_t curBlackCount);
	bool _Find(Node* pNode, const K& key);
private:
    Node* _root;    // 根节点
	size_t _size;   // 结点个数
};

#endif /* _RBTREE_H_ */

重点是插入过程的调整

在分析或画图之前要牢记:1.cur为当前结点 2.p为父节点 3.g为祖父结点 4.u为叔叔结点

一、当pCur为根节点时无需旋转调整,直接让根节点的颜色为黑即可,无需进入循环

二、当pCur不为根节点时,调整出现两个红色结点相连的情况,注意此时cur一定是红,p也是红,g为黑,可分为如下情况:

a). 叔叔结点存在且为红色, 此时将父亲和叔叔结点调整为黑色,祖父变为红色,然后继续循环


b). 叔叔结点不存在或者为黑色,即 a)的 else情况,主要有如下情形:

1.p是g的左孩子

如果cur是p的左孩子 , 需要右旋g   — 简称 左左 右旋

如果cur是p的右孩子 , 先左旋p, 然后和上述<1>.中第一条情况一样   — 简称 左右,先左旋后右旋

2. p是g的右孩子
如果cur是p右孩子   , 需要左旋g  — 简称 右右 左旋
如果cur是p的左孩子 , 先右旋p, 然后和上述<2>. 中第一条情况一样   — 简称 右左,先右旋后左旋
完成后将 g 的颜色改为红色 p的颜色改为黑色

在test.cpp中有测试用例,基本覆盖了上面的所有情况。

RBTree.cpp

#include "RBTree.hpp"

/********************/
/* 以下为 public函数*/
/*******************/


// 拷贝构造
template<class K, class V>
RBTree<K, V>::RBTree(const RBTree & rb)     
:_size(rb._size)
{
	Node* parent = NULL;
	_root = _Copy(rb._root, parent);
}

// 赋值重载
template<class K, class V>
RBTree<K, V>& RBTree<K, V>::operator=(const RBTree &rb)
{
	if (*this != &rb)
	{
		_Clear(_root);
		RBTree temp(rb);
		_size = temp._size;
		std::swap(_root, temp._root);
	}
	return *this;
}


// 插入元素
template<class K, class V>
bool RBTree<K, V>::Insert(const K &key, const V &val)
{
	// 1. 树为空的时候,直接插入
	if (NULL == _root)
	{
		_root = new Node(key, val, BLACK);
		return true;
	}

	// 2. 树不为空
	Node* pCur = _root;    // 保存插入的结点
	Node* pParent = NULL;  // 保存插入结点的父亲,方便链接

	//		1)、 开始找结点位置
	while (pCur != NULL)
	{
		if (pCur->_key < key)
		{
			pParent = pCur;
			pCur = pCur->_right;
		}
		else if (pCur->_key > key)
		{
			pParent = pCur;
			pCur = pCur->_left;
		}
		else
			return false;
	}
	//     2)、位置已找到,插入并连接
	pCur = new Node(key, val);

	if (pParent->_key < key)
	{
		pParent->_right = pCur;
	}
	else if (pParent->_key > key)
	{
		pParent->_left = pCur;
	}
	pCur->_parent = pParent;

	// 3. 判断是否需要调整树,可整合为如下情况
	/************************************************************************/
	/* 在分析之前要牢记:1.cur为当前结点 2.p为父节点 3.g为祖父结点 4.u为叔叔结点
	/* 1. 当pCur为根节点时无需旋转调整,直接让根节点的颜色为黑即可,无需进入循环
	/*
	/* 2. 当pCur不为根节点时,调整出现两个红色结点相连的情况,注意此时cur一定是红,p也是红,祖父为黑
	/*		a). 叔叔结点存在且为红色, 此时将父亲和叔叔结点调整为黑色,祖父变为红色,然后继续循环
	/*		b). 叔叔结点不存在或者为黑色,即 a)的 else情况
	/*         <1>. p是g的左孩子
	/*			    如果cur是p的左孩子 , 需要右旋g
	/*              如果cur是p的右孩子 , 先左旋p, 然后和上述<1>.中第一条情况一样
	/*		   <2>. p是g的右孩子
	/*				如果cur是p右孩子   , 需要左旋g
	/*              如果cur是p的左孩子 , 先右旋p, 然后和上述<2>. 中第一条情况一样
	/*          完成后将 g 的颜色改为红色 p的颜色改为黑色
	/************************************************************************/

	while (pCur != _root && pParent->_color == RED)  // 此条件相当于限制了树的高度大于2
	{

		Node* pGrandfather = pParent->_parent;		 // 保存祖父结点
		Node* pUnclue = NULL;					     // 保存叔叔结点

		if (pParent == pGrandfather->_left)          // 找到叔叔结点
			pUnclue = pGrandfather->_right;
		else
			pUnclue = pGrandfather->_left;

		if (pUnclue != NULL && RED == pUnclue->_color) // 情况2.a
		{
			pGrandfather->_color = RED;
			pParent->_color = BLACK;
			pUnclue->_color = BLACK;
		}
		else
		{
			if (pParent == pGrandfather->_left) // 左子树
			{
				if (pCur == pParent->_right)    // 左右
				{
					RBTree<K, V>::RotateLeft(pParent);
					std::swap(pCur, pParent);
				}
				RBTree<K, V>::RotateRight(pGrandfather);      // 左左
			}
			else								// 右子树
			{
				if (pCur == pParent->_left)     // 右左
				{
					RBTree<K, V>::RotateRight(pParent);
					std::swap(pCur, pParent);
				}
				RBTree<K, V>::RotateLeft(pGrandfather);      // 右右
			}
			pGrandfather->_color = RED;
			pParent->_color = BLACK;
		}


		pCur = pGrandfather;     // 跳过黑色结点直接开始调整红色结点
		pParent = pCur->_parent;
	}
	_root->_color = BLACK;
	_size++;
	return true;
}

// 判断是否满足红黑树性质
template<class K, class V>
bool RBTree<K, V>::IsRBTree()
{
	if (_root == NULL)
		return true;

	if (RED == _root->_color)  // 红黑树性质以
		return false;

	size_t leftBlackCount = 0; // 保存最左路径上黑结点个数
	size_t rightBlackCount = 0; // 保存最右路径上黑结点个数
	Node* lNext = _root;
	Node* rNext = _root;
	while (lNext)
	{
		if (lNext->_color == BLACK)
			leftBlackCount++;
		lNext = lNext->_left;
	}
	while (rNext)
	{
		if (rNext->_color == BLACK)
			rightBlackCount++;
		rNext = rNext->_right;
	}

	// 黑结点个数不同
	if (leftBlackCount != rightBlackCount)
		return false;

	size_t curBlackCount = 0; // 保存每走一步黑结点个数

	return RBTree<K, V>::_IsRBTree(_root, curBlackCount, curBlackCount);
}

// 中序打印
template<class K, class V>
void RBTree<K, V>::Inorder()
{
	cout << "Inorder : ";
	RBTree::_Inorder(_root);
	cout << endl;
}

// 查找key是否存在
template<class K, class V>
bool RBTree<K, V>::Find(const K& key) 
{
	return _Find(_root, key);
}


// 结点个数
template<class K, class V>
size_t RBTree<K, V>::Size()
{
	return _size;
}

// 判空
template<class K, class V>
bool RBTree<K, V>::IsEmpty()
{
	return NULL == _root;
}

// 析构函数
template<class K, class V>
RBTree<K, V>::~RBTree()
{
	_Clear(_root);
}


//////////////////////////////////////////////////////////////////////////
/* 以下为类private 成员函数*/
//////////////////////////////////////////////////////////////////////////

// 切记返回值一定要给模板参数。。。。。
// 拷贝
template<class K, class V>
RBTreeNode<K,V>* RBTree<K, V>::_Copy(const Node* rbNode, Node* parent)
{
	Node* ptemp = NULL;
	if (rbNode != NULL)
	{
		ptemp = new Node(rbNode->_key, rbNode->_val, rbNode->_color);
		ptemp->_parent = parent;
		parent = ptemp;
		ptemp->_left = _Copy(rbNode->_left, parent);
		ptemp->_right = _Copy(rbNode->_right, parent);
	}
	return ptemp;
}

// 递归中序遍历
template<class K, class V>
void RBTree<K, V>::_Inorder(Node *pNode)
{
	if (pNode != NULL)
	{
		RBTree<K, V>::_Inorder(pNode->_left);
		cout << pNode->_val << " ";
		RBTree<K, V>::_Inorder(pNode->_right);
	}

}

// 左旋
template<class K, class V>
void RBTree<K, V>::RotateLeft(Node* parent)
{
	Node* subR = parent->_right;          // 右孩子
	Node* pGrangfather = parent->_parent; // 父亲
	Node* subRL = subR->_left;			  // 右孩子的左孩子

	if (subRL != NULL)
	{
		subRL->_parent = parent;      // 1.如果subRL存在则改变其父指针域指向parent
	}
	parent->_right = subRL;           // 2.改变parent的右指针域
	parent->_parent = subR;           // 3.改变parent的父指针域
	subR->_left = parent;             // 4. 改变subL的左指针域
	subR->_parent = pGrangfather;     // 5. 改变subR的父指针域
	if (pGrangfather != NULL)
	{
		if (pGrangfather->_left == parent)
			pGrangfather->_left = subR;
		else
			pGrangfather->_right = subR;
	}
	else
	{
		_root = subR;
	}

}

// 右旋
template<class K, class V>
void RBTree<K, V>::RotateRight(Node* parent)
{
	Node* subL = parent->_left;           // 左孩子
	Node* pGrandfather = parent->_parent; // 父亲
	Node* subLR = subL->_right;           // 左孩子的右孩子

	if (subLR != NULL)
	{
		subLR->_parent = parent;     // 1. 如果subLR存在改变subLR的父指针域指向parent
	}

	parent->_left = subLR;			 // 2. 改变parent的左指针域
	parent->_parent = subL;			 // 3. 改变parent的父指针域
	subL->_right = parent;           // 4. 改变subL的右指针域
	subL->_parent = pGrandfather;    // 5. 改变subL的父指针域

	if (pGrandfather != NULL)        // 6. 如果parnet存在父节点,则连接否则改变根节点
	{
		if (pGrandfather->_left == parent)
			pGrandfather->_left = subL;
		else
			pGrandfather->_right = subL;
	}
	else
	{
		_root = subL;
	}
}

template<class K, class V>
bool RBTree<K, V>::_Find(Node* pNode, const K& key)
{
	if (pNode != NULL)
	{
		if (pNode->_key == key)
			return true;
		else if (pNode->_key < key)
			return _Find(pNode->_left,key);
		else
			return _Find(pNode->_right, key);
	}
	return false;
}

// 判断是不是红黑树
template<class K, class V>
bool RBTree<K, V>::_IsRBTree(Node* pNode, const size_t blackCount, size_t curBlackCount)
{
	if (NULL == pNode)
		return true;

	if (pNode->_color == RED && pNode->_parent != NULL && pNode->_parent->_color == RED)
		return false;

	if (pNode->_color == BLACK)
		curBlackCount++;

	if (pNode->_left == NULL && pNode->_right == NULL)
	if (blackCount == curBlackCount)
		return false;

	return RBTree<K, V>::_IsRBTree(pNode->_left, blackCount, curBlackCount) &&
		RBTree<K, V>::_IsRBTree(pNode->_right, blackCount, curBlackCount);
}

// 释放所有节点
template<class K, class V>
void RBTree<K, V>::_Clear(Node*& pNode)
{
	if (pNode != NULL)
	{
		_Clear(pNode->_left);
		_Clear(pNode->_right);
		delete pNode;
		pNode = NULL;
	}
}

test.cpp

#include "RBTree.cpp"
#include "RBTree.hpp"

#define ARRAYSIZE(arr) sizeof(arr)/sizeof(arr[0])


void TestRBTreeInsert()
{
	// test1
	int array1[] = { 10, 8, 7, 15, 5, 6, 11, 13, 12 };
	RBTree<int, int> rb1;
	for (size_t idx = 0; idx <ARRAYSIZE(array1); ++idx)
	{
		rb1.Insert(array1[idx], array1[idx]);
		rb1.Inorder();
		cout << "插入 " << array1[idx] << " 满足红黑树性质否? " << boolalpha << rb1.IsRBTree() << endl;
	}
	cout << "-------------------------------------------------------" << endl;

	// test2
	int array2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
	RBTree<int, int> rb2;
	for (size_t idx = 0; idx < ARRAYSIZE(array2); ++idx)
	{
		rb2.Insert(array2[idx], array2[idx]);
		rb2.Inorder();
		cout << "插入 " << array2[idx] << " 满足红黑树性质否? " << boolalpha << rb2.IsRBTree() << endl;
	}
	cout << "-------------------------------------------------------" << endl;
	
	// test3
	RBTree<int, int> rb3;
	int array3[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 1, 3, 2, 1, 0 };

	for (size_t idx = 0; idx < ARRAYSIZE(array3); ++idx)
	{
		rb3.Insert(array3[idx], array3[idx]);
		rb3.Inorder();
		cout << "插入 " << array3[idx] << " 满足红黑树性质否? " << boolalpha << rb3.IsRBTree() << endl;
	}
}

void TestCopyCon_Assign()
{
	// 测试拷贝构造和复制运算符重载
	RBTree<string, int> rb;
	rb.Insert("张三", 22);
	rb.Insert("里斯", 18);
	rb.Insert("呵呵", 12);
	rb.Inorder();
	RBTree<string, int> rbcopy(rb);
	rbcopy.Inorder();
}

void TestOtherFun()
{
	RBTree<string, int> rb;
	cout << rb.Size() << endl;
	cout << boolalpha << rb.IsEmpty() << endl;
	rb.Insert("张三", 22);
	rb.Insert("里斯", 18);
	cout << boolalpha << rb.Find("张三") << endl;
	cout << boolalpha << rb.Find("不存在") << endl;
	rb.Insert("呵呵", 12);
	cout << rb.Size() << endl;
	rb.Inorder();
	cout << boolalpha << rb.IsEmpty() << endl;
	rb.~RBTree();
}



int main()
{
	//TestRBTreeInsert();
	//TestCopyCon_Assign();
	TestOtherFun();

    return 0;
}

    原文作者:算法小白
    原文地址: https://blog.csdn.net/a1414345/article/details/72286913
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞