set和map

本文介绍了C++STL中的关联式容器,包括set、multiset、map和multimap,它们基于树状结构(如AVL树和红黑树)实现,用于存储键值对。set和multiset不允许重复元素,而map和multimap则存储键值对,map不允许键重复。文章详细讲解了这些容器的使用方法以及底层平衡树的插入和旋转操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关联式容器

在之前的学习中,所学习的容器类型都是序列式容器,其底层是线性数据结构存储的是元素本身

本章所学习的是另一种容器关联式容器,关联就意味着元素与元素之间存在着某种关联;存储的是数据是 <key,value>结构的键值对,接下来学习什么是键值对

键值对

键值对用来表示具有一一对应关系的一种结构,此结构中一般只包括两个成员变量 keyvaluekey代表键值, value代表与 key对应的信息;通过结构体pair进行定义

在这里插入图片描述
在这里插入图片描述

template<class T1,class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;

	T1 first;
	T2 second;

	pair()
		:first(T1())
		,second(T2())
	{}

	pair(const T1&a,const T2&b)
		:first(a)
		,second(b)
	{}
};

一般在进行对pair赋值并插入时,使用其另一种形式make_pair
在这里插入图片描述

树状结构关联式容器

根据应用场景的不同,STL实现了两种不同结构的管理式容器:树形结构和哈希结构;树形结构的关联式容器主要有四种: set, multiset, map, multimap。这四种容器都是以平衡搜索树作为底层结构来实现的。接下来对这些容器一一介绍

set

介绍

  1. set是按照一定次序存储元素的
  2. 在set中,只存放实值value,但在底层实际存放的是键值对<value,value>;插入元素时只需要插入实值value,不需要构造键值对
  3. 元素不可以重复,且不允许修改
  4. 元素默认按照小于进行比较
  5. 底层由二叉搜索树实现

使用

模板参数列表

在这里插入图片描述

  1. T:set中存放的元素的类型是T,既是key也是value
  2. Compare:set中元素默认按照小于进行比较
int main()
{
	set<int>myset;
	myset.insert(5);
	myset.insert(5);
	myset.insert(3);
	myset.insert(4);
	myset.insert(1);
	myset.insert(0);
	myset.insert(0);
	for (auto e : myset)
	{
		cout << e << " ";
	}
	cout << endl;
	auto pos = find(myset.begin(), myset.end(), 3);
	myset.erase(pos);
	for (auto e : myset)
	{
		cout << e << " ";
	}
	return 0;
}

在这里插入图片描述

通过运行结果可以发现, set会自动对元素进行排序+去重

multiset

介绍

在这里插入图片描述

  1. multiset是按照特定顺序存储元素的容器,元素可以重复
  2. 其余的与set一致

使用

int main()
{
	multiset<int>myset;
	myset.insert(5);
	myset.insert(5);
	myset.insert(3);
	myset.insert(4);
	myset.insert(1);
	myset.insert(0);
	myset.insert(0);
	for (auto e : myset)
	{
		cout << e << " ";
	}
	cout << endl;
	auto pos = find(myset.begin(), myset.end(), 3);
	myset.erase(pos);
	for (auto e : myset)
	{
		cout << e << " ";
	}
	return 0;
}

在这里插入图片描述

通过结果可以发现,相比于set,multiset只是进行简单的排序,并不会进行去重操作

map

介绍

  1. 关联式容器,按照 key的比较次序来存储由键值 key和值 value组成的键值对,通过成员类型 value_type,称为 pair
typedef pair<const key,T>value_type;
  1. 键值 key用于排序和标识元素;值 value存储与键值关联的内容;两者的类型可能不同
  2. 在内部,map中的元素总是按照键值key进行比较排序的,默认是按照小于进行排序的
  3. map中存放的元素是键值对pair<key,value>
  4. 支持下标访问,即可以通过键值key访问与之对应的value

使用

模板参数说明
在这里插入图片描述

  1. Key:key的类型
  2. T:value的类型
  3. Compare:比较器,通过key进行比较
int main()
{
	map<string, string>dict;
	dict.insert(make_pair("east", "东"));
	dict.insert(make_pair("west", "西"));
	dict.insert(make_pair("south", "南"));
	dict.insert(make_pair("north", "北"));
	for (const auto& e : dict)
	{
		cout << e.first << " " << e.second << endl;
	}
	return 0;
}

在这里插入图片描述

multimap

介绍

在这里插入图片描述

  1. multimap是按照特定顺序存储键值对pair<key,value>的序列式容器,元素可以重复
  2. 其余的与map一致

使用

统计球的次数

int main()
{
	string arr[] = { "篮球","羽毛球","乒乓球","羽毛球","乒乓球","羽毛球""羽毛球","乒乓球" };
	multimap<string, int>coutmap;
	for (auto& e : arr)
	{
		auto it = coutmap.find(e);
		if (it == coutmap.end())
		{
			coutmap.insert(make_pair(e, 1));
		}
		else
		{
			it->second++;
		}
	}
	for (const auto& e : coutmap)
	{
		cout << e.first << " " << e.second << endl;
	}
	return 0;
}

在这里插入图片描述

这里还有另一种方式进行统计,需要使用operator[],使用之前,先介绍其原理,做到知其然知其所以然

在这里插入图片描述

下标访问返回值:

(*((this->insert(make_pair(k,mapped_type()))).first)).second

逐步进行分析:

  1. make_pair(k,mapped_type()),根据键值k和实值类型相同的临时对象mapped_type()创建一个元素
  2. insert(make_pair(k,mapped_type())),将该元素插入到map中去,这里还需要了解到插入操作会返回一个pair,第一个元素是迭代器,指向插入的新元素或者键值重复的旧元素;第二个元素标识着插入的成功与否
pair<iterator,bool> insert (const value_type& val);
  1. (this->insert(make_pair(k,mapped_type()))).first,取得插入返回 pair中的第一个元素,即迭代器,指向插入的元素
  2. (*((this->insert(make_pair(k,mapped_type()))).first)).second,对迭代器进行解引用,获得一个 map元素,由键值 key和实值 value组成的 pair取得其第二个元素,也就是实值

对上面的代码进行修改

int main()
{
	string arr[] = { "篮球","羽毛球","乒乓球","羽毛球","乒乓球","羽毛球""羽毛球","乒乓球" };
	map<string, int>coutmap;
	for (auto& e : arr)
	{
		coutmap[e]++;
	}

	for (const auto& e : coutmap)
	{
		cout << e.first << " " << e.second << endl;
	}
	return 0;
}

在这里插入图片描述

底层容器

前面的set/multiset/map/multimap有个共同特点:底层都是通过二叉搜索树来实现的,二叉搜索树本身也存在着缺陷,如果插入值不够随机,或者经过某些插入或删除操作导致二叉搜索树失去平衡,造成搜寻效率低的情况,因此set/map的底层结构是对二叉搜索树进行了平衡处理的平衡树来实现的

在这里插入图片描述

AVL树

概念

加上了平衡条件的二叉搜索树,要求任何节点的左右子树高度差最多是1,可以降低树的高度,从而减少平均搜索长度

一颗AVL树或者空树,具有以下性质

  1. 它的左右子树都是AVL树
  2. 左右子树的高度差的绝对值不超多1(-1/0/1)

在这里插入图片描述

操作

节点定义

template<class K,class V>
struct AVLTreenode
{
	pair<K, V> _kv;
	AVLTreenode<K, V>* _left;
	AVLTreenode<K, V>* _right;
	AVLTreenode<K, V>* _parent;
	int _bf;//平衡因子
	AVLTreenode(const pair<K, V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
	{}
};

这里引进了一个新的概念平衡因子,简单来说就是:右子树的高度减去左子树的高度得到的结果就是该节点的平衡因子

插入

AVL树在插入元素方面与二叉搜索树大致相同,除了需要对平衡因子进行处理之外;所以插入的过程分为两步:1.按照二叉搜索树的方式插入新节点2.调节平衡因子

插入新节点

bool insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new node(kv);
		return true;
	}
	node* parent = nullptr;
	node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if(cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->left;	
		}
		else
		{
			return false;
		}
	}
	cur = new node(kv);
	if (parent->_kv.first > kv.first)
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	//更新平衡因子
}

更新平衡因子

  1. 插入节点在右,parent->_bf++
  2. 插入节点在左,parent->_bf--
if (cur == parent->_left)
{
	parent->_bf--;
}
else
{
	parent->_bf++;
}

父节点的平衡因子更新过后,是否需要继续向上更新的依据是:子树的高度是否发生变化

  1. parent->_bf==0,说明插入之前父节点就空缺了左右节点其中一个parent->_bf==-1/parent->_bf==1,而插入之后刚好补全了空缺;也就是说子树并没有发生变化,不需要继续向上更新
if (parent->_bf == 0)
{
	break;
}
  1. parent->_bf==1/parent->_bf==-1,说明插入之前左右子树的高度一样,插入之后其中一边变得更高;由于子树的高度发生了变化,所以需要向上更新
else if (parent->_bf == 1 || parent->_bf == -1)
{
	cur = parent;
	parent = parent->_parent;
}
  1. parent->_bf==2/parent->_bf==-2,说明插入之前左右子树就已经一边高一边低;插入之后平衡已经被破坏,需要就地处理->旋转

旋转

旋转的核心思想:
使这颗子树的高度差不超过1;旋转过程中继续保持它是二叉搜索树;更新子节点的平衡因子;使子树的高度与插入之前保持一致

根据父节点的平衡因子和插入节点的平衡因子旋转的情景大志分为4种:

  1. parent->_bf==2&&cur->_bf==1

在这里插入图片描述

旋转的具体操作:将节点6的左节点调整到节点5的右节点上;节点5变成节点6的左节点;节点6作为根指向ppnode

//左旋
void RotateL(node* parent)
{
	node* subR = parent->_right;
	node* subRL = subR->_left;
	parent->_right = subRL;
	if (subRL)
	{
		subRL->_parent = parent;
	}
	node* ppnode = parent->_parent;
	subR->_left = parent;
	parent->_parent = subR;
	if (ppnode == nullptr)
	{
		_root = subR;
		_root->_parent = nullptr;
	}
	else
	{
		if (ppnode->_left = parent)
		{
			ppnode->_left = subR;
		}
		else
		{
			ppnode->_right = subR;
		}
		subR->_parent = ppnode;
	}
	parent->_bf = subR->_bf = 0;
}
  1. parent->_bf == -2 && cur->_bf == -1

在这里插入图片描述

旋转具体操作:将节点3的右节点调整到节点6的左节点上;节点6变成节点3的右节点;节点3作为根节点指向ppnode

//右旋
void RotateR(node* parent)
{
	node* subL = parent->_left;
	node* subLR = subL->_right;
	parent->_left = subLR;
	if (subLR)
	{
		subLR->parent = parent;
	}
	node* ppnode = parent->_parent;
	subL->_right = parent;
	parent->_parent = subL;
	if (ppnode == nullptr)
	{
		_root = subL;
		_root->_parent = nullptr;
	}
	else
	{
		if (ppnode->_left == parent)
		{
			ppnode->_left = subL;
		}
		else
		{
			ppnode->_right = subL;
		}
		subL->_parent = ppnode;
	}
	subL->_bf = parent->_bf = 0;
}
  1. parent->_bf == -2 && cur->_bf == 1

在这里插入图片描述

旋转操作:以节点3为轴点,进行左单旋,将节点6的左节点调整到节点3的右节点上;节点3变成节点6的左节点,节点6变成节点9的左节点;以节点9为轴点,进行右单旋,将节点6的右节点调整到节点9的左节点上;节点9变成节点6的右节点;节点6变成根节点指向ppnode

//左右双旋
void RotateLR(node* parent)
{
	node* subL = parent->_left;
	node* subLR = subL->_right;
	int bf = subLR->_bf;
	RotateL(parent->_left);
	RotateR(parent);
	//subLR左子树新增节点
	if (bf == -1)
	{
		subL->_bf = 0;
		parent->_bf = 1;
		subLR->_bf = 0;
	}//subLR右子树新增节点
	else if (bf == 1)
	{
		parent->_bf = 0;
		subL->_bf = -1;
		subLR->_bf = 0;
	}//subLR自己是新增节点
	else if (bf == 0)
	{
		parent->_bf = 0;
		subL->_bf = 0;
		subLR->_bf = 0;
	}
	else
	{
		assert(false);
	}
}
  1. parent->_bf == 2 && cur->_bf == -1

在这里插入图片描述

旋转操作:以节点9为轴点,进行右单旋,将节点6的右节点调整到节点9的左节点上;节点9变成节点6的右节点,节点6变成节点3的右节点;以节点3为轴点进行左单旋,将节点6的左节点调整为节点3的右节点;节点3变成节点6的左节点,节点6变成根节点指向ppnode

//右左双旋
void RotateRL(node* parent)
{
	node* subR = parent->_right;
	node* subRL = subR->_left;
	int bf = subRL->_bf;
	RotateR(parent->_right);
	RotateL(parent);
    //subRL右子树新增节点
	if (bf == 1)
	{
		subR->_bf = 0;
		subRL->_bf = 0;
		parent->_bf = -1;
	}
	//subRL右子树新增节点
	else if (bf == -1)
	{
		subR->_bf = 1;
		subRL->_bf = 0;
		parent->_bf = 0;
	}
	//subRL本身就是新增节点
	else if (bf == 0)
	{
		subR->_bf = 0;
		subRL->_bf = 0;
		parent->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

红黑树(RBTree)

概念

AVL树之外,另一个颇具历史被广泛运用的平衡二叉树就是红黑树RBTree,所谓红黑树,不仅是一个二叉搜索树,而且必须满足以下规则

  1. 每个节点不是红色就是黑色
  2. 根节点为黑色
  3. 如果节点为红,其子节点必须为黑(注意,这里并不没有说明不可以是连续的黑色)
  4. 任意节点至树尾端的路劲,所含有的黑节点数必须相同

最长路径不超过最短路径的二倍
最长路径:黑红相间的路径
最短路径:全为黑色

在这里插入图片描述

根据规则4,新增节点必须是红色

节点的设计

红黑树有红黑两种颜色,并且拥有左右子节点,节点的设计如下

enum Color
{
	RED,
	BLACK,
};
template<class K,class V>
struct RBTreenode
{
	pair<K, V> _kv;
	RBTreenode<K, V>* _left;
	RBTreenode<K, V>* _right;
	RBTreenode<K, V>* _parent;

	Color _col;

	RBTreenode(const pair<K, V>& kv)
		:_kv(kv)
		,_col(RED)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
	{}
};

迭代器的设计

红黑树的迭代器属于双向迭代器,各种操作都类似于list,较为特殊的就是++--,这两个操作完全依据红黑树的节点的排列法则

先处理++,根据红黑树的中序遍历:左子树-根-右子树
如果右子树不为空,可直接到右子树中寻找键值最小的节点便是根节点++之后的节点

在这里插入图片描述

如果右子树不存在,并且此时节点还是父节点的右节点,向上找到的祖先便是++之后的节点;如果此时节点是父节点的左节点,那么父节点便是++之后的节点

在这里插入图片描述

--操作于此相同

这里还需要注意一点的是红黑树的迭代器中存在着构造函数
当普通迭代器调用时,权限缩小,此时是拷贝构造;当const迭代器调用时,此时是构造函数

_RBTreeIterator(const iterator& s)
	:_node(s._node)
{}

迭代器的完整代码如下

template<class T>
struct _RBTreeIterator
{
	typedef RBTreenode<T> node;
	typedef _RBTreeIterator<T, Ref, Ptr> Self;
	typedef _RBTreeIterator<T, T&, T*> iterator;
	node* _node;

	_RBTreeIterator(node* node)
		:_node(node)
	{}

	_RBTreeIterator(const iterator& s)
		:_node(s._node)
	{}

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

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

	Self& operator++()
	{
		if (_node->_right)
		{
			node* min = _node->_right;
			while (min->_left)
			{
				min = min->_left;
			}

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

			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node->_left)
		{
			node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}

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

			_node = parent;
		}

		return *this;
	}

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

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

结构

红黑树本质是二叉搜索树,每个节点存储的数据是键值对pair<key,value>,键值key唯一标识节点不可修改,实质value保存信息可以修改;

在这里插入图片描述
STL规定,begin()end()代表一段前闭后开的区间,对红黑树进行中序遍历之后,可以得到有序序列,因此:begin()可以放在红黑树中最小节点的位置,end()可以放在最大节点的下一个位置(这里直接设置为空指针)

begin()

iterator begin()
{
	node* left = _root;
	while (left && left->_left)
	{
		left = left->_left;
	}
	return iterator(left);
}

end()

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

插入

插入操作的返回值是键值对pair

pair<iterator,bool> insert (const value_type& val);

红黑树是在二叉树搜索树的基础上加上其平衡条件,因此红黑树的插入可分两步:

  1. 按照二叉搜索树的规则插入新节点

  2. 检测新节点插入之后,红黑树的性质是否被破坏
    新增节点必须是红色的,如果双亲结点的颜色是黑色,则没有违反红黑树规则,不需要调整;如果双亲结点是红色,此时就违反了双亲结点的规则3不能有连续的红色节点,需要进行调整;根据叔叔节点的情况,调整的情景也分为3种:
    约定:新增节点为cur,父节点为p,祖父节点为g,叔叔节点为u

cur为红,p为红,g为黑,u存在且为红

在这里插入图片描述

调整操作:将父节点 p和叔叔节点 u改为黑色,祖父节点 g改为红色;将祖父节点 u作为新增节点,继续向上调整

cur为红,p为红,g为黑,u不存在/存在且为黑

在这里插入图片描述

调整操作:如果父节点 p是祖父节点 g的左节点,新增节点cur是父节点p的左节点,进行左单旋;相反,父节点 p是祖父节点 g的右节点,新增节点cur是父节点p的右节点,进行左单旋

以父节点 p为轴点进行左单旋;将父节点 p改为黑色,祖父节点 g改为红色;父节点 p作为新增节点继续向上调整

cur为红,p为红,g为黑,u不存在/存在且为黑

在这里插入图片描述

调整操作:如果父节点p是祖父节点g的左节点,新增节点cur是父节点p的右节点,则以父节点p为轴心进行左单旋;如果父节点p是祖父节点g的右节点,新增节点cur是父节点p的左节点,则以父节点p为轴心进行右单旋
以父节点p为轴心进行左单旋,转换成情景二进行调整

pair<iterator, bool>insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new node(data);
		_root->_col = BLACK;
		return make_pair(iterator(_root), true);
	}
	
	node* parent = nullptr;
	node*newnode=node(cur);
	node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return make_pair(iterator(cur),false);
		}
	}

	cur = new node(kv);
	node* newnode = cur;
	cur->_col = RED;
	if (parent->_kv.first < kv.first)
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	while (parent && parent->_col == RED)
	{
		node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			node* uncle = grandfather->_right;
			//情景1,叔叔存在且为红
			if (uncle && uncle->_col == RED)
			{
				parent->_col == uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_left)
				{
					//情景2
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//情景3
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
		else
		{
			node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
		
	}
	_root->_col = BLACK;
	return make_pair(iterator(newnode), true);
}

红黑树模拟实现setmap

通过红黑树来模拟实现这两种容器,首先要解决的一个问题就是:set的节点中存储的数据是key(也是value),而map中存储的是键值对pair<key,value>;所以在红黑树中先要修改节点中存储数据的类型,使得当模板根据不同的类型,实现不同的容器

在这里插入图片描述

节点改造如下

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)
		,_col(RED)
	{}
};

节点修改之后,还会出现一个问题,在进行插入操作时,会将待插入节点的键值与根节点的键值进行比较,但是由于此时节点中的数据类型是data,如果是模拟实现set那么不会出现任何问题;如果是模拟实现map,编译器也不清楚data是表示键值还是实值,而且键值对pair中的比较大小的函数,并不是只比较键值大小

在这里插入图片描述

所以这时便需要想办法将键值对中的键值取出来;解决办法:在红黑树模板中加上一个模板参数KeyofT,创建仿函数;当实现setdata的是键值也是实值,当实现map时取出键值对pair中的键值即可

改造红黑树如下

//map->RBTree<K,pair<const K,V>,MapKeyofT> _t
//set->RBTree<K,K,SetKeyofT> _t
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()
	{
	......
	}

	iterator begin()
	{
		node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return iterator(left);
	}

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

	const_iterator begin() const
	{
		node* left = _root;
		while (left && left->_left)
		{
			left = left->left;
		}
		return const_iterator(left);
	}

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

	pair<iterator, bool>insert(const T& data)
	{
		......
		return make_pair(iterator(newnode), true);
	}
	
private:
	node* _node = nullptr;
};

模拟实现map

template<class K,class V>
class map
{
    //获取键值
	struct MapkeyofT
	{
		const K& operator()(const pair<const 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()
	{
		return _t.begin();
	}
	const_iterator end()
	{
		return _t.end();
	}
	pair<iterator, bool>insert(const pair<const K, V>& kv)
	{
		return _t.insert(kv);
	}
	V& operator[](const K& key)
	{
		pair<iterator, bool>ret = insert(make_pair(key, V()));
		return ret.first->second;
	}
private:
	RBTree<K, pair<const T, V>, MapkeyofT> _t;
};

模拟实现set

template<class K>
class set
{
	struct SetkeyofT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};

public:
	typedef typename RBTree<K, K, SetkeyofT>::const_iterator iterator;
	typedef typename RBTree<K, K, SetkeyofT>::const_iterator const_iterator;

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

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

	pair<iterator, bool>insert(const K& key)
	{
		pair<typename RBTree<K, K, SetkeyofT>::iterator, bool>ret = _t.insert();
		return pair<iterator, bool>(ret.first, ret.second);
	}

private:
	RBTree<K, K, SetkeyofT> _t;
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值