红黑树的实现

目录

红黑树的概念

红黑树的插入

情况1

情况2

情况3 

红黑树节点的代码实现

 红黑树迭代器的代码实现

红黑树本体代码的实现


红黑树的概念

红黑树本质上是一棵二叉搜索树,只不过在此基础上增加了一些条件规定与限制

1.结点是红色或黑色。

2.根结点是黑色。

3.每个叶子结点都是黑色的空结点(NIL结点)。

4 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)

5.从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。

空树也是红黑树

红黑树的插入

这个也是红黑树的精髓,其他的地方代码一看就懂了

我们假设有三种情况

情况1

叔叔节点存在且为红

如图,星星可以表示空,也可以表示子树

cur为新的红色的节点

在这种情况下,cur和p两个必须有一个要变成红色,如果把cur变成黑色,你会发现不符合上面的规定5,而且这种情况想改都没有办法改,所以我们需要把p变成黑色,虽然也不符合规定5,但是我们可以继续把u节点变成黑色,这样对于这颗子树就符合红黑树的规定了

但是如果g上面还有双亲,对于g的双亲,又不满足规定5了,所以我们需要对g进行修改,将g改为红色,这样就好了,有人可能会问了,那万一g的双亲也是红色的,怎么办,不急,我们不是说了么,星星可以代表子树,那可能到时候就又是情况1了,不是也无妨,我们看情况2

情况2

叔叔节点不存在||存在且为黑

类似于这种,在这种情况下,我们可以得到一个结论,cur一定不是叶子节点,为什么呢,因为如果cur是叶子结点,那在它插入之前,你可以看一下,这棵树已经不是红黑树了,所以cur不是页子节点,而是调整上来的,就比如说情况1把g节点变成红色了,这个cur可能就是情况1的g

说这个有什么用呢?这表明,cur的子树里面一定有黑色节点,因为它是调整上来的嘛,它原本就是黑色节点,后来调整成为了红色,那它的下面就一定得有黑色的节点,那可得,p的右子树里面也一定有黑色节点,不然cur是黑色的时候就不满足规定5了

现在cur和p必须要有一个变成黑色,肯定不能是cur,因为它是调整上来的,一变又回去了,所以只能是p变成黑色,但是对应g,又不满足规定5了,所以我们得继续调整,我们需要把g变为黑色,然后进行右单旋,旋转之后就满足了,如果不知道什么是右单旋可以看我的AVL树的博客,里面介绍了右单旋

(56条消息) AVL树插入操作与验证操作的简单实现_芜湖开冲~的博客-CSDN博客

之后就符合红黑树的性质了

有的同学可能说,你这不符合规定5啊,我们前面已经讨论过了,cur底下的两棵子树必定有黑色节点,而且g的左子树,也就是原本p的右子树,里面也一定有黑色节点

情况3 

叔叔节点不存在||存在且为黑

只不过这一次cur是在内侧

 这种情况和情况2很像,直接说结论吧,把parent变成黑色,g变成红色,然后对p左单旋变成情况2

红黑树节点的代码实现

//红黑树的节点
template <class T>
struct RBTreeNode {
	//构造函数,默认颜色是红色,因为如果默认是黑色的,那必须每一次增加一个新的节点,都必须调整一次
	//而新增红色的节点有时候可以不用调
	RBTreeNode(const T& value = T(), const Colour& colour = RED) 
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_col(colour)
		,_val(value)
	{}

	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;
	T _val;
};

 红黑树迭代器的代码实现

//红黑树的迭代器结构,本质也是对节点的封装
template <class T>
struct RBTreeIterator {
	
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T> Self;

	RBTreeIterator(Node* pNode) 
		:_pNode(pNode)
	{}

	//让迭代器拥有类似于指针的功能
	//解引用
	T& operator*() {
		return _pNode->_val;
	}
	//->运算符,这里把它当做一个一元的后缀运算符,返回的是一个指针,表示的是指针里面值的地址
	//感觉好像是退裤子放屁,其实不是,因为迭代器的值可能是一个结构体里面的某一个值,取的是那一个值的地址
	T* operator->() {
		return &(operator*());
	}
	//前置++与后置++
	Self& operator++() {
		Increament();
		return *this;
	}
	//迭代器本身指向的是新的元素,而返回的是上一个元素
	Self operator++(int) {
		Self temp(*this);
		Increament();
		return temp;
	}
	//前置--与后置--
	Self& operator--() {
		DeIncreament();
		return *this;
	}
	Self operator--(int) {
		Self temp(*this);
		DeIncreament();
		return temp;
	}
	//迭代器比较
	bool operator!=(const Self& s) {
		return _pNode != s._pNode;
	}
	bool operator==(const Self& s) {
		return _pNode == s._pNode;
	}

	//迭代器向后移动
	//对于红黑树来说,就是找比它大的节点中最小的那个
	void Increament() {
		if (_pNode->_right) {
			_pNode = _pNode->_right;
			while (_pNode->_left) {
				_pNode = _pNode->_left;
			}
		}
		else {
			Node* parent = _pNode->_parent;
			while (parent->_right == _pNode) {
				_pNode = parent;
				parent = parent->_parent;
			}
			if (_pNode->_right != parent) {
				_pNode = parent;
			}
		}
	}
	//迭代器向前移动
	void DeIncreament() {
		if (_pNode->_parent->_parent == _pNode && _pNode->_col == RED) {
			_pNode = _pNode->_right;
			return;
		}
		if (_pNode->_left) {
			_pNode = _pNode->_left;
			while (_pNode->_right) {
				_pNode = _pNode->_right;
			}
		}
		else {
			Node* parent = _pNode->_parent;
			while (parent->_left == _pNode) {
				_pNode = parent;
				parent = parent->_parent;
			}
			_pNode = parent;
		}
	}
	Node* _pNode;
};

红黑树本体代码的实现

//红黑树,约定:不考虑key重复的情况
template <class T, class KeyOfValue> //这里的KeyOfValue不用管,是用于map,set封装的时候使用的
class RBTree {
	typedef RBTreeNode<T> Node;
public:
	typedef RBTreeIterator<T> iterator;
public:
	RBTree()
		: _size(0)
	{
		_pHead = new Node;
		_pHead->_left = _pHead;
		_pHead->_right = _pHead;
		_pHead->_parent = nullptr;
	}

	~RBTree() {
		_Destroy(_pHead->_parent);
		delete _pHead;
		_pHead = nullptr;
		_size = 0;
	}
	//插入值为data的节点,iterator表示这个节点的迭代器,bool表示是否插入成功
	pair<iterator, bool> Insert(const T& data) {
		//找到要插入的位置
		KeyOfValue kov;
		//空树
		Node* newnode = nullptr;
		if (_size == 0) {
			newnode = new Node(data, BLACK);
			newnode->_parent = _pHead;
			_pHead->_parent = newnode;
		}
		//非空,用二叉搜索树的方式插入节点
		else {
			Node* cur = _pHead->_parent;
			Node* parent = _pHead;
			//找位置
			while (cur) {
				parent = cur;
				if (kov(data) < kov(cur->_val)) {
					cur = cur->_left;
				}
				else if (kov(data) > kov(cur->_val)) {
					cur = cur->_right;
				}
				else {
					return make_pair(iterator(_pHead), false);
				}
			}
			cur = new Node(data);
				if (kov(data) < kov(parent->_val)) {
					parent->_left = cur;
				}
				else {
					parent->_right = cur;
				}
			cur->_parent = parent;
			newnode = cur;
			while (parent != _pHead && parent->_col == RED) {
				//parent是红色的,所以不是根节点,也不可能原本是根节点,被改成红节点
				//因为此时还没有对parent进行操作呢,也不可能是_pHead,所以一定有双亲
				Node* grandFather = parent->_parent;
				//分两大类,parent在grandFather左和右
				//在左
				if (parent == grandFather->_left) {
					Node* uncle = grandFather->_right;
					if (uncle && RED == uncle->_col) {
						// 叔叔节点存在且为红
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandFather->_col = RED;
						cur = grandFather;
						parent = cur->_parent;
					}
					else {
						// 叔叔节点为空 || 叔叔节点存在且为黑
						// cur在内侧
						if (cur == parent->_right)
						{
							// 先对parent进行左单旋,然后将parent和cur交换---->变成cur在外侧
							RotateL(parent);
							swap(parent, cur);
						}

						// cur在外侧
						// 将祖父和双亲节点的颜色交换,然后再对祖父树进行右单旋
						grandFather->_col = RED;
						parent->_col = BLACK;
						RotateR(grandFather);
					}
				}
				//在右
				else {
					Node* uncle = grandFather->_left;
					if (uncle && RED == uncle->_col)
					{
						// 叔叔节点存在且为红
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandFather->_col = RED;
						cur = grandFather;
						parent = cur->_parent;
					}
					else
					{
						// 叔叔节点不存在 || 叔叔节点存在并且颜色是黑色
						if (cur == parent->_left)
						{
							//cur在内侧
							RotateR(parent);
							swap(cur, parent);
						}

						//cur在外侧
						parent->_col = BLACK;
						grandFather->_col = RED;
						RotateL(grandFather);
					}
				}
			}
		}
		// 需要更新_pHead的left和right指针域
		_pHead->_left = _LeftMost();
		_pHead->_right = _RightMost();
		_pHead->_parent->_col = BLACK;
		++_size;
		return make_pair(iterator(newnode), true);
	}

	//迭代器,之所以第一个字母大写,一方面是内部整齐,另一方面是一般不直接用红黑树,而是封装使用
	//所以不必考虑外部使用的情况
	iterator Begin() {
		return iterator(_LeftMost());
	}
	iterator End() {
		return iterator(_pHead);
	}

	// 判空
	bool Empty()const {
		return _size == 0;
	}

	//红黑树中有效节点的个数
	size_t Size()const {
		return _size;
	}

	void Clear() {
		_Destroy(GetRoot());
		_size = 0;
	}
	// 查找
	iterator Find(const T& data) {
		Node* cur = GetRoot();
		if (cur == nullptr) {
			return iterator(_pHead);
		}
		KeyOfValue kov;
		while (cur != nullptr) {
			if (kov(data) < kov(cur->_val)) {
				cur = cur->_left;
			}
			else if (kov(data) > kov(cur->_val)) {
				cur = cur->_right;
			}
			else {
				return iterator(cur);
			}
		}
		return iterator(_pHead);
	}

	//交换
	void Swap(RBTree<T, KeyOfValue>& t) {
		std::swap(_pHead, t._pHead);
	}

private:
	//寻找最小节点
	Node* _LeftMost() {
		Node* cur = GetRoot();
		//判断一下,不然底下不好写
		if (nullptr == cur) {
			return _pHead;
		}
		while (cur->_left) {
			cur = cur->_left;
		}
		return cur;
	}

	//寻找最大节点
	Node* _RightMost() {
		Node* cur = GetRoot();
		if (nullptr == cur) {
			return _pHead;
		}
		while (cur->_right) {
			cur = cur->_right;
		}
		return cur;
	}

	//销毁树
	void _Destroy(Node*& pRoot) {
		if (pRoot) {
			_Destroy(pRoot->_left);
			_Destroy(pRoot->_right);
			delete pRoot;
			pRoot = nullptr;
		}
	}

	//左单旋
	void RotateL(Node* parent) {
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		// 右单支
		if (subRL) {
			subRL->_parent = parent;
		}

		subR->_left = parent;

		// 更新parent和subR的双亲
		Node* pparent = parent->_parent;
		parent->_parent = subR;
		subR->_parent = pparent;

		// 还需要处理pparent的孩子
		if (pparent == _pHead) {
			_pHead->_parent = subR;
		}
		else {
			if (parent == pparent->_left) {
				pparent->_left = subR;
			}
			else {
				pparent->_right = subR;
			}
		}
	}

	void RotateR(Node* parent) {
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		// 左单支
		if (subLR) {
			subLR->_parent = parent;
		}

		subL->_right = parent;

		// 更新parent和subL的双薪
		Node* pparent = parent->_parent;
		parent->_parent = subL;
		subL->_parent = pparent;

		if (pparent == _pHead) {
			_pHead->_parent = subL;
		}
		else {
			if (parent == pparent->_left) {
				pparent->_left = subL;
			}
			else {
				pparent->_right = subL;
			}
		}
	}

	Node* GetRoot() {
		return _pHead->_parent;
	}

private:
	Node* _pHead;
	size_t _size;
};

实现里面的细节我已经放到注释里面了,如果还有什么不懂的,欢迎评论区留言

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值