数据结构关联式容器底层红黑树的模拟实现

Posted zhaocx111222333

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构关联式容器底层红黑树的模拟实现相关的知识,希望对你有一定的参考价值。


红黑树是一种二叉搜索树,接近平衡。
主要有几个特点:

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的

红黑树节点

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

enum COLOR{
	RED,
	BLACK
};

//红黑树节点
//包含3指向,1颜色,1val
//val可以是pair,和k
template<class V>
struct RBNode{
	RBNode<V>* _parent;
	RBNode<V>* _left;
	RBNode<V>* _right;
	V _val;
	COLOR _color;

	RBNode(const V& val = V())
		:_parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		, _val(val)
		, _color(RED){}
};

迭代器

由于树形结构的底层实现,红黑树的迭代器是封装了node节点的迭代器

//红黑树迭代器
template<class V>
struct RBTreeIterator{
	typedef RBNode<V> Node;
	typedef RBTreeIterator<V> Self;

	Node* _node;

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

	//迭代器一般要实现解引用,->,!=操作符
	//解引用需要返回节点的val的引用,可以修改val
	V& operator*(){
		return _node->_val;
	}

	//->操作符需要返回节点的val的指针
	V* operator->(){
		return &_node->_val;
	}

	//判断迭代器是否相等传参就是迭代器,不是迭代器指针
	bool operator!=(const Self& it){
		return _node != it._node;
	}

	//迭代器的++和--重载
	//返回值就是迭代器本身
	//红黑树的迭代器是根据中序遍历实现,++就是中序遍历的下一个
	Self& operator++(){
		//如果右节点存在,下一个就是右节点的最左节点
		if (_node->_right){
			_node = _node->_right;
			while (_node->_left){
				_node = _node->_left;
			}
		}
		//若右节点不存在,需要向上寻找
		//当node作为父节点的左子树时,下一个才是parent
		//是父节点的右子树,父亲已经先遍历过了
		else{
			Node* parent = _node->_parent;
			while (_node == parent->_right){
				_node = parent;
				parent = parent->_parent;
			}
			//避免出现没有右子树,node==parent(此时parent是root),此时结束遍历了
			//若node再++,程序还会跑,p更新到了root的左子树,node再一次=parent
			//再次进入循环,出现死循环
			if (_node->_right != parent){
				_node = parent;
			}
			
		}
		return *this;//返回调用++的迭代器*(前置++)
	}


	//--和++只有顺序上的不同,逻辑上相同
	Self& operator--(){
		if (_node->_left){
			_node = _node->_left;
			while (_node->_right){
				_node = _node->_right;
			}
		}
		else{
			Node* parent = _node->_parent;
			while (parent->_left == _node){
				_node = parent;
				parent = parent->_parent;
			}
			if (_node->_left != parent){
				_node = parent;
			}
		}
		return *this;
	}

};
//红黑树实现
template<class K,class V,class KeyOfValue>
class RBTree{
public:
	typedef RBNode<V> Node;
	typedef RBTreeIterator<V> iterator;

	RBTree()
		:_header(new Node)
	{
		_header->_left = _header->_right = _header;
		//partent默认为nullptr
	}

	iterator begin(){
		return iterator(_header->_left);
	}
	
	iterator end(){
		return iterator(_header);
	}

	iterator rbegin(){
		return iterator(_header->_right);
	}



红黑树的插入

插入接口非常繁琐,涉及到了各种情况的不同处理,需要旋转操作来保持平衡

	//红黑树的插入
	//返回值是当前插入位置的迭代器和一个bool
	pair<iterator, bool> insert(const V& val){
		//若是空树的情况
		if (_header->_parent == nullptr){
			Node* root = new Node(val);

			_header->_parent = root;
			root->_parent = _header;
			_header->_left = _header->_right = root;

			root->_color = BLACK;
			return make_pair(iterator(root), true);

		}

		//不是空树走二叉搜索树的逻辑
		Node* node = _header->_parent;//无法直接拿到root,需要这样取
		Node* parent = nullptr;

		KeyOfValue kov;
		//注意此时就要使用回调函数区分map,set
		//map的val是<k,v>键值对,比较的是val.first
		//set的val就是k,可以直接比较
		while (node){
			parent = node;
			if (kov(node->_val) == kov(val)){
				return make_pair(iterator(node), false);
			}
			else if (kov(node->_val) > kov(val)){
				node = node->_left;

			}
			else{
				node = node->_right;
			}
		}
			node = new Node(val);
			Node* cur = node;//用于返回迭代器

			if (kov(parent->_val) > kov(node->_val)){
				parent->_left = node;
			}
			else {
				parent->_right = node;
			}
			node->_parent = parent;//加入指向
			

			//开始进行调整,若出现连续的两个红色就需要调整
			//结束条件是node不是根节点,因为根节点是黑色的,node和parent是红色就需要调整
			while (node != _header->_parent&&node->_parent->_color == RED){
				parent = node->_parent;
				Node* gfather = parent->_parent;
				//若父节点是祖父节点的左节点
				if (gfather->_left == parent){
					Node* uncle = gfather->_right;
					//若果uncle存在并为红色
					if (uncle && uncle->_color == RED){
						parent->_color = uncle->_color = BLACK;
						gfather->_color = RED;
						//此时父节点是黑色,gfather为红色,所以更新到红色
						node = gfather;
					}
					else{
					//uncle不存在或者为黑色都需要旋转
					//此时node的位置也需要判断是否为双旋

						if (node == parent->_right){
							RotateL(parent);
							swap(node, parent);

						}
						RotateR(gfather);
						parent->_color = BLACK;
						gfather->_color = RED;
						break;
					}
				}
				//若父节点是祖父节点的右节点
				else{
					Node* uncle = gfather->_left;
					//若果uncle存在并为红色
					if (uncle && uncle->_color == RED){
						parent->_color = uncle->_color = BLACK;
						gfather->_color = RED;
						//此时父节点是黑色,gfather为红色,所以更新到红色
						node = gfather;
					}
					else{
						//uncle不存在或者为黑色都需要旋转
						//此时node的位置也需要判断是否为双旋

						if (node == parent->_left){
							RotateR(parent);
							swap(node, parent);

						}
						RotateL(gfather);
						parent->_color = BLACK;
						gfather->_color = RED;
						break;
					}
				
				}
			}

			//调整结束,node此时可能刚刚结束旋转,gparent为红,并且不会再进入循环
			//需要把根节点改黑色
			_header->_parent->_color = BLACK;

			//更新_header的指向
			_header->_left = leftmost();
			_header->_right = rightmost();
		
			return make_pair(iterator(cur), true);
	}


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

		subR->_left = parent;
		parent->_right = subRL;

		if (subRL)
			subRL->_parent = parent;
		//判断根,不是根的情况需要祖父节点的指向
		if (parent == _header->_parent){
			_header->_parent = subR;
			subR->_parent = _header;
		}
		else{
			Node* gparent = parent->_parent;

			if (gparent->_left == parent){
				gparent->_left = subR;
			}
			else{
				gparent->_right = subR;
			
			}
			subR->_parent = gparent;
			
		}
		parent->_parent = subR;
	}


	//右旋接口
	void RotateR(Node* parent){
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		subL->_right = parent;
		parent->_left = subLR;

		if (subLR)
			subLR->_parent = parent;
		//判断根,不是根的情况需要祖父节点的指向
		if (parent == _header->_parent){
			_header->_parent = subL;
			subL->_parent = _header;
		}
		else{
			Node* gparent = parent->_parent;

			if (gparent->_left == parent){
				gparent->_left = subL;
			}
			else{
				gparent->_right = subL;

			}
			subL->_parent = gparent;
			
		}
		parent->_parent = subL;
	}

	Node* leftmost(){
		Node* node = _header->_parent;
		while (node&& node->_left){
			node = node->_left;
		}
		return node;
	}

	Node* rightmost(){
		Node* node = _header->_parent;
		while (node && node->_right){
			node = node->_right;
		}
		return node;
	}

	void inorder(){
		_inorder(_header->_parent);
		cout << endl;
	}

	void _inorder(Node* root){
		if (root){
			_inorder(root->_left);
			cout << root->_kv.first << " ";
			_inorder(root->_right);
		
		}
	}

验证

根据红黑树的特点验证树

	//红黑树的验证
	bool isBalence(){
		//空树也是红黑树
		if (_header->_parent == nullptr){
			return true;
		}

		Node* root = _header->_parent;
		//根节点必须为黑色
		if (root->_color == RED){
			return false;
		}

		//统计一条路径黑色节点的个数
		int bcount = 0;
		Node* node = root;
		while (node){
			if (node->_color == BLACK){
				++bcount;
			}
			node = node->_left;
		}
		
		cout << "Bcount: " << bcount << endl;

		int nodebcount = 0;
		return _isBalence(root, bcount, nodebcount);
	}


	//递归判断每一条路径上的节点数字是否相等
	//递归的结束条件:当递归到空指针时,用计数器的节点大小相比
	//需要返回上一级是否当前路径的数字相等,bool返回值
	//本轮递归需要判断是否为黑色并++,并继续判定左右子树是否为黑色
	bool _isBalence(Node* root,int &bcount,int nodecount){
		if (root == nullptr){
			if (nodecount == bcount){
				return true;
			}
			else{
				return false;
			}
		}

		if (root->_color == BLACK){
			++nodecount;
		}
		//判断是否有相邻的红色节点
		if (root->_parent && root->_color == RED && root->_parent->_color == RED){
			cout << "data: " << root->_kv.first << endl;
			return false;
		}
		return _isBalence(root->_left, bcount, nodecount) &&
			_isBalence(root->_right, bcount, nodecount);
	}

private:
	Node* _header;
};

以上是关于数据结构关联式容器底层红黑树的模拟实现的主要内容,如果未能解决你的问题,请参考以下文章

[C/C++]详解STL容器9-基于红黑树模拟实现map和set

[C/C++]详解STL容器9-基于红黑树模拟实现map和set

[C/C++]详解STL容器9-基于红黑树模拟实现map和set

[C/C++]详解STL容器9-基于红黑树模拟实现map和set

10.STL简单红黑树的实现

手撕STLmap和set