C++之map和set模拟实现

前言

在map和set的使用文章中提到了C++STL中的map和set的底层其实就是用的红黑树来实现的,所以可以用红黑树简单模拟实现一下STL中的map和set.


STL源码中map和set的实现 

map: 

我们看到它的底层这个成员变量其实就是一棵红黑树, 之前说过map其实就对应搜索树的KV模型,那其实这里的key_type和data_type就对应的是Key和Value的的类型, 还有一个value_type是一个pair, 而且key_type和value_type都传给了红黑树成员. 

 set:

 首先set的底层也是红黑树, 我们会发现map和set底层用的是同一个红黑树的类模板, 但是它们不是一个对应KV模型, 一个对应K模型嘛, 那我们想的可能是应该实现两个红黑树, 一棵存Key的单个值,另一棵存KV的键值对, 然后用这两棵红黑树分别封装map和set。

额外说明一下, stl_map.h里没有包含红黑树的头文件, 但是可以使用红黑树, 因为红黑树是间接包含的,set也是同理: 

这里是如何做到map和set底层都用同一个红黑树的类模板呢? 

观察红黑树的结点的成员变量, 发现并不能直接观察出红黑树是k模型还是kv模型, 再看红黑树的成员, 用Value对结点做初始化, 简单来说就是红黑树的第二个模板参数决定了node中存的是什么. 

rb_tree是k结构还是kv结构是由第二个模板参数决定, 这是泛型编程的思想.

比如map中value_type是一个pair, set中value_type是key, 它们把value_type作为rb_tree的第二个模板参数传递, value_type是什么, 这棵树的结点内容就存什么, 从而决定了他们的k/kv结构.

可以发现set中key_type和value_type都是key, 给rb_tree传了两遍, 这不是重复了吗? 而且map中key_type是key不是可以通过pair获取吗, 为什么还要传一个key_type? 

先想一下map和set都有find, erase这些接口, 那它们有什么不同?
set它的查找是按结点里面存的key去查找的, 返回的是对应元素的迭代器.
map它里面存的是KV的键值对, 但是它查找的时候是不是只拿K去查找啊, 因为key是唯一的,而value其实是可以重复的。 因为map里面存的是KV的键值对, 但是查找这些操作的时候是以K为目标去查找的,所以红黑树里面要多搞出来一个参数Key来单独获取这个Key。
那其实对于set来说是不需要的(因为set里面存的就是单独一个key,查找也是用的key, 上面我们也看了, 它传的前两个模板就是一样的, 都是key), 但是因为set要和map共用一个红黑树模板, 所以必须迎合这个红黑树的结构, 也去多传一个。
其实map也可以不用第一个参数, 查找的时候用pair.first就行了, 但是set里面只存了一个key,它不能进行.first, 因为模板是泛型编程,一套逻辑要对所有的实例都适用, 所以红黑树才要增加一个模板参数, 这样红黑树里面的find, erase这些函数就可以以一种统一的实现(即都用第一个模板参数接收的key去查找), 这样map和set才可以共同复用同一个红黑树的模板类.

改造红黑树+封装map和set 

红黑树结构修改 

结点结构修改:

首先结点的结构只能传一个模板参数来限定类型, 和库中一样:

emplate<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _parent;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _left;
	T _data;
	COLOR _col;

	RBTreeNode(const T& data)
		: _parent(nullptr)
		, _right(nullptr)
		, _left(nullptr)
		, _data(data)
		, _col(RED)
	{}

};

但我们的跟库里面源码还是不太一样,库里还继承了一个base, 里面又实现了一些其他功能,求最大值最小值之类,但没什么影响。

 树的结构也要修改: 

template<class Key,class Value>
class RBTree
{
	typedef RBTreeNode<Value> Node;
public:
    //..成员函数
private:
	Node* _root = nullptr;
};

 map、set的结构定义:

template<class K>
class set
{
public:
    //成员函数
private:
	RBTree<K,K> rb_tree;
};
template<class K,class V>
class map
{
public:

private:
	RBTree<K, pair<const K, V>> rb_tree;
};

因为map里面pair的键key不能修改, 所以我们加一个const, 库里面也是这样的。


 insert的封装 

其实还是复用红黑树的Insert, 当然之前我们学过map和set的使用, 它们insert的返回值其实是一个pair(只是插入一个元素的那个重载), 这里先这样写,后面需要的时候再结合具体场景修改。 

先粘贴过来: 

bool Insert(const Value& data)
{
	if (_root == nullptr)
	{
		_root = new Node(data);
		_root->_col= BLACK;
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_data > data)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_data < data)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			return false;
		}
	}

	cur = new Node(data);
	//cur->_col = RED;
	if (parent->_data >data)
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	else if (parent->_data < data)
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
		assert(false);
	
	//插入完成, 调整颜色
	parent的颜色是黑,不需要调整
	//if (parent->_col == BLACK)
	//{
	//	return true;
	//}
	//parent的颜色是红,需要调整
	while (parent && parent->_col == RED)
	{
		Node* grandparent = parent->_parent;
		//parent在grandparent的左
		//		 g			      g
		//   p	u		   p 	  u
		//c					  c
		if (parent == grandparent->_left)
		{
			Node* uncle = grandparent->_right;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandparent->_col = RED;
				cur = grandparent;
				parent = cur->_parent;
			}
			else
			{
				//		 g
				//   p	u
				//c
				if (cur == parent->_left)
				{
					rotateR(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				//		 g
				//   p	u
				//      c
				else if (cur == parent->_right)
				{
					rotateL(parent);
					rotateR(grandparent);
					grandparent->_col = RED;
					cur->_col = BLACK;
				}
			}
		}
		//parent在grandparent的左
		//		 g			      g
		//   u 	p		   u 	  p
		//				c			c
		else if (parent == grandparent->_right)
		{
			Node* uncle = grandparent->_left;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandparent->_col = RED;
				cur = grandparent;
				parent = cur->_parent;
			}
			else
			{
				//		 g
				//   u		p
				//				c
				if (cur == parent->_right)
				{
					rotateL(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				//		 g
				//   u		p
				//      c
				else if (cur == parent->_left)
				{
					rotateR(parent);
					rotateL(grandparent);
					grandparent->_col = RED;
					cur->_col = BLACK;
				}
			}
		}
		_root->_col = BLACK;
	}
	return true;
}

 我们前面实现的红黑树是K模型的, 里面的插入就是按照结点里面的_data去比较的

但是现在map和set都是复用这棵红黑树, 所以data里面可能是单独一个key(那就是set实例化的时候),也可能是一个pair(那就对于map).
那set肯定没问题, 因为set对应的就是K模型, 内置类型的比较可以直接比较.
那map的时候呢, map的data里面就是存的pair的键值对, 那pair虽然也是可以比较大小的,但是pair比较大小的方式不是我们想要的, 它是pair.first和pair.second里有一个小就小, 我们需要的是pair.first进行比价, 不需要pair.second的参与.

 那我们怎么去解决?

一种思路是可以用类型去判断, typeid.name去判断类型: 

if(typeid(cu->_data).name == K)
{
    //处理set..
}

库里面的做法是接收了第三个模板参数, 第三个参数其实就是为了解决这个问题而引入的,它接收一个仿函数, 用这个仿函数取出对应的key值, 先来看map取出pair里面的first, set直接返回key即可:

struct SetKeyOfValue
{
	const K& operator()(const K& v)
	{
		return v;
	}
};

struct MapKeyOfValue
{
	const K& operator()(const pair<K, V>& kv)
	{
		return kv.first;
	}
};

这个仿函数接收一个pair, 返回里面的first, 然后对于set, 其实是不需要的, 但是为了匹配, 因为它们用了同一个红黑树模板, 所以也要写一个, 然后把这个仿函数传给RBTree的第三个模板参数,这样在红黑树里面,我们不需要管data是单独的key还是pair类型的数据,都可以用一个统一的方式拿到真正用了比较的那个单独的key

bool Insert(const Value& data)
{
	if (_root == nullptr)
	{
		_root = new Node(data);
		_root->_col= BLACK;
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (KeyOfValue()(cur->_data) > KeyOfValue()(data))
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (KeyOfValue()(cur->_data) < KeyOfValue()(data))
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			return false;
		}
	}

	cur = new Node(data);
	//cur->_col = RED;
	if (KeyOfValue()(parent->_data) > KeyOfValue()(data))
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	else if (KeyOfValue()(parent->_data) < KeyOfValue()(data))
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
		assert(false);
	
	//插入完成, 调整颜色
	parent的颜色是黑,不需要调整
	//if (parent->_col == BLACK)
	//{
	//	return true;
	//}
	//parent的颜色是红,需要调整
	while (parent && parent->_col == RED)
	{
		Node* grandparent = parent->_parent;
		//parent在grandparent的左
		//		 g			      g
		//   p	u		   p 	  u
		//c					  c
		if (parent == grandparent->_left)
		{
			Node* uncle = grandparent->_right;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandparent->_col = RED;
				cur = grandparent;
				parent = cur->_parent;
			}
			else
			{
				//		 g
				//   p	u
				//c
				if (cur == parent->_left)
				{
					rotateR(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				//		 g
				//   p	u
				//      c
				else if (cur == parent->_right)
				{
					rotateL(parent);
					rotateR(grandparent);
					grandparent->_col = RED;
					cur->_col = BLACK;
				}
			}
		}
		//parent在grandparent的左
		//		 g			      g
		//   u 	p		   u 	  p
		//				c			c
		else if (parent == grandparent->_right)
		{
			Node* uncle = grandparent->_left;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandparent->_col = RED;
				cur = grandparent;
				parent = cur->_parent;
			}
			else
			{
				//		 g
				//   u		p
				//				c
				if (cur == parent->_right)
				{
					rotateL(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				//		 g
				//   u		p
				//      c
				else if (cur == parent->_left)
				{
					rotateR(parent);
					rotateL(grandparent);
					grandparent->_col = RED;
					cur->_col = BLACK;
				}
			}
		}
		_root->_col = BLACK;
	}
	return true;
}

红黑树迭代器实现

迭代器只要实现红黑树的, map和set直接复用就行了, 那红黑树的迭代器呢其实就是结点指针的封装,与list的迭代器差不多

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;
    }
}

 map中:

 set中:

迭代器的begin和end: 

begin返回的应该返回指向中序遍历第一个结点的迭代器, 而中序遍历的第一个结点就是整棵树最左边的那个结点。

iterator begin()
{
	Node* cur = _root;
	while (cur->_left)
	{
		cur = cur->_left;
	}
	return iterator(cur);
}

 end返回中序遍历最后一个结点的下一个元素, 最后一个结点后面就没有元素了, 所以我们直接用空指针构造一个迭代器返回就行了

iterator end()
{
	return iterator(nullptr);
}

const迭代器: 

const_iterator begin()	const
{
	Node* cur = _root;
	while (cur->_left)
	{
		cur = cur->_left;
	}
	return const_iterator(cur);
}

const_iterator end() const
{
	return const_iterator(nullptr);
}

map和set中的begin和end都是一样的, 直接返回红黑树的begin和end: 


++和- -的重载:

最开始迭代器it在1这个结点的位置(它是中序遍历第一个), 其实不论it当前在那个位置, 我们都可以认为它在当前所处的这棵子树的根结点上, 那么根结点访问完, 中序遍历的话下面访问右子树, 那对于右子树来说, 如果它不为空的话, 同样要对它进行中序, 所以it下面要访问的就是it的右子树的最左结点, 前提是右子树不为空.

如果是右为空的情况呢? 

1.如果这个结点是它父亲的左子树, 比如22, 22右子树为空相当于访问完了, 它是父亲的左子树, 所以下一个一定要访问它的父亲, 直接访问即可, 不需要其他的判断.

2.如果这个结点是它父亲的右子树, 比如6,6的右子树为空, 相当于6这棵树访问完了, 6是1的右子树, 说明1的右子树访问完了, 1是8的左子树, 所以1这棵树访问完就相当于8的左子树访问完了, 那下面就要访问根结点8了, 所以如果右为空且它是父亲的右子树的话, 我们就应该往上去判断it是不是它父亲的左子树, 如果不是, 就顺着父亲指针往上走(比如27 25 17 13一直走到空), 是的话停下来,代表此时父亲的左子树访问完了,此时这个父亲就是下一个要访问的结点.

Self operator++()
{
	if (_node->_right)
	{
		_node = _node->_right;
		while (_node->_left)
		{
			_node = _node->_left;
		}
		return _node;
	}
	else
	{
		if (_node == _node->_parent->_left)
		{
			_node = _node->_parent;
		}
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return _node;
	}
}

operator--其实就是跟++的反过来, 中序是左子树,根,右子树, 那- -就右子树、根、左子树.

1. 先判断的左子树为不为空,不为空就访问左子树的最右结点.

2. 如果左为空, 它是父亲的右子树, 那直接返回父亲,比如11

如果它是父亲的左子树, 那就要向上去找it是谁的右子树, 找到的话当前it的这个父亲就是下一个要访问的结点.

Self operator--()
{
	if (_node->_left)
	{
		_node = _node->_left;
		while (_node->_right)
		{
			_node = _node->_right;
		}
		return _node;
	}
	else
	{
		if (_node == _node->_parent->_right)
		{
			_node = _node->_parent;
		}
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return _node;
	}
}

源码的实现跟我们有一些不同:

他给这棵红黑树增加了一个头结点, 头结点的左指针指向最左结点(中序第一个), 头结点的右指针指向最右结点, 然后它的parent指向根结点, 根结点的parent指向这个头结点.

这样迭代器end就指向这个头结点, 就不为空, 这样的好处是对end–的话可以找到前一个结点,而像上面那样写的end设置为空, end--会出现解引用空指针的问题, 因为我们的end使用空nullptr构造的, 就没法向前寻找前一个结点, 因此最好的方式是将end()放在头结点的位置.


map的[]重载 

学习map的使用时,我们知道map重载的[]. 底层是跟insert的返回值有关系的, 所以我们得修改一下红黑树的insert,应该让它返回一个pair:

当插入成功的时候, pair的first为指向新插入元素的迭代器, second为true, 当插入失败的时候(其实就是插入的键已经存在了),那它的first为容器中已存在的那个元素的迭代器, second为false, 后面这个bool标识插入成功还是失败.

1.如果根是空 

 2.插入失败

3. 插入成功

 

 map和set里面insert的封装也要改:

map重载[]:


测试: 

void test_set()
{
	test::set<int> s;
	s.insert(4);
	s.insert(1);
	s.insert(2);
	s.insert(3);
	s.insert(2);
	s.insert(0);
	s.insert(10);
	s.insert(5);

	test::set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

 

void test_map()
{
	test::map<string, string> dict;
	dict.insert(make_pair("sort", "排序"));
	dict.insert(make_pair("sort", "排序"));
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));

	test::map<string, string>::iterator it = dict.begin();
	while (it != dict.end())
	{
		//it->first += 'x'; //修改key会报错
		it->second += 'y';

		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;

	string arr[] = {"苹果","香蕉","苹果"};
	test::map<string, int> countMap;
	for (auto& e : arr)
	{
		countMap[e]++;
	}

	for (auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;
}

 


关于元素不能修改的问题 

set里面的元素是不能修改的, 因为底层是搜索树, 如果可以随意修改的话就会破坏搜索树的关系;而对于map, key是不可以修改的, 不过value可以修改。  

目前的实现中set元素是可以修改的:

 

 

如何解决呢? 

库里面是怎么解决的:

先来看set里面的迭代器,它里面的普通迭代器和const迭代器都是用的红黑树的const迭代器, 它虽然看起来是普通的迭代器, 但是还是const迭代器, 再写了一个const迭代器是为了符合规范. 

所以我们也在这里加上typedef: 

 但是会出现这个问题: 

 

 这里类型转换发生了错误, 因为rb_tree的insert返回的是一个普通迭代器, 而set里虽然形式上写的是iterator, 但是这个iterator其实还是红黑树里的const迭代器, 为什么不能直接让红黑树的insert也返回const迭代器, 这样类型不就符合了吗? 但是这仅仅满足了set, map还要用这个返回值进行修改, 需要的是普通迭代器. 

怎么解决? 

要想办法让类型匹配起来, 这里就可以利用pair自身的那个既是构造又是拷贝构造的拷贝构造, 让红黑树里的insert返回值的第一个元素变成Node*, 当它作为set的返回值的时候, set的pair自己根据Node*去构造一个自己的迭代器(const迭代器),  当它作为map的返回值的时候, map的pair根据Node*去构造自己的迭代器(普通迭代器).

编译通过了, 而且想要修改set的值也不行了:

 

 


代码

RBTree.h

#pragma once
#include<assert.h>
enum COLOR
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _parent;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _left;
	T _data;
	COLOR _col;

	RBTreeNode(const T& data)
		: _parent(nullptr)
		, _right(nullptr)
		, _left(nullptr)
		, _data(data)
		, _col(RED)
	{}

};

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;
	}

	Self operator++()
	{
		if (_node->_right)
		{
			_node = _node->_right;
			while (_node->_left)
			{
				_node = _node->_left;
			}
			return _node;
		}
		else
		{
			if (_node == _node->_parent->_left)
			{
				_node = _node->_parent;
			}
			else
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent && parent->_right == cur)
				{
					cur = parent;
					parent = parent->_parent;
				}
				_node = parent;
			}
			return _node;
		}
	}

	Self operator--()
	{
		if (_node->_left)
		{
			_node = _node->_left;
			while (_node->_right)
			{
				_node = _node->_right;
			}
			return _node;
		}
		else
		{
			if (_node == _node->_parent->_right)
			{
				_node = _node->_parent;
			}
			else
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent && cur == parent->_left)
				{
					cur = parent;
					parent = parent->_parent;
				}
				_node = parent;
			}
			return _node;
		}
	}

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

template<class Key,class Value,class KeyOfValue>
class RBTree
{
	typedef RBTreeNode<Value> Node;

public:
	typedef __RBTreeIterator<Value, Value&, Value*> iterator;
	typedef __RBTreeIterator<Value, const Value&, const Value*> const_iterator;

	iterator begin()
	{
		Node* cur = _root;
		while (cur->_left)
		{
			cur = cur->_left;
		}
		return iterator(cur);
	}

	iterator end()
	{
		return iterator(nullptr);
	}

	const_iterator begin()	const
	{
		Node* cur = _root;
		while (cur->_left)
		{
			cur = cur->_left;
		}
		return const_iterator(cur);
	}

	const_iterator end() const
	{
		return const_iterator(nullptr);
	}

	pair<Node*,bool> Insert(const Value& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col= BLACK;
			return make_pair(_root, true);
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (KeyOfValue()(cur->_data) > KeyOfValue()(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (KeyOfValue()(cur->_data) < KeyOfValue()(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return make_pair(cur, false);
			}
		}

		cur = new Node(data);
		Node* newnode = cur;//需要保存一下cur, 因为最后要返回这个节点, cur在下面的调整中可能会发生变化
		if (KeyOfValue()(parent->_data) > KeyOfValue()(data))
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else if (KeyOfValue()(parent->_data) < KeyOfValue()(data))
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
			assert(false);
		
		//插入完成, 调整颜色
		parent的颜色是黑,不需要调整
		//if (parent->_col == BLACK)
		//{
		//	return true;
		//}
		//parent的颜色是红,需要调整
		while (parent && parent->_col == RED)
		{
			Node* grandparent = parent->_parent;
			//parent在grandparent的左
			//		 g			      g
			//   p	u		   p 	  u
			//c					  c
			if (parent == grandparent->_left)
			{
				Node* uncle = grandparent->_right;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandparent->_col = RED;
					cur = grandparent;
					parent = cur->_parent;
				}
				else
				{
					//		 g
					//   p	u
					//c
					if (cur == parent->_left)
					{
						rotateR(grandparent);
						parent->_col = BLACK;
						grandparent->_col = RED;
					}
					//		 g
					//   p	u
					//      c
					else if (cur == parent->_right)
					{
						rotateL(parent);
						rotateR(grandparent);
						grandparent->_col = RED;
						cur->_col = BLACK;
					}
				}
			}
			//parent在grandparent的左
			//		 g			      g
			//   u 	p		   u 	  p
			//				c			c
			else if (parent == grandparent->_right)
			{
				Node* uncle = grandparent->_left;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandparent->_col = RED;
					cur = grandparent;
					parent = cur->_parent;
				}
				else
				{
					//		 g
					//   u		p
					//				c
					if (cur == parent->_right)
					{
						rotateL(grandparent);
						parent->_col = BLACK;
						grandparent->_col = RED;
					}
					//		 g
					//   u		p
					//      c
					else if (cur == parent->_left)
					{
						rotateR(parent);
						rotateL(grandparent);
						grandparent->_col = RED;
						cur->_col = BLACK;
					}
				}
			}
			_root->_col = BLACK;
		}
		return make_pair(newnode, true);
	}

	void InOrderPrint()
	{
		_InOrder(_root);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
			return true;
		if (_root->_col == RED)
			return false;
		
		Node* cur = _root;
		int ref = 0;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				ref++;
			}
			cur = cur->_left;

		}
		int blacknum = 0;
		return _Check(_root, blacknum,ref);
	}

	/*Node* Find(const K& data)
	{
		Node* cur = _root;
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}*/

	int Height()
	{
		return _Height(_root);
	}

	int Size()
	{
		return _Size(_root);
	}

	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}
private:
	Node* _root = nullptr;

	bool _Check(Node* root,int blacknum, const int ref)
	{
		if (root == nullptr)
		{
			if (blacknum != ref)
				return false;
			return true;
		}
		if (root->_col == BLACK)
			blacknum++;
		if (root->_col == RED && root->_parent->_col == RED)
			return false;
		return _Check(root->_left, blacknum, ref)
			&& _Check(root->_right, blacknum, ref);
	}

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

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);
		return leftH > rightH ? leftH + 1 : rightH + 1;
	}

	int _Size(Node* root)
	{
		if (root == nullptr)
			return 0;
		return _Size(root->_left) + _Size(root->_right) + 1;
	}

	void _Destroy(Node* root)
	{
		if (root == nullptr)
			return;
		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

	void rotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (_root == parent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_right)
				parentParent->_right = subR;
			else if (parent == parentParent->_left)
				parentParent->_left = subR;
			subR->_parent = parentParent;
		}
	}

	void rotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentParent = parent->_parent;

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

		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;

		if (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subL;
			}
			else if (parentParent->_right == parent)
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}
	}
};

 Myset:

#pragma once
namespace test
{
	template<class K>
	class set
	{
		struct SetKeyOfValue
		{
			const K& operator()(const K& v)
			{
				return v;
			}
		};
	public:
		typedef typename RBTree<K, K, SetKeyOfValue>::const_iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfValue>::const_iterator const_iterator;


		iterator begin() const
		{
			return rb_tree.begin();
		}

		iterator end() const
		{
			return rb_tree.end();
		}

		pair<iterator,bool> insert(const K& data)
		{
			return rb_tree.Insert(data);
		}

	private:
		RBTree<K,K, SetKeyOfValue> rb_tree;
	};
}

 mymap:

#pragma once
#include "RBTree.h"
namespace test
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfValue
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfValue>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfValue>::const_iterator const_iterator;

		//typedef __RBTreeIterator<pair<const K,V>, pair<const K, V>&, pair<const K, V>*> iterator;

		iterator begin()
		{
			return rb_tree.begin();
		}

		iterator end()
		{
			return rb_tree.end();
		}

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

		V& operator[](const K& k)
		{
			return (*insert(make_pair(k, V())).first).second;
		}
	private:
		RBTree<K, pair<const K, V>,MapKeyOfValue> rb_tree;
	};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/153461.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

面向企业的人脸属性检测技术方案

人脸识别技术已经成为企业提升服务质量、优化用户体验的重要工具。美摄科技&#xff0c;作为领先的人工智能技术提供商&#xff0c;我们致力于为企业提供最先进、最全面的人脸属性检测技术解决方案。 我们的AI人脸检测与属性分析技术&#xff0c;能够快速准确地检测人脸并返回…

安全狗云安全体系为高校提升立体化纵深防御能力

客户情况 某高校有服务器500台&#xff0c;对外站点200个&#xff0c;核心交换流量20G。 客户痛点 校园网系统分类较多&#xff0c;并且每类网站中安全级重要程度又各不相同&#xff0c;同时有多个网络出口(如&#xff1a;教育网、电信网、移动网等)&#xff0c;二级学院存在…

物联网网关在工业行业的应用案例

物联网网关在工业行业的应用案例 随着物联网技术的不断发展&#xff0c;物联网网关在工业行业的应用越来越广泛。本文将介绍一个物联网网关在工业行业的应用案例&#xff0c;以期为相关领域的研究和实践提供借鉴和启示。 一、案例背景 某大型制造企业是一家全球知名的汽车制…

vscode中git拉取、提交代码、解决冲突,以及合并代码的操作

vscode中git拉取、提交代码、解决冲突&#xff0c;以及合并代码的操作 场景&#xff1a;本地有修改代码&#xff0c;远程仓库没有更新&#xff0c;这时本地想要提交代码。 步骤&#xff1a;本地修改了testA文件内容->本地先暂存提交->拉取->推送&#xff1b; 本地修改…

2023.11.14 hivesql的容器,数组与映射

目录 https://blog.csdn.net/m0_49956154/article/details/134365327?spm1001.2014.3001.5501https://blog.csdn.net/m0_49956154/article/details/134365327?spm1001.2014.3001.5501 8.hive的复杂类型 9.array类型: 又叫数组类型,存储同类型的单数据的集合 10.struct类型…

SpringNative遇到的问题

问题1 org.apache.catalina.LifecycleException: An invalid Lifecycle transition was attempted ([before_stop]) for component 用不了反射&#xff0c;所以需要这个文件去 package org.wxy.example.sqlite.config;import java.lang.reflect.Constructor; import java.lan…

【机器学习基础】多元线性回归(适合初学者的保姆级文章)

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;专栏&#xff1a;机器学习 欢迎订阅&#xff01;后面的内容会越来越有意思~ &#x1f4a1;往期推荐&#xff1a; 【机器学习基础】机器学习入门&#xff08;1&#xff09; 【机器学习基…

BI智能财务分析真的神,财务人都来用

不用等&#xff0c;真的不用等&#xff01;这边接入数据&#xff0c;那边就能把利润表、资产负债表、现金流量表等财务数据分析报表送到眼前&#xff0c;不用开发&#xff0c;直接就看分析结果。 奥威BI财务方案真能把我要的指标、分析都做出来&#xff1f; 能&#xff0c;可…

在win10环境下安装python,配置python环境,执行python脚本

1.安装python 去python官网下载&#xff1a; https://www.python.org/ 这里采用 Python 3.10.8 版本 选择windows 64位 双击安装&#xff1a; 安装这里有两个选项&#xff1a; 1.默认安装直接选Install Now 2.勾选install launcher for all users&#xff08;recommend&a…

Github小彩蛋显示自己的README,git 个人首页的 README,readme基本语法

先上效果&#x1f447; 代码在下面&#xff0c;流程我放最下面了&#xff0c;思路就是创建一个和自己同名的仓库&#xff0c;要公开&#xff0c;创建的时候会提示小彩蛋你的reademe会展示在你的首页&#xff0c;或许你在这个readme里面的修改都会在你的主页上看到了&#x1f44…

TEMU平台要求电子产品提供的UL测试报告如何办理?

平台销售的电子产品&#xff0c;要符合指定的标准&#xff0c;如果不合格很容易发生起火&#xff0c;等危及消费者生命财产的安全&#xff0c;因此很多客户因为缺少UL报告&#xff0c;导致产品被下架。 带电的产品上架亚马逊或相关的跨境电商平台都需要相关的UL报告/UL标准&…

vue-router配置

1、路由安装 npm install vue-router4 2、创建router目录 3、编辑文件且引入router包 4、main.js引入

申明式管理方式与配置清单文件

目录 申明式管理方式 1、使用申明式管理方式相关操作 1&#xff09;获取资源配置清单 2&#xff09;更改获取的yaml配置清单&#xff0c;并进行修改然后创建或更新资源 3&#xff09;在线修改或编辑资源配置 4&#xff09;删除资源 2、如何获取资源配置清单文件模板&…

spark性能调优 | 默认并行度

Spark Sql默认并行度 看官网&#xff0c;默认并行度200 https://spark.apache.org/docs/2.4.5/sql-performance-tuning.html#other-configuration-options 优化 在数仓中 task最好是cpu的两倍或者3倍(最好是倍数&#xff0c;不要使基数) 拓展 在本地 task需要自己设置&a…

客户管理系统升级,助力企业快速增长——API线索对接功能

在数字化时代&#xff0c;企业需要迅速适应不断变化的市场需求&#xff0c;实现高效的客户管理&#xff0c;以便迅速发现商机并提供更好的客户体验。为了助力企业取得成功&#xff0c;客户管理系统的API线索对接功能应运而生&#xff0c;带来更多机会、更高效率以及更全面的客户…

怎么把ogg转mp3格式?

音频声音小怎么增强&#xff1f;现在对于音频文件的使用越来越频繁&#xff0c;自媒体从业者会使用到音频素材&#xff0c;还有很多人会从网上下载很多的学习音频文件&#xff0c;有时候下载的音频文件播放之后会发现声音很小&#xff0c;此时大家会调大音频播放器的音量或者电…

JavaWeb-CSS

一、什么是CSS CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;能够对网页中元素的位置排版进行精确的控制&#xff0c;拥有对网页对象和模型样式的编辑能力&#xff0c;简单来说就是页面美化。 CSS样式代码中的注释需要使用/**/。 二、CSS的引入方…

Notion汉化

Notion真无语&#xff0c;汉化版都没有。真的无力吐槽。 2023.11.7汉化经历 教程链接&#xff1a;github Reamd7/notion-zh_CN at 2.4.20-handmade (github.com) 网页版&#xff1a; 油猴下载插件。 Notion中文汉化 浏览器插件下载 windows&#xff1a; github realse 这…

Latex如何消除并自定义算法标识

正常&#xff1a; 修改后&#xff1a; 正常代码&#xff1a; \documentclass{article} \usepackage[ruled]{algorithm2e} \begin{document} \begin{algorithm} \caption{Hi} My name is XXX. \end{algorithm} \end{document}修改后代码&#xff1a; \documentclass{articl…

C++运算符重载详解(日期类实操)

前言&#xff1a;为什么要实现运算符重载&#xff1f; 在C语言中&#xff0c;对于内置类型&#xff0c;我们可以根据符号>、<、等去直接比较大小&#xff0c;但是对于自定义来说&#xff0c;肯定不能直接比较大小&#xff0c;例如下面的日期类&#xff0c;想要比较两个两…