C++:基于红黑树封装map和set

news2024/12/22 2:37:01

目录

红黑树的修改

红黑树节点

红黑树结构

红黑树的迭代器

红黑树Insert函数

红黑树的默认成员函数

修改后完整的红黑树 

set、map的模拟实现

set

map

测试封装的set和map


红黑树的修改

想要用红黑树封装map和set,需要对之前实现的key-value红黑树进行修改,因为map是key-value结构而set是key结构之前实现的红黑树不能满足需求

我们需要将key和key-value抽象统一成成一个类型T,需要修改红黑树节点类红黑树类进行。

红黑树节点

enum Color
{
	RED,
	BLACK
};

//T代表set传过来的key或者map传过来的(pair)key-value
template<class T>
struct RBTreeNode
{
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Color _col;

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

红黑树结构

template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;

public:
    //...

private:
	Node* _root = nullptr;
};

3个模板参数的解释:

1.对于K代表的是key的类型,无论你是set和map,key的类型都是需要传入的,因为在Find函数的参数需要用到key的类型,如果是set,K和T都代表key的类型,第一个模板参数可有可没有,但是对于map来说,T代表的key-value类型(pair)没有第一个参数的话就无法拿到key的类型,从而无法实现Find函数。

2.对于T,代表的是红黑树里存的是key还是key-value

3.对于KeyOfT,这个参数其实是一个仿函数(对一个struct类重载  () ),这个仿函数是为了拿到T里面的key的具体值,因为Insert函数涉及到key的比较如果是map的话,T是key-value,拿不到具体的key值,就无法实现Insert函数,对于set的话,KeyOfT是可有可没有的。

红黑树的迭代器

红黑树的迭代器是对红黑树节点指针的封装,其实现与list的迭代器类似,但是由于红黑树的遍历走的是中序遍历,所以其迭代器的++走的是当前节点中序遍历的下一个节点,其--也是类似的道理。所以想要实现迭代器关键是实现++和--,其余部分与list的迭代器类似。

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

	Node* _node;
	Node* _root;

	RBTreeIterator(Node* node, Node* root)
		:_node(node)
		,_root(root)
	{}


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

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

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

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

};

实现红黑树中序遍历++

实现++的核心就是只关注局部逻辑而不看全局,只考虑中序遍历的下一个节点

迭代器it++时

  • 1.如果it指向的当前节点的右子树不为空,则去找当前节点的右子树的最左节点。
  • 2.如果it指向的当前节点的右子树为空,则去向上寻找一个特殊节点,该特殊节点是其父亲的左节点,此时特殊节点的父亲就是当前节点中序遍历的下一个节点

 上述逻辑是根据二叉树中序遍历总结而来。

	//前置++,返回++之后的值
	Self operator++()
	{
		// 当前节点的右子树不为空,中序的下一个节点是
		// 当前节点的右子树的最左(最小)节点
		if (_node->_right)
		{
			Node* rightMin = _node->_right;
			while (rightMin->_left)
				rightMin = rightMin->_left;

			_node = rightMin;
		}
		//当前节点的右子树为空,说明当前节点所属的子树已经中序遍历访问完毕
		//往上寻找中序遍历的下一个节点,找到一个节点是父亲的左节点的特殊节点
		//此时特殊节点的父亲就是当前节点中序遍历的下一个节点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	//后置++
	Self operator++(int)
	{
		Self tmp = *this;
		++(*this);
		return tmp;
	}

由于有关迭代器的函数 begin() 和 end() 是左闭右开区间,这里我们就用nullptr作为 end()这里的++逻辑也是可以兼顾到 end()。

  • 假设当前节点是中序遍历的最后一个节点,也就是整棵树的最右节点,其右子树为空,向上寻找的过程中,找不出满足条件的特殊节点(根节点的父亲是nullptr),parent为空时退出循环,给_node赋值,还是找到了当前节点中序遍历的下一个节点。

实现红黑树中序遍历--

这与++的逻辑是相反的,也可以当成反中序遍历右根左的++。

//前置--
Self operator--()
{
	//处理end()的情况
	//--end()得到的应该是中序遍历的最后一个节点
	//整一棵树的最右节点
	if (_node == nullptr)
	{
		Node* rightMost = _root;
		while (rightMost && rightMost->_right)
			rightMost = rightMost->_right;

		_node = rightMost;
	}
	//当前节点的左子树不为空,找左子树的最右节点
	else if (_node->_left)
	{
		Node* leftMost = _node->_left;
		while (leftMost && leftMost->_right)
			leftMost = leftMost->_right;

		_node = leftMost;
	}
	//当前节点的左子树为空
	else
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_left)
		{
			cur = parent;
			parent = cur->_parent;
		}

		_node = parent;
	}

	return *this;
}

//后置--
Self operator--(int)
{
	Self tmp = *this;
	--(*this);
	return tmp;
}

实现迭代器的完整代码

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

	Node* _node;
	Node* _root;

	RBTreeIterator(Node* node, Node* root)
		:_node(node)
		,_root(root)
	{}


	//前置++,返回++之后的值
	Self operator++()
	{
		// 当前节点的右子树不为空,中序的下一个节点是
		// 当前节点的右子树的最左(最小)节点
		if (_node->_right)
		{
			Node* rightMin = _node->_right;
			while (rightMin->_left)
				rightMin = rightMin->_left;

			_node = rightMin;
		}
		//当前节点的右子树为空,说明当前节点所属的子树已经中序遍历访问完毕
		//往上寻找中序遍历的下一个节点,找到一个节点是父亲的左节点的特殊节点
		//此时特殊节点的父亲就是当前节点中序遍历的下一个节点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	//后置++
	Self operator++(int)
	{
		Self tmp = *this;
		++(*this);
		return tmp;
	}

	//前置--
	Self operator--()
	{
		//处理end()的情况
		//--end()得到的应该是中序遍历的最后一个节点
		//整一棵树的最右节点
		if (_node == nullptr)
		{
			Node* rightMost = _root;
			while (rightMost && rightMost->_right)
				rightMost = rightMost->_right;

			_node = rightMost;
		}
		//当前节点的左子树不为空,找左子树的最右节点
		else if (_node->_left)
		{
			Node* leftMost = _node->_left;
			while (leftMost && leftMost->_right)
				leftMost = leftMost->_right;

			_node = leftMost;
		}
		//当前节点的左子树为空
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	//后置--
	Self operator--(int)
	{
		Self tmp = *this;
		--(*this);
		return tmp;
	}

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

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

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

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

};

红黑树Insert函数

对Insert函数的修改,主要修改其返回值和key的比较逻辑返回值应改为pair<Iterator, bool>key的比较逻辑用KeyOfT实例化出来的对象比较即可

pair<Iterator, bool> Insert(const T& data)
{
	//按二叉搜索树插入
	if (_root == nullptr)
	{
		_root = new Node(data);
		//根节点为黑色
		_root->_col = BLACK;
		//return pair<Iterator, bool>(Iterator(_root, _root), true);
		return { Iterator(_root, _root), true };
	}

	//仿函数,获取T中的具体的key
	KeyOfT kot;
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (kot(cur->_data) < kot(data))
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (kot(cur->_data) > kot(data))
		{
			parent = cur;
			cur = cur->_left;
		}
		else
			return { Iterator(cur, _root), false };
	}

	cur = new Node(data);
	Node* newnode = cur;
	//非空树插入红色节点
	cur->_col = RED;

	//判断cur应该插入到parent的左节点还是右节点
	if (kot(parent->_data) < kot(data))
		parent->_right = cur;
	else
		parent->_left = cur;

	//链接父亲
	cur->_parent = parent;

	//父亲是红色节点,出现连续的红色节点,要处理
	while (parent && parent->_col == RED)
	{
		Node* grandfather = parent->_parent;

		//判断叔叔是grandfather的左节点还是右节点
		if (parent == grandfather->_left)
		{
			Node* uncle = grandfather->_right;

			//uncle存在且为红
			if (uncle && uncle->_col == RED)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				//继续向上更新颜色
				cur = grandfather;
				parent = cur->_parent;
			}
			else  //uncle不存在 或者 uncle存在且为黑
			{
				if (cur == parent->_left)
				{
					//     g
					//   p    u
					// c
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//     g
					//   p    u
					//     c
					RotateL(parent);
					RotateR(grandfather);

					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
		else if (parent == grandfather->_right)
		{
			Node* uncle = grandfather->_left;

			//uncle存在且为红
			if (uncle && uncle->_col == RED)
			{
				//变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				//继续向上更新颜色
				cur = grandfather;
				parent = cur->_parent;
			}
			else //uncle不存在 或者 uncle存在且为黑
			{
				if (cur == parent->_right)
				{
					//     g
					//   u   p
					//        c
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//     g
					//   u    p 
					//      c
					RotateR(parent);
					RotateL(grandfather);

					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
	}
	//更新颜色时,_root的颜色可能会改变
	//当grandfather是_root时
	//    g        更新颜色时,parent和uncle会变黑
	//  p   u      grandfather会变红
		// c
	//所以必须加这句代码保证_root的颜色为黑。
	_root->_col = BLACK;
	return { Iterator(newnode, _root), true };
}

红黑树的默认成员函数

主要是补充之前实现红黑树时没有写的拷贝构造函数、赋值重载函数和析构函数。

​
    //默认构造
	RBTree() = default;

	//拷贝构造
	RBTree(const RBTree<K, T, KeyOfT>& t)
	{
		_root = Copy(t._root);
	}

	//赋值重载
	RBTree<K, T, KeyOfT>& operator=(const RBTree<K, T, KeyOfT> t)
	{
		std::swap(_root, t._root);
		return *this;
	}

	//析构
	~RBTree()
	{
		Destroy(_root);
		_root = nullptr;
	}


    Node* Copy(Node* root)
    {
	    if (root == nullptr)
		    return nullptr;

	    //前序遍历复制
	    Node* newnode = new Node(root->_data);
	    newnode->_col = root->_col;

	    newnode->_left = Copy(root->_left);
	    if (root->_left)
		    root->_left->_parent = newnode;


	    newnode->_right = Copy(root->_right);
	    if (root->_right)
		    root->_right->_parent = newnode;

        return newnode;
    }

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

	    delete root;
	    root = nullptr;
    }

​

修改后完整的红黑树 

​
enum Color
{
	RED,
	BLACK
};

//T代表set传过来的key或者map传过来的(pair)key-value
template<class T>
struct RBTreeNode
{
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Color _col;

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

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

	Node* _node;
	Node* _root;

	RBTreeIterator(Node* node, Node* root)
		:_node(node)
		,_root(root)
	{}


	//前置++,返回++之后的值
	Self operator++()
	{
		// 当前节点的右子树不为空,中序的下一个节点是
		// 当前节点的右子树的最左(最小)节点
		if (_node->_right)
		{
			Node* rightMin = _node->_right;
			while (rightMin->_left)
				rightMin = rightMin->_left;

			_node = rightMin;
		}
		//当前节点的右子树为空,说明当前节点所属的子树已经中序遍历访问完毕
		//往上寻找中序遍历的下一个节点,找到一个节点是父亲的左节点的特殊节点
		//此时特殊节点的父亲就是当前节点中序遍历的下一个节点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	//后置++
	Self operator++(int)
	{
		Self tmp = *this;
		++(*this);
		return tmp;
	}

	//前置--
	Self operator--()
	{
		//处理end()的情况
		//--end()得到的应该是中序遍历的最后一个节点
		//整一棵树的最右节点
		if (_node == nullptr)
		{
			Node* rightMost = _root;
			while (rightMost && rightMost->_right)
				rightMost = rightMost->_right;

			_node = rightMost;
		}
		//当前节点的左子树不为空,找左子树的最右节点
		else if (_node->_left)
		{
			Node* leftMost = _node->_left;
			while (leftMost && leftMost->_right)
				leftMost = leftMost->_right;

			_node = leftMost;
		}
		//当前节点的左子树为空
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	//后置--
	Self operator--(int)
	{
		Self tmp = *this;
		--(*this);
		return tmp;
	}

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

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

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

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

};

template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;

public:

	typedef RBTreeIterator<T, T&, T*> Iterator;
	typedef RBTreeIterator<T, const T&, const T*> Const_Iterator;

	//默认构造
	RBTree() = default;

	//拷贝构造
	RBTree(const RBTree<K, T, KeyOfT>& t)
	{
		_root = Copy(t._root);
	}

	//赋值重载
	RBTree<K, T, KeyOfT>& operator=(const RBTree<K, T, KeyOfT> t)
	{
		std::swap(_root, t._root);
		return *this;
	}

	//析构
	~RBTree()
	{
		Destroy(_root);
		_root = nullptr;
	}

	//中序遍历的第一个节点是整棵树的最左节点
	Iterator begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
			cur = cur->_left;
		return Iterator(cur, _root);
	}

	Iterator end()
	{
		return Iterator(nullptr, _root);
	}

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

	Const_Iterator end() const
	{
		return Const_Iterator(nullptr, _root);
	}

	void RotateR(Node* parent)
	{
		//subL为parent的左孩子节点
		Node* subL = parent->_left;
		//subLR为subL的右子节点
		Node* subLR = subL->_right;

		// 将parent与subLR节点进行链接
		parent->_left = subLR;
		//在SubLR的情况下更改,让其指向正确的父亲
		if (subLR)
			subLR->_parent = parent;

		//提前记录祖父节点
		Node* pParent = parent->_parent;

		//链接subL与parent
		subL->_right = parent;
		parent->_parent = subL;

		//根据parent是否是根节点进行不同处理
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			//将pParent和subL链接
			//但得先判断parent是pParent的左节点还是右节点
			if (pParent->_left == parent)
				pParent->_left = subL;
			else
				pParent->_right = subL;

			//修改subL的parent指针,让其指向正确的父亲
			subL->_parent = pParent;
		}

	}

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

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

		Node* pParent = parent->_parent;

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

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

			subR->_parent = pParent;
		}

	}


	pair<Iterator, bool> Insert(const T& data)
	{
		//按二叉搜索树插入
		if (_root == nullptr)
		{
			_root = new Node(data);
			//根节点为黑色
			_root->_col = BLACK;
			//return pair<Iterator, bool>(Iterator(_root, _root), true);
			return { Iterator(_root, _root), true };
		}

		//仿函数,获取T中的具体的key
		KeyOfT kot;
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
				return { Iterator(cur, _root), false };
		}

		cur = new Node(data);
		Node* newnode = cur;
		//非空树插入红色节点
		cur->_col = RED;

		//判断cur应该插入到parent的左节点还是右节点
		if (kot(parent->_data) < kot(data))
			parent->_right = cur;
		else
			parent->_left = cur;

		//链接父亲
		cur->_parent = parent;

		//父亲是红色节点,出现连续的红色节点,要处理
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;

			//判断叔叔是grandfather的左节点还是右节点
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;

				//uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续向上更新颜色
					cur = grandfather;
					parent = cur->_parent;
				}
				else  //uncle不存在 或者 uncle存在且为黑
				{
					if (cur == parent->_left)
					{
						//     g
						//   p    u
						// c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p    u
						//     c
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else if (parent == grandfather->_right)
			{
				Node* uncle = grandfather->_left;

				//uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					//变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续向上更新颜色
					cur = grandfather;
					parent = cur->_parent;
				}
				else //uncle不存在 或者 uncle存在且为黑
				{
					if (cur == parent->_right)
					{
						//     g
						//   u   p
						//        c
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   u    p 
						//      c
						RotateR(parent);
						RotateL(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}
		//更新颜色时,_root的颜色可能会改变
		//当grandfather是_root时
		//    g        更新颜色时,parent和uncle会变黑
		//  p   u      grandfather会变红
 		// c
		//所以必须加这句代码保证_root的颜色为黑。
		_root->_col = BLACK;
		return { Iterator(newnode, _root), true };
	}

	Iterator Find(const K& key)
    {
	    Node* cur = _root;
	    KeyOfT kot;

	    while (cur)
	    {
		    if (key > kot(cur->_data))
			    cur = cur->_right;
		    else if (key < kot(cur->_data))
			    cur = cur->_left;
		    else
			    return Iterator(cur, _root);
	    }

	    return Iterator(nullptr, _root);
    }

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

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

	
private:
	int _Height(Node* root)
	{
		if (root == nullptr) return 0;

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	int _Size(Node* root)
	{
		if (root == nullptr) return 0;

		return _Size(root->_left) + _Size(root->_right) + 1;
	}

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		//前序遍历复制
		Node* newnode = new Node(root->_data);
		newnode->_col = root->_col;

		newnode->_left = Copy(root->_left);
		if (root->_left)
			root->_left->_parent = newnode;


		newnode->_right = Copy(root->_right);
		if (root->_right)
			root->_right->_parent = newnode;
	}

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

		delete root;
		root = nullptr;
	}

private:
	Node* _root = nullptr;
};

​

set、map的模拟实现

对红黑树进行修改后,只需对其接口函数进行封装和实现KeyOfT仿函数即可。

set

template<class K>
class set
{
	//实现key
	struct SetKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};

public:

	typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
	typedef typename RBTree<K, const K, SetKeyOfT>::Const_Iterator const_iterator;

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

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

	const_iterator begin() const 
	{
		return _t.begin();
	}

	const_iterator end() const
	{
		return _t.end();
	}

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

	iterator Find(const K& key)
	{
		return _t.Find(key);
	}

private:

	//加const令其不能修改key
	RBTree<K, const K, SetKeyOfT> _t;

};

map

class map
{
	struct MapKeyOfT
	{
		const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};
public:
	typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
	typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Const_Iterator const_iterator;

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

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

	const_iterator begin() const
	{
		return _t.begin();
	}

	const_iterator end() const
	{
		return _t.end();
	}

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

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

	iterator Find(const K& key)
	{
		return _t.Find(key);
	}

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

测试封装的set和map

void test_myset1()
{
	zh::set<int> s;
	s.insert(3);
	s.insert(1);
	s.insert(5);
	s.insert(4);
	s.insert(6);

	auto it = s.Find(3);
	cout << *it << endl;
	it++;
	cout << *it << endl;

	auto it = s.begin();

	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

void test_Mymap1()
{
	zh::map<string, string> dict;
	dict.insert({ "sort", "排序" });
	dict.insert({ "left", "左边" });
	dict.insert({ "right", "右边" });

	auto it = dict.Find("sort");
	cout << it->first << ":" << it->second << endl;
	cout << endl;

	it = dict.begin();
	while (it != dict.end())
	{
		//it->first += 'x';

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

	it = dict.end();
	it--;
	while (it != dict.begin())
	{
		cout << it->first << ":" << it->second << endl;
		it--;
	}
	cout << endl;

	dict["left"] = "左边,剩余";
	dict["insert"] = "插入";
	dict["string"];

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


 拜拜,下期再见😏

摸鱼ing😴✨🎞

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习基础 | 预备知识】数据预处理

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…

前端面试笔试(四)

目录 一、数据结构算法等综合篇 1.线性探查法解决哈希冲突 2.请求分页系统中文件区和对换区 3.RADIUS认证协议&#xff0c;运行在哪个网络协议上 二、代码输出篇 1.res[1,2,100].map(parseInt) 如果我们想要输出为[1,2,100]&#xff0c;可以&#xff1a; 还可以换map里…

从零开始学习 sg200x 多核开发之 milkv-duo256 编译运行 sophpi

sophpi 是 算能官方针对 sg200x 系列的 SDK 仓库 https://github.com/sophgo/sophpi &#xff0c;支持 cv180x、cv81x、sg200x 系列的芯片。 SG2002 简介 SG2002 是面向边缘智能监控 IP 摄像机、智能猫眼门锁、可视门铃、居家智能等多项产品领域而推出的高性能、低功耗芯片&a…

【客户服务】互联网时代客户投诉处理金点子

互联网时代客户投诉新特点 客户投诉渠道广投诉的内容涉及到企业的各个方面客户维权意识越来越强负面效应很难管 卓越客户体验成为企业核心竞争力 移动互联网与社会化媒体背景下&#xff0c;客户的全方位感知、情感、卓越体验、高效需求成为驱动技术、应用、终端以及服务持续…

SQL 审核在 CloudQuery 的四大场景应用

数据库作为数据的核心载体&#xff0c;其安全性和稳定性对业务的影响至关重要。而在我们日常业务中&#xff0c;SQL 编写不当是引起数据库故障的一个重要原因&#xff0c;轻则影响数据库性能&#xff0c;重则可能直接导致「雪崩」。因此&#xff0c;SQL 审核作为 SQL 代码投入生…

【前端知识】Javascript前端框架Vue入门

前端框架VUE入门 概述基础语法介绍组件特性组件注册Props 属性声明事件组件 v-model(双向绑定)插槽Slots内容与出口 组件生命周期完整示例1. 创建 Vue 项目&#xff08;如果还没有&#xff09;2. 定义和使用组件3. 在主应用中使用组件4. 运行应用完整项目结构 参考文献 概述 V…

Jmeter基础篇(24)Jmeter目录下有哪些文件夹是可以删除,且不影响使用的呢?

一、前言 Jmeter使我们日常做性能测试最常用的工具之一啦&#xff01;但是我们在和其他同学协同工作的时候&#xff0c;偶尔也会遇到一些问题&#xff0c;例如我想要给别人发送一个Jmeter工具包&#xff0c;但这个文件包往往会很大&#xff0c;比较浪费流量和空间&#xff0c;…

Python_爬虫3_Requests库网络爬虫实战(5个实例)

目录 实例1&#xff1a;京东商品页面的爬取 实例2&#xff1a;亚马逊商品页面的爬取 实例3&#xff1a;百度360搜索关键词提交 实例4&#xff1a;网络图片的爬取和存储 实例5&#xff1a;IP地址归地的自动查询 实例1&#xff1a;京东商品页面的爬取 import requests url …

StructuredStreaming (一)

一、sparkStreaming的不足 1.基于微批,延迟高不能做到真正的实时 2.DStream基于RDD,不直接支持SQL 3.流批处理的API应用层不统一,(流用的DStream-底层是RDD,批用的DF/DS/RDD) 4.不支持EventTime事件时间&#xff08;一般流处理都会有两个时间&#xff1a;事件发生的事件&am…

计算机网络HTTP——针对实习面试

目录 计算机网络HTTP什么是HTTP&#xff1f;HTTP和HTTPS有什么区别&#xff1f;分别说明HTTP/1.0、HTTP/2.0、HTTP/3.0请说明访问网页的全过程请说明HTTP常见的状态码Cookie和Session有什么区别&#xff1f;HTTP请求方式有哪些&#xff1f;请解释GET和POST的区别&#xff1f;HT…

转轮数组(C语言实现)

题目介绍 方法一我们可以先把数字1 2 3 4逆转一下&#xff0c;第二步我们可以逆转一下5 6 7&#xff0c; 最后整体逆置一下就会变成上面的数字。 void reverse(int* nums, int begin, int end) {while (begin < end){int tmp nums[begin];nums[begin] nums[end];nums[en…

Ubuntu 的 ROS 操作系统 turtlebot3 gazebo仿真

引言 TurtleBot3 Gazebo仿真环境是一个非常强大的工具&#xff0c;能够帮助开发者在虚拟环境中测试和验证机器人算法。 Gazebo是一个开源的3D机器人仿真平台&#xff0c;它能支持物理引擎&#xff0c;允许机器人在虚拟环境中模拟和测试。结合ROS&#xff0c;它能提供一个完整的…

uniapp vuex的使用

实现组件全局&#xff08;数据&#xff09;管理的一种机制&#xff0c;可以方便的实现组件之间共享数据&#xff0c;不同于上述三种传递值的方式。 可以把vuex当成一个store仓库&#xff0c;可以集中管理共享的数据&#xff0c;并且存储在vuex中的数据都是响应式的&#xff0c…

uniapp适配暗黑模式配置plus.nativeUI.setUIStyle适配DarkMode配置

uniapp适配暗黑模式配置 目录 uniapp适配暗黑模式配置setUIStyleDarkMode 适配app-plus manifest.json配置theme.json配置pages.json配置页面切换代码实现同步手机暗黑配置额外适配 参考官方文档&#xff1a;https://uniapp.dcloud.net.cn/tutorial/darkmode.html 主要用到api…

element ui table进行相同数据合并单元格

示例如图 //要合并的项&#xff08;自定义&#xff09; const columnArr ["dq","sj","xj","zj","zjj","zjfzr","nhxm","nhsjh","nhsfzh","","",""…

uniapp 实现 ble蓝牙同时连接多台蓝牙设备,支持app、苹果(ios)和安卓手机,以及ios连接蓝牙后的一些坑

首先对 uniapp BLE蓝牙API进行封装 这里我封装了一个类&#xff1a;bluetoothService.js 代码&#xff1a; import { throttle } from lodash export default class Bluetooth {constructor() {this.device {};this.connected false;// 使用箭头函数绑定类实例的上下文&am…

51单片机应用开发(进阶)---模块化编程

实现目标 1、掌握.h 文件的格式、extern 的用法&#xff1b; 2、握模块化编程方法步骤&#xff1b; 3、具体实现&#xff1a;&#xff08;1&#xff09;提供一个C文件&#xff0c;将其按照功能模块进行模块化。 一、为什么要进行模块化编程&#xff1f; 传统的编程方式&…

arkUI:水果选择与管理:基于 ArkUI 的长按编辑功能实现

水果选择与管理&#xff1a;基于 ArkUI 的长按编辑功能实现 1 主要内容说明2 相关内容2.1 相关内容2.1.1 源码1内容的相关说明2.1.1.1 数据结构与状态管理2.1.1.2 添加水果功能2.1.1.3 水果列表展示2.1.1.4 长按进入编辑模式2.1.1.5 复选框的多选功能2.1.1.6 删除水果功能2.1.1…

小程序20-样式:自适应尺寸单位 rpx

手机设备的宽度逐渐多元化&#xff0c;也就需要开发者开发过程中&#xff0c;去适配不同屏幕宽度的手机&#xff0c;为了解决屏幕适配问题&#xff0c;微信小程序推出了 rpx 单位 rpx&#xff1a;小程序新增的自适应单位&#xff0c;可以根据不同设备的屏幕宽度进行自适应缩放 …

unity3d————Resources异步加载

知识点一&#xff1a;Resources异步加载是什么&#xff1f; 在Unity中&#xff0c;资源加载可以分为同步加载和异步加载两种方式。同步加载会在主线程中直接进行&#xff0c;如果加载的资源过大&#xff0c;可能会导致程序卡顿&#xff0c;因为从硬盘读取数据到内存并进行处理…