【深度探索STL】关联式容器set和multiset

本文详细介绍了C++ STL中的set与multiset容器。set不允许键值重复且元素自动排序,而multiset允许键值重复。两者底层均采用红黑树实现,提供了丰富的操作接口。文章还对比了两者的使用差异,并给出了实例代码。
摘要由CSDN通过智能技术生成

标准的STL关联式容器分为set(集合)和map(映射表)两大类,以及两大类的衍生体multiset(多键集合)和multimap(多键映射表)(还有一些不在标准规格之列的关联式容器)。这些容器的底层机制均以RB-tree(红黑树)完成。

一、set

set 的特性是,所有元素都会根据元素的键值自动被排序,set 只拥有一个元素,它的键值就是实值,实值就是键值,并且set 不允许两个元素有相同的键值。

由于,set 元素值就是其键值,关系到 set 元素的排列规则,所以我们不能通过set 的迭代器来改变set 的元素值。标准的STL set 是以RB-tree 为底层机制,几乎所有的 set 操作行为,都只是转调用 RB-tree 的操作行为而已。

下面我们来看看 set 的源码(老版本)

class set {
public:
	// typedefs:

	typedef Key key_type;
	typedef Key value_type;
	typedef Compare key_compare;
	typedef Compare value_compare;
private:
	typedef rb_tree<key_type, value_type,
		identity<value_type>, key_compare, Alloc> rep_type;    //typedef RB-tree 类型
	rep_type t;  // 定义一棵红黑树t,也是set的唯一私有成员,set内部的元素皆存放于此
public:
	/*鉴于set特性,不允许修改其元素值,所以迭代器类型被定义为底层RB-tree的const_iterator*/
	typedef rep_type::const_pointer pointer;
	typedef rep_type::const_reference reference;
	typedef rep_type::const_reference const_reference;
	typedef rep_type::const_iterator iterator;
	typedef rep_type::const_iterator const_iterator;
	typedef rep_type::const_reverse_iterator reverse_iterator;
	typedef rep_type::const_reverse_iterator const_reverse_iterator;
	typedef rep_type::size_type size_type;
	typedef rep_type::difference_type difference_type;

	// allocation/deallocation
	/*构造函数*/
	set() : t(Compare()) {}
	explicit set(const Compare& comp) : t(comp) {}

#ifdef __STL_MEMBER_TEMPLATES
	template <class InputIterator>
	set(InputIterator first, InputIterator last)
		: t(Compare()) {
		t.insert_unique(first, last);
	}

	template <class InputIterator>
	set(InputIterator first, InputIterator last, const Compare& comp)
		: t(comp) {
		t.insert_unique(first, last);
	}
#else
	/*将[first,end)之间的元素插入set中*/
	set(const value_type* first, const value_type* last)
		: t(Compare()) {
		t.insert_unique(first, last);
	}
	set(const value_type* first, const value_type* last, const Compare& comp)
		: t(comp) {
		t.insert_unique(first, last);
	}

	set(const_iterator first, const_iterator last)
		: t(Compare()) {
		t.insert_unique(first, last);
	}
	set(const_iterator first, const_iterator last, const Compare& comp)
		: t(comp) {
		t.insert_unique(first, last);
	}
#endif /* __STL_MEMBER_TEMPLATES */

	//赋值运算符重载
	set(const set<Key, Compare, Alloc>& x) : t(x.t) {}
	set<Key, Compare, Alloc>& operator=(const set<Key, Compare, Alloc>& x) {
		t = x.t; //直接调用RB-tree的赋值操作函数
		return *this;
	}

	// accessors:
	/*实值,键值都是同一元素值*/
	key_compare key_comp() const { return t.key_comp(); }
	value_compare value_comp() const { return t.key_comp(); }

	/*下面的接口函数都是调用RB-tree的*/
	iterator begin() const { return t.begin(); }
	iterator end() const { return t.end(); }
	reverse_iterator rbegin() const { return t.rbegin(); }
	reverse_iterator rend() const { return t.rend(); }
	bool empty() const { return t.empty(); }
	size_type size() const { return t.size(); }
	size_type max_size() const { return t.max_size(); }
	void swap(set<Key, Compare, Alloc>& x) { t.swap(x.t); }

	// insert/erase
	typedef  pair<iterator, bool> pair_iterator_bool;
	/*插入一个元素值,返回pair类型(分别是插入元素的迭代器和是否插入成功)*/
	pair<iterator, bool> insert(const value_type& x) 
	{
		//RB-tree的insert_unique()返回一个pair类型
		pair<rep_type::iterator, bool> p = t.insert_unique(x);//调用RB-tree的插入函数(自动排序)
		return pair<iterator, bool>(p.first, p.second);
	}
	/*从position开始寻找一个可以插入值为x 的元素的位置将其插入并返回其迭代器*/
	//set(RB_tree)内部元素是自动排序,均不能在指定位置插入元素(或替换元素)
	iterator insert(iterator position, const value_type& x) 
	{
		return t.insert_unique((rep_type::iterator&)position, x);
	}
#ifdef __STL_MEMBER_TEMPLATES
	template <class InputIterator>
	void insert(InputIterator first, InputIterator last) {
		t.insert_unique(first, last);
	}
#else
	void insert(const_iterator first, const_iterator last) {
		t.insert_unique(first, last);
	}
	void insert(const value_type* first, const value_type* last) {
		t.insert_unique(first, last);
	}
#endif /* __STL_MEMBER_TEMPLATES */

	/*删除迭代器position所指元素*/
	void erase(iterator position)
	{
		t.erase((rep_type::iterator&)position);
	}
	/*删除key值为x的元素并返回被删除元素的个数*/
	size_type erase(const key_type& x) 
	{
		return t.erase(x);
	}
	/*删除[first,end)之间的元素*/
	void erase(iterator first, iterator last) 
	{
		t.erase((rep_type::iterator&)first, (rep_type::iterator&)last);
	}
	/*清空容器*/
	void clear() { t.clear(); }

	// set operations:

	iterator find(const key_type& x) const { return t.find(x); }
	size_type count(const key_type& x) const { return t.count(x); }//统计元素值为 x的个数
	/*返回一个迭代器,指向键值>=key的第一个元素*/
	iterator lower_bound(const key_type& x) const
	{
		return t.lower_bound(x);
	}
	/*返回一个迭代器,指向键值>key的第一个元素*/
	iterator upper_bound(const key_type& x) const {
		return t.upper_bound(x);
	}
	//查找键值等于x 的元素,返回pair
	pair<iterator, iterator> equal_range(const key_type& x) const 
	{
		return t.equal_range(x);
	}
	friend bool operator==(const set&, const set&);
	friend bool operator<(const set&, const set&);
};

/*调用RB-tree底层机制*/
template <class Key, class Compare, class Alloc>
inline bool operator==(const set<Key, Compare, Alloc>& x,
	const set<Key, Compare, Alloc>& y) {
	return x.t == y.t;
}

template <class Key, class Compare, class Alloc>
inline bool operator<(const set<Key, Compare, Alloc>& x,
	const set<Key, Compare, Alloc>& y) {
	return x.t < y.t;
}

set 包括下面将提到的multiset 其内部结构都是RB-tree,元素的存储以及相应操作都是引用Rb-tree的操作行为。set和multiset 内部元素都是自动排序的,这就意味着set和multiset 不提供用来直接存取元素的任何操作函数。另外对于迭代器而言,所有元素都被视为常数,这保证了不会人为的改变元素值,从而打乱既定排序。如果一定要改变元素值,必须先删除指定元素值,然后再重新插入新元素。


二、multiset

multiset 的特性以及用法和 set 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制RB-tree 的insert_equal() 而非set 采用的 insert_unique()。

从上面的源代码可以看出,set 和 multiset 的操作函数都是转而去调用RB-tree的操作函数,其具体实现都在RB-tree中。

我们知道了set 和multiset 的内部结构是RB-tree(存储方式) ,有利于我们更好的把握这两个关联式容器的函数操作。

set 的 multiset 的区别

#include <iostream>
#include <set>

using namespace std;

int main()
{
	int i;
	int ia[] = { 0, 1, 2, 3, 4 };
	set<int> iset(ia, ia + 5);//set的构造函数
	multiset<int> imset(ia, ia + 5);

	cout << "iset size = " << iset.size() << endl;//iset size = 5
	cout << "iset 3 count = " << iset.count(3) << endl;//iset 3 count = 1

	iset.insert(3);
	cout << "iset size = " << iset.size() << endl;//iset size = 5
	cout << "iset 3 count = " << iset.count(3) << endl;//iset 3 count = 1

    imset.insert(3);
	cout << "imset size = " << imset.size() << endl;//imset size = 6
	cout << "imset 3 count = " << imset.count(3) << endl;//imset 3 count = 2

	/*面对关联式容器,应该使用其所提供的find函数来搜寻元素*/
	set<int>::iterator iter;
	iter = iset.find(3);
	if (iter != iset.end())
		cout << "3 found" << endl;

	//*iter = 4; error,不能修改元素值

	return 0;
}

multiset 应用案例:最小的k个数(适合海量数据处理)

#include <iostream> 
#include <set>

using namespace std;

int main()
{
	int m, k;
	int val;
	cin >> m >> k;

	multiset<int> mset;
	multiset<int>::iterator iter;

	for (int i = 0; i < m; ++i)
	{
		cin >> val;
		if (mset.size() < k)
		{
			mset.insert(val);
		}
		else
		{
			/*求最大的k个数,只需修改下面的迭代器和后面的if判断语句即可*/
			iter = --mset.end();
			if (*iter > val)
			{
				mset.erase(*iter);
				mset.insert(val);
			}
		}
	}
	iter = mset.begin();
	for (int i = 0; i < k; ++i)
	{
		cout << *(iter++) << endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值