红黑树的性质、平衡过程的模拟实现、迭代器的模拟实现,以及用自己实现的红黑树封装map和set

目录

1、红黑树

红黑树的性质

2、红黑树的定义:

3、红黑树插入的平衡过程

插入以及调整思路

红黑树的左旋和右旋实现

4、红黑树迭代器的实现

5、用自己实现的红黑树封装set

6、用自己实现的红黑树封装map


1、红黑树

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

红黑树的性质

        1. 每个结点不是红色就是黑色

        2. 根节点是黑色的

        3. 如果一个节点是红色的,则它的两个孩子结点是黑色的

        4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均  包含相同数目的黑色结点

        5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

2、红黑树的定义:

enum color {
	RED,
	BLACK
};

// 为了后序封装map和set,在实现时给红黑树多增加了一个头结点
template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, T&, T*> iterator;
	KeyOfT keyoft;
public:
	RBTree()
	{
		_pHead = new Node;
		_pHead->_pLeft = _pHead;
		_pHead->_pRight = _pHead;
	}

	iterator begin() { return LeftMost(); }
	iterator end() { return RightMost(); }

	// 在红黑树中插入值为data的节点,
	// 注意:为了简单起见,本次实现红黑树不存储重复性元素
	pair<iterator, bool> Insert(const T& data);

	// 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
	Node* Find(const T& data) {
		Node* cur = GetRoot();
		while (cur) {
			if (keyoft(data) == keyoft(cur->_data)) {
				return cur;
			}
			else if (keyoft(data) < keyoft(cur->_data)) {
				cur = cur->_pLeft;
			}
			else if (keyoft(data) > keyoft(cur->_data)) {
				cur = cur->_pRight;
			}
		}
		return cur;
	}

	// 获取红黑树最左侧节点
	Node* LeftMost() {
		Node* cur = _pHead->_pParent;
		while (cur) {
			if (!cur->_pLeft) {
				return cur;
			}
			cur = cur->_pLeft;
		}
		return cur;
	}

	// 获取红黑树最右侧节点
	Node* RightMost() {
		Node* cur = _pHead->_pParent;
		while (cur) {
			if (!cur->_pRight) {
				return cur;
			}
			cur = cur->_pRight;
		}
		return cur;
	}

	//中序遍历
	void Inorder() {
		_inorder(GetRoot());
		cout << endl;
	}

	// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
	bool IsValidRBTRee() {
		size_t black = 0;
		return _IsValidRBTree(GetRoot()->_pLeft, 1, black) && _IsValidRBTree(GetRoot()->_pRight, (size_t)1, black);;
	}
private:

	void _inorder(Node* root) {
		if (!root) {
			return;
		}
		_inorder(root->_pLeft);
		cout << keyoft(root->_data) << " ";
		_inorder(root->_pRight);
	}

	bool _IsValidRBTree(Node* root, size_t blackCount, size_t& pathBlack) {
		if (!root) {
			if (pathBlack == 0) {
				pathBlack = blackCount;
				return true;
			}
			if (blackCount != pathBlack) {
				return false;
			}
		}
		if (root->_col == RED && root->_pParent->_col == RED) {
			return false;
		}
		if (root->_col == BLACK)
			++blackCount;

		return _IsValidRBTree(root->_pLeft, blackCount, pathBlack) && _IsValidRBTree(root->_pRight, blackCount, pathBlack);
	}
	// 左单旋
	void RotateL(Node* cur);
	// 右单旋
	void RotateR(Node* cur);
	// 为了操作树简单起见:获取根节点
	Node*& GetRoot() {
		return _pHead->_pParent;
	}
private:
	Node* _pHead;
};

3、红黑树插入的平衡过程

红黑树在插入的时候,也是根据二叉搜索树的插入规则去插入,插入的新节点的颜色,我们选择将他定义成红色,这样可以省去很多麻烦。

插入节点之后,红黑树的结构可能会被破坏,违背性质中的某几条,

因此我们需要对其进行调整:

插入以及调整思路

pair<iterator, bool> Insert(const T& data) {
		
	Node* node = new Node(data,RED);
	//红黑树为空
	if (!GetRoot()) {
		GetRoot() = node;
		node->_pParent = _pHead;
		node->_col = BLACK;
		_pHead->_pLeft = LeftMost();
		_pHead->_pRight = RightMost();
			
		return make_pair(iterator(node), true);
	}
	//插入非根节点
	Node* cur = GetRoot();
	while (cur) {
		if (keyoft(node->_data) < keyoft(cur->_data)) {
			if (!cur->_pLeft) {
				cur->_pLeft = node;
				node->_pParent = cur;
				break;
			}
			cur = cur->_pLeft;
		}
		else if (keyoft(node->_data) > keyoft(cur->_data)) {
			if (!cur->_pRight) {
				cur->_pRight = node;
				node->_pParent = cur;
				break;
			}
			cur = cur->_pRight;
		}
		else {
			delete node;
			return make_pair(iterator(cur), false) ;
		}
	}
	//插入完成后需要更新头结点的左右节点
	_pHead->_pLeft = LeftMost();
	_pHead->_pRight = RightMost();
		
	//检查当前是否符合红黑树的性质
	cur = node;
	Node* parent = cur->_pParent;
	Node* grendpa = nullptr;
	Node* uncle = nullptr;

	while (parent != _pHead) {
		//更新爷爷节点和叔叔节点
		grendpa = parent->_pParent;
		uncle = nullptr;
		if (parent == grendpa->_pLeft)
			uncle = grendpa->_pRight;
		else
			uncle = grendpa->_pLeft;
			
		//判断
		if (parent->_col == BLACK)
			//如果父亲结点为黑色,符合红黑树性质,直接跳出
			break;
		else if (parent->_col == RED && uncle && uncle->_col == RED) {
			//如果父亲节点为红色,且叔叔节点也为红色
			//那就将父亲和叔叔节点的颜色改为黑色,将爷爷结点改为红色,继续网上更新
			parent->_col = BLACK;
			uncle->_col = BLACK;
			grendpa->_col = RED;

			//更新cur和parent结点
			cur = grendpa;
			parent = cur->_pParent;
		}
		else if (parent->_col == RED && (uncle == nullptr || uncle->_col == BLACK)) {
			//如果父亲的节点为红色,且叔叔节点的颜色为黑色,此时需要进行旋转
			//这里分为四种情况:
			//		1、cur结点为parent的左节点,同时parent也是grendpa的左节点
			//		2、cur结点为parent的右节点,同时parent也是grendpa的右节点
			//	以上两种情况只需要进行右单旋或者左单旋就可以解决
			//		3、cur结点为parent的左节点,同时parent是grendpa的右节点
			//		4、cur结点为parent的右节点,同时parent是grendpa的左节点
			//	以上两种情况需要进行双旋解决

			if (cur == parent->_pLeft && parent == grendpa->_pLeft) {
				//进行右单旋
				RotateR(cur);
				parent->_col = BLACK;
				grendpa->_col = RED;
			}
			else if (cur == parent->_pRight && parent == grendpa->_pRight) {
				//左单旋
				RotateL(cur);
				parent->_col = BLACK;
				grendpa->_col = RED;
			}
			else if (cur == parent->_pLeft && parent == grendpa->_pRight){
				//先右单旋,在左单旋
				RotateR(cur);
				RotateL(cur);
				cur->_col = BLACK;
				grendpa->_col = RED;
			}
			else if (cur == parent->_pRight && parent == grendpa->_pLeft) {
				//先左单旋,在右单旋
				RotateL(cur);
				RotateR(cur);
				cur->_col = BLACK;
				grendpa->_col = RED;
			}
			break;
		}
	}
	//更新根节点为黑色
	GetRoot()->_col = BLACK;
	return make_pair(iterator(node), true);
}

红黑树的左旋和右旋实现

// 左单旋
void RotateL(Node* cur) {
	Node* left = cur->_pLeft;
	Node* right = cur->_pRight;
	Node* parent = cur->_pParent;
	Node* grendpa = parent->_pParent;
		
	//更新cur左子树和parent的关系
	if (left)
		left->_pParent = parent;
	parent->_pRight = left;

	//更新cur和parent的关系
	cur->_pLeft = parent;
	if (GetRoot() == parent) {
		//如果parent为根节点的话,需要链接_pHead
		_pHead->_pParent = cur;
		cur->_pParent = _pHead;
	}
	if (parent == grendpa->_pLeft)
		grendpa->_pLeft = cur;
	else
		grendpa->_pRight = cur;

}
// 右单旋
void RotateR(Node* cur) {
	Node* left = cur->_pLeft;
	Node* right = cur->_pRight;
	Node* parent = cur->_pParent;
	Node* grendpa = parent->_pParent;

	//更新cur右子树与parent的关系
	if (right)
		right->_pParent = parent;
	parent->_pLeft = right;

	//更新cur 和parent的关系
	cur->_pRight = parent;
	if (GetRoot() == parent) {
		//如果parent为根节点,则需要更新_pHead的链接
		_pHead->_pParent = cur;
		cur->_pParent = _pHead;
	}
	if (parent == grendpa->_pLeft)
		grendpa->_pLeft = cur;
	else
		grendpa->_pRight = cur;
}

4、红黑树迭代器的实现

红黑树的迭代器比较抽象的是需要实现 ++ 和 -- ,
 

template<class T, class Ref, class Ptr>
struct __RBTreeIterator {
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;

	Node* _node;

	__RBTreeIterator(Node* node) :_node(node){}

	Ref operator*() {
		return _node->_data;
	}

	Ptr operator->() {
		return &_node->_data;
	}

	bool operator==(const Self& s) {
		return _node == s->_node;
	}

	bool operator!=(const Self& s) {
		return _node != s->_node;
	}

	Self& operator++() {
		if (_node->_pRight)
			_node = _node->_pRight;
		else {
			if (_node == _node->_pParent->_pLeft)
				_node = _node->_pParent;
			else if (_node == _node->_pParent->_pRight) {
				while (_node != _node->_pParent->_pLeft) {
					if (_node == _node->_pParent->_pParent) {
						_node = _node->_pParent;
						break;
					}
					_node = _node->_pParent;
				}
			}
		}
		return *this;
	}
	Self& operator--() {
		if (_node->_pLeft)
			_node = _node->_pLeft;
		else {
			if (_node == _node->_pParent->_pRight)
				_node = _node->_pParent;
			else if (_node == _node->_pParent->_pLeft) {
				while (_node != _node->_pParent->_pRight) {
					if (_node == _node->_pParent->_pParent) {
						_node = _node->_pParent;
						break;
					}
					_node = _node->_pParent;
				}
			}
		}
		return *this;
	}
};

5、用自己实现的红黑树封装set

#include "RedBlackTree.hpp"

//封装set的大框架
namespace ltx {
	template<class K>
	class set {
	public :
		set():_bf(new RBTree<K, K, setKeyOfT>()) {}

		typedef typename RBTree<K, K, setKeyOfT>::iterator iterator;

		iterator begin() { return _bf.begin(); }
		iterator end() { return _bf.end(); }

		pair<iterator, bool> insert(const K& key) {
			return _bf.Insert(key);
		}
	private :
		struct setKeyOfT {
			const K& operator()(const K& key) {
				return key;
			}
		};

	private :
		RBTree<K, K, setKeyOfT> _bf;
	};
}

6、用自己实现的红黑树封装map
 

#include "RedBlackTree.hpp"

//实现封装的大框架
namespace ltx {
	template<class K, class V>
	class map {
	public:
		map() :_bf(new RBTree<K, pair<K, V>, mapKeyOfT>()) {}

		typedef typename RBTree<K, pair<K, V>, mapKeyOfT>::iterator iterator;

		iterator begin() { return _bf.begin(); }
		iterator end() { return _bf.end(); }

		pair<iterator, bool> Insert(const pair<K, V>& kv) {
			return _bf.Insert(kv);
		}

		V& operator[](const K& key) {
			pair<iterator, bool> ret = Insert(make_pair(key, V()));
			return ret.first->second;
		}

	private:
		struct mapKeyOfT{
			const K& operator()(const pair<K, V>& key) {
				return key.first;
			}
		};

	private:
		RBTree<K, pair<K, V>, mapKeyOfT> _bf;
	};
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

孟婆的cappucino

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

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

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

打赏作者

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

抵扣说明:

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

余额充值