【C++红黑树应用】模拟实现STL中的map与set

目录

  • [🚀 前言](#🚀 前言)
  • [一: 🔥 红黑树的修改](#一: 🔥 红黑树的修改)
  • [二: 🔥 红黑树的迭代器](#二: 🔥 红黑树的迭代器)
  • [三: 🔥 perator++() 与 operator--()](#三: 🔥 perator++() 与 operator--())
  • [四: 🔥 红黑树相关接口的改造](#四: 🔥 红黑树相关接口的改造)
    • [✨ 4.1 Find 函数的改造](#✨ 4.1 Find 函数的改造)
    • [✨ 4.2 Insert 函数的改造](#✨ 4.2 Insert 函数的改造)
  • [五:🔥 Set 和 map的模拟实现](#五:🔥 Set 和 map的模拟实现)
    • [✨ 5.1 Set的基本设计](#✨ 5.1 Set的基本设计)
    • [✨ 5.2 Map的基本设计](#✨ 5.2 Map的基本设计)
  • [六:📖 红黑树改造的完整代码及总结](#六:📖 红黑树改造的完整代码及总结)

🚀 前言

红黑树类的实现参考上一篇文章:【C++高阶数据结构】红黑树:全面剖析与深度学习
之前我们已经学习了如何手搓一棵红黑树,现在让我们来对红黑树进行改造,并且封装成map和set.

我们通过观察其stl源码切入进行仿写:

我们发现无论是map还是set都复用的一个红黑树,模板参数都是k,v模型。通过源码我们可以发现:set -> rb_tree<k, k> | | map<k, v> -> rb_tree<k, pair<const k, v>>。所以节点中存什么内容是由v决定的,不是k决定的。将红黑树写成泛型,所以我们必须将之前写的红黑树的模板进行修改!

一: 🔥 红黑树的修改

【改造前】

cpp 复制代码
template<class K, class T>
class RBTree
{
	typedef RBTreeNode<T> Node; // 红黑树节点
public:
	RBTree() :_root(nullptr) {}  // 构造函数
    bool Insert(const T& data);  // 插入节点
    // ...
private:
	Node* _root;
};

红黑树的插入节点接口中要先通过比较节点中数据的大小来查找适合插入的位置,但是红黑树并不知道数据 data 到底是 key 还是 pair。如果数据 data 是 key,那么直接取 key 来作比较;如果数据 data 是 pair,则需要取 first 来作比较。

  • 所以我们就得使用仿函数进行操作 ,我们创建keyoft仿函数取出T对象中的key即可

【改造后】

红黑树的定义

  • K:键值key的类型。
  • T:数据的类型,如果是 map,则为 pair<const K, V>;如果是 set,则为 K。
  • KeyOfT:通过 T 的类型来获取 key 值的一个仿函数类。
cpp 复制代码
template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node; // 红黑树节点
public:
	RBTree() :_root(nullptr) {}  // 构造函数
    bool Insert(const T& data);  // 插入节点(接口返回值目前是bool,后续要改为pair)
    // ...
private:
	Node* _root;
};

二: 🔥 红黑树的迭代器

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代

器,需要考虑以前问题:begin()与end()

STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,

可以得到一个有序的序列。

SGI-STL源码中,红黑树有一个哨兵位的头节点,begin() 是放在红黑树中最小节点(即最左侧节点)的位置,end() 是放在 end() 放在头结点的位置。

本文所采取的方式并没有使用头节点,而是通过其他方法实现,使用空指针代表end()

cpp 复制代码
template<class K, class T, class KeyOfT>
struct RBTree
{
	typedef RBTreeNode<T> Node; // 红黑树节点
public:
	typedef _RBTreeIterator<T, T&, T*> iterator; // 迭代器
    
    iterator begin() // begin(): 指向红黑树的最左节点的迭代器
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return iterator(left);
        // 注意:单参数的构造函数支持隐式类型转换,节点会被构造成迭代器
        // 所以也可以这样写:return left;
	}
 
	iterator end() // end(): 指向nullptr的迭代器
	{
		return iterator(nullptr);
	}
private:
	Node* _root = nullptr;
};

三: 🔥 perator++() 与 operator--()

  1. operator++()
  • 右不为空,右子树的最左节点
  • 右为空,沿着到根的路径找孩子是父亲左的那个祖先
  1. operator--()
  • 左不为空,左子树的最右节点
  • 左为空,沿着到根的路径找孩子是父亲右的那个祖先
cpp 复制代码
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* leftMost = _node->_right;
			while (leftMost->_left)
			{
				leftMost = leftMost->_left;
			}

			_node = leftMost;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node == nullptr)            // end()情况特殊处理
		{
			// end()--  走到最右节点
			Node* rightMost = _root;
			while (rightMost && rightMost->_right)
			{
				rightMost = rightMost->_right;
			}

			_node = rightMost;
		}
		else if (_node->_left)
		{
			// 右不为空,右子树最左节点就是中序下一个
			Node* rightMost = _node->_left;
			while (rightMost->_right)
			{
				rightMost = rightMost->_right;
			}

			_node = rightMost;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}
};
}

四: 🔥 红黑树相关接口的改造

✨ 4.1 Find 函数的改造

查找成功,返回查找到的那个节点的迭代器,查找失败,就返回 nullptr。

cpp 复制代码
Iterator Find(const K& key)
{
	KeyOfT kot;              // 仿函数类的对象
	Node* cur = _root;
	while (cur)
	{
		if (kot(cur->_data) < key)
		{
			cur = cur->_right;
		}
		else if (kot(cur->_data) > key)
		{
			cur = cur->_left;
		}
		else
		{
			return Iterator(cur, _root);
		}
	}

	return End();
}

✨ 4.2 Insert 函数的改造

注意:map 里的 operator[] 需要依赖 Insert 的返回值

cpp 复制代码
pair<Iterator, bool> Insert(const T& data)
{
	if (_root == nullptr) {
		_root = new Node(data);
		_root->_col = BLACK;
		return make_pair(Iterator(_root, _root), true);
	}

	KeyOfT kot;        // 仿函数
	// 找到插入位置
	Node* cur = _root, * parent = nullptr;
	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 make_pair(Iterator(cur, _root), false);
		}
	}
	cur = new Node(data);
	Node* newnode = cur;
	//	新增节点 颜色优先选择红色
	cur->_col = RED;
	if (kot(data) > kot(parent->_data)) parent->_right = cur;
	else parent->_left = cur;

	cur->_parent = parent;

	// 1、parent不存在,cur就是根了,出去后把根处理成黑的
	// 2、parent存在,且为黑
	// 3、parent存在,且为红,继续循环处理
	// 变色了之后持续网上处理
	while (parent && parent->_col == RED)          // 父亲颜色是红色就需要继续处理(来连续的红节点, 关键看叔叔)
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)               // 父亲在爷爷的左边 右边就是对称的
		{
			Node* uncle = grandfather->_right;
			//    g
			//  p   u
			if (uncle && uncle->_col == RED)     // 如果叔叔存在且为红色
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				cur = grandfather;
				parent = grandfather->_parent;
			}
			else {                               // 叔叔存在且为黑或者不存在  那么旋转+变色
				//    g
				//  p   u
				// c
				// 单旋
				if (cur == parent->_left)
				{
					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 {
			//    g
			//  u   p
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)     // 如果叔叔存在且为红色
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				cur = grandfather;
				parent = grandfather->_parent;
			}
			else {                               // 叔叔存在且为黑或者不存在  那么旋转+变色
				//    g
				//  u   p
				//        c
				// 单旋
				if (cur == parent->_right)
				{
					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->_col = BLACK;

	return make_pair(Iterator(newnode, _root), true);
}

五:🔥 Set 和 map的模拟实现

✨ 5.1 Set的基本设计

set的底层为红黑树,因此只需在set内部封装一棵红黑树,即可将该容器实现出来。

为了解决 set 中 key 值不能修改的问题,在传给 RBTree 的第二个模板参数前加 const 即可。

cpp 复制代码
#pragma once

#include"RBTree.h"

namespace bit
{
	template<class K>
	class set
	{   
		// 取出 keyofvalue 的仿函数类
		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>::ConstIterator 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:
		RBTree<K, const K, SetKeyOfT> _t;
	};

	void Print(const set<int>& s)
	{
		set<int>::const_iterator it = s.end();
		while (it != s.begin())
		{
			--it;
			//*it += 2;
			cout << *it << " ";
		}
		cout << endl;
	}

	void test_set()
	{
		bit::set<int> s;
		int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
		//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		for (auto e : a)
		{
			s.insert(e);
		}

		for (auto e : s)
		{
			cout << e << ' ';
		}

		set<int>::iterator it = s.end();
		while (it != s.begin())
		{
			--it;
			cout << *it << " ";
		}
		cout << '\n';
	}
}

✨ 5.2 Map的基本设计

cpp 复制代码
#pragma once

#include"RBTree.h"

namespace bit
{
	template<class K, class V>
	class map
	{
		// 取出 keyofvalue 的仿函数类
		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>::ConstIterator 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);
		}

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

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


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

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

		map<string, string>::iterator it = dict.begin();
		while (it != dict.end())
		{
			// 不能修改first,可以修改second
			//it->first += 'x';
			it->second += 'x';

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

}

六:📖 红黑树改造的完整代码及总结

cpp 复制代码
#pragma once

#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <map>
#include <assert.h>

using namespace std;

enum Color
{
	RED,
	BLACK
};

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* leftMost = _node->_right;
			while (leftMost->_left)
			{
				leftMost = leftMost->_left;
			}

			_node = leftMost;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node == nullptr)            // end()情况特殊处理
		{
			// end()--  走到最右节点
			Node* rightMost = _root;
			while (rightMost && rightMost->_right)
			{
				rightMost = rightMost->_right;
			}

			_node = rightMost;
		}
		else if (_node->_left)
		{
			// 右不为空,右子树最左节点就是中序下一个
			Node* rightMost = _node->_left;
			while (rightMost->_right)
			{
				rightMost = rightMost->_right;
			}

			_node = rightMost;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

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

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

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


// T可以是key 也可以是map 三个参数中第一个key是给find和 erase的   pair和第二个key是给insert的
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*> ConstIterator;

	Iterator Begin()
	{
		Node* leftMost = _root;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}

		return Iterator(leftMost, _root);
	}

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

	ConstIterator Begin() const
	{
		Node* leftMost = _root;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}

		return ConstIterator(leftMost, _root);
	}

	ConstIterator End() const
	{
		return ConstIterator(nullptr, _root);
	}

	RBTree() = default;

	RBTree(const RBTree<K, T, KeyOfT>& t)
	{
		_root = Copy(t._root);
	}

	RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~RBTree()
	{
		Destroy(_root);
		_root = nullptr;
	}


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

		KeyOfT kot;        // 仿函数
		// 找到插入位置
		Node* cur = _root, * parent = nullptr;
		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 make_pair(Iterator(cur, _root), false);
			}
		}
		cur = new Node(data);
		Node* newnode = cur;
		//	新增节点 颜色优先选择红色
		cur->_col = RED;
		if (kot(data) > kot(parent->_data)) parent->_right = cur;
		else parent->_left = cur;

		cur->_parent = parent;

		// 1、parent不存在,cur就是根了,出去后把根处理成黑的
		// 2、parent存在,且为黑
		// 3、parent存在,且为红,继续循环处理
		// 变色了之后持续网上处理
		while (parent && parent->_col == RED)          // 父亲颜色是红色就需要继续处理(来连续的红节点, 关键看叔叔)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)               // 父亲在爷爷的左边 右边就是对称的
			{
				Node* uncle = grandfather->_right;
				//    g
				//  p   u
				if (uncle && uncle->_col == RED)     // 如果叔叔存在且为红色
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = grandfather->_parent;
				}
				else {                               // 叔叔存在且为黑或者不存在  那么旋转+变色
					//    g
					//  p   u
					// c
					// 单旋
					if (cur == parent->_left)
					{
						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 {
				//    g
				//  u   p
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)     // 如果叔叔存在且为红色
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = grandfather->_parent;
				}
				else {                               // 叔叔存在且为黑或者不存在  那么旋转+变色
					//    g
					//  u   p
					//        c
					// 单旋
					if (cur == parent->_right)
					{
						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->_col = BLACK;

		return make_pair(Iterator(newnode, _root), true);
	}

	Iterator Find(const K& key)
	{
		KeyOfT kot;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return Iterator(cur, _root);
			}
		}

		return End();
	}

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

			Node* newRoot = new Node(root->_kv);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);

			return newRoot;
		}

	void Destroy(Node * root)
		{
			if (root == nullptr)
				return;

			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
		}

	void InOrder()
	{
		_InOrder(_root);
	}

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

	// 检查是否是红黑树
	bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		if (_root->_col == RED)
		{
			return false;
		}

		// 参考值
		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++refNum;
			}

			cur = cur->_left;
		}

		return Check(_root, 0, refNum);
	}

private:
	bool Check(Node* root, int blackNum, const int refNum)
	{
		if (root == nullptr)
		{
			//cout << blackNum << endl;
			if (refNum != blackNum)
			{
				cout << "存在黑色节点的数量不相等的路径" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << root->_kv.first << "存在连续的红色节点" << '\n';
			return false;
		}

		if (root->_col == BLACK)
		{
			blackNum++;
		}

		return Check(root->_left, blackNum, refNum) && Check(root->_right, blackNum, refNum);
	}

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

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

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

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

			Node* parent_parent = parent->_parent;

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

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

				subR->_parent = parent_parent;
			}
		}


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

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

			Node* parent_parent = parent->_parent;

			subL->_right = parent;
			parent->_parent = subL;


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

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << '\n';
		_InOrder(root->_right);
	}


	Node* _root = nullptr;

};

//void TestRBTree1()
//{
//	RBTree<int, int> t;
//	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
//	// int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
//	for (auto e : a)
//	{
//
//		t.Insert({ e, e });
//	}
//
//	t.InOrder();
//	cout << t.IsBalance() << endl;
//}

以上就是红黑树的改造及封装map和set全部内容,需要我们好好掌握,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉

相关推荐
plmm烟酒僧几秒前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv
测试界的酸菜鱼11 分钟前
Python 大数据展示屏实例
大数据·开发语言·python
我是谁??12 分钟前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
晨曦_子画21 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
Black_Friend29 分钟前
关于在VS中使用Qt不同版本报错的问题
开发语言·qt
南宫生44 分钟前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
发霉的闲鱼1 小时前
MFC 重写了listControl类(类名为A),并把双击事件的处理函数定义在A中,主窗口如何接收表格是否被双击
c++·mfc
小c君tt1 小时前
MFC中Excel的导入以及使用步骤
c++·excel·mfc
希言JY1 小时前
C字符串 | 字符串处理函数 | 使用 | 原理 | 实现
c语言·开发语言
残月只会敲键盘1 小时前
php代码审计--常见函数整理
开发语言·php