C++标准容器类STL中的集合 set 比较特殊,在其中存储的元素是独一无二的,因为它存储的元素值同时也充当“key”的角色,也即可以通过“key”访问指定的元素。此外,元素一旦被插入到 set 容器类中,就不能在被修改,不过,我们可以删除它或者插入新的元素。
STL 中的集合 set
要使用C++标准容器类 STL 中的 set,需要先包含<set>
头文件:
#include <set>
之后,便可定义 set 对象:
set<data_type> myset;
其中的 data_type 可以是任意的数据类型,所谓“任意”,当然也包括 set 本身,例如下面这两行C++代码:
set<int> int_set;
set<set<int> > int_sets;
上述第一行代码定义的 int 类型的 set 容器,其中可以存储 int 型的元素。第二行代码则定义了 set
操作 set
操作C++中标准容器类 set 很像操作 map(本专栏之后会讨论),下面是 set 类的一些基本操作(成员函数):
- begin:返回指向 set 第一个元素的迭代器。
- end:返回指向 set 最后一个元素的迭代器。
- insert:将新元素插入 set,insert 方法一般有 3 中形式:
- insert(elem):将 elem 直接插入到 set 中,并且重新排序。
- insert(pos, elem):将 elem 插入到 set 中的 pos 位置。
- insert(it.begin(), it.end()):将 it 表示范围内的元素插入到 set。
- erase:从 set 中删除一个元素。
- size:返回 set 中元素的数目。
- max_size:返回 set 能够容纳的最大 size。
- empty:返回 set 中是否有元素。
- clear:删除 set 中的所有元素。
- find:在 set 中查找元素,如果找到了,返回一个指向改元素的迭代器,如果找不到,返回一个指向 set.end 的迭代器。
作为实例,下面这段C++代码简单使用了上述各种方法,请看:
#include <iostream>
#include <set>
#include <iterator>
using namespace std;
int main()
{
set <int> myset;
myset.insert(140);
myset.insert(130);
myset.insert(160);
myset.insert(120);
cout<<"\nSize of myset: "<<myset.size();
set <int > :: iterator itr;
cout << "\nThe set myset is : ";
for (itr = myset.begin(); itr != myset.end(); ++itr)
cout << ' ' << *itr;
cout << endl;
set<int>::iterator it = myset.begin();
myset.insert(it,100);
cout << "\nAfter inserting 100,the set myset is : ";
for (itr = myset.begin(); itr != myset.end(); ++itr)
cout << ' ' << *itr;
cout << endl;
int arr[3] = {110,150,150};
myset.insert(arr,arr+2);
cout << "\nAfter inserting array arr,the set myset is : ";
for (itr = myset.begin(); itr != myset.end(); ++itr)
cout << ' ' << *itr;
cout << endl;
cout << "\nAfter removal of elements less than 130, myset: ";
myset.erase(myset.begin(), myset.find(130));
for (itr = myset.begin(); itr != myset.end(); ++itr)
cout << ' ' << *itr;
cout << endl;
return 0;
}
编译并执行这段C++代码,得到如下输出:
Size of myset: 4
The set myset is : 120 130 140 160
After inserting 100,the set myset is : 100 120 130 140 160
After inserting array arr,the set myset is : 100 110 120 130 140 150 160
After removal of elements less than 130, myset: 130 140 150 160
这段C++程序一开始通过简单的 insert() 函数创建了一个 size 为 4 的 set。
接着,程序使用了另外一种形式的 insert(it,100) 函数将 100 插入到 set 中,从输出来看,元素 100 被成功插入到 set 中了,而且 set 也被重新排序了。
然后,我们又通过第三种形式的 insert(arr,arr+2) 函数插入了数组 {110,150,150}。但是从输出来看,在执行完插入后,set 中只有一个 150,这是因为 set 中的元素是独一无二的。
最后,我们通过 find() 函数查找到元素 130 在 set 中的位置,并且调用 erase() 函数将 set 开头到该位置的元素删除,也就是将小于 130 的元素删除,从结果来看,一切与预期一致。
“多元 set” multiset
从字面意思来看,multiset 可以拆分成 multi 和 set,因此 multiset 的本质是 set,而 multi 则说明了它与普通 set 不同的地方:multiset 允许内部有相同的元素。
定义一个 multiset 是简单的,例如定义一个存放 int 元素的 multiset:
multiset<int> mset;
操作 multiset 的方式与普通 set 没有什么不同,下面这段C++代码是一个实例,请看:
#include <iostream>
#include <set>
#include <iterator>
using namespace std;
int main()
{
multiset <int> myset;
myset.insert(11);
myset.insert(13);
myset.insert(13);
myset.insert(10);
cout<<"\nSize of myset: "<<myset.size();
set <int > :: iterator itr;
cout << "\nAfter inserting four elements, the multiset myset is : ";
for (itr = myset.begin(); itr != myset.end(); ++itr)
cout << ' ' << *itr;
cout << endl;
set<int>::iterator it = myset.begin();
myset.insert(it,15);
cout << "\nAfter inserting 15,the multiset myset is : ";
for (itr = myset.begin(); itr != myset.end(); ++itr)
cout << ' ' << *itr;
cout << endl;
cout << "\nAfter removal of elements less than 15, myset: ";
myset.erase(myset.begin(), myset.find(15));
for (itr = myset.begin(); itr != myset.end(); ++itr)
cout << ' ' << *itr;
cout << endl;
return 0;
}
编译并执行这段C++代码,可得到如下输出:
Size of myset: 4
After inserting four elements, the multiset myset is : 10 11 13 13
After inserting 15,the multiset myset is : 10 11 13 13 15
After removal of elements less than 15, myset: 15
代码一开始插入了 4 个元素到 multiset,其中有两个元素是相同的。但是从最后的输出来看,multiset 不像普通 set,它允许内部存放两个相同的元素。其他过程就与 set 的实例没什么不同了。
“无序 set” unreordered_set
前面讨论了两种集合:set 和 multiset。其中,set 是一个有序的集合,并且其中的元素独一无二,也即不会有两个相同的元素。multiset 也是一个“有序”的集合,它与 set 的不同点在于其内部可以存放相同的元素。
C++中的 unreordered_set 是一个“无序”的集合容器,其中存放的元素的顺序是任意的,这一点与 set/multiset 是不同的。
类似于 map 容器类,unreordered_set 也使用了哈希表,通过 key 访问元素。作为对比,set 在其内部使用了平衡树管理元素。
使用“无序 set”,需要先包含头文件<reordered_set>
,相关C++代码如下:
#include <unordered_set>
定义一个“无序 set” 是简单的,例如定义一个存放 int 元素的对象的 C++代码可以如下写:
unordered_set<int> uset;
操作 unreordered_set 的方法大都与 set 相同,下面是一段简单的C++代码实例,请看:
#include<iostream>
#include <unordered_set>
using namespace std;
int main()
{
unordered_set<int> uset;
unordered_set<int> :: iterator it;
for(int i=0;i<5;i++){
uset.insert(i+2);
}
cout<<"\nSize of uset: "<<uset.size();
cout<<endl;
it= uset.begin();
uset.insert(it,99);
int ary[]= { 13, 26, 39};
uset.insert(ary, ary+3); // Inserting using method3
cout<<"\nElements in unordered set are: ";
for(it= uset.begin(); it!=uset.end(); it++) cout << *it << " ";
cout<<endl;
int key=13;
if (uset.find(key) == uset.end())
cout<<"\nkey = "<< key << "not found\n\n";
else
cout << "\nFound key = " << key << endl << endl;
cout<<"umap bucket_count : "<<uset.bucket_count();
cout<<"\nbucket_size : "<<uset.bucket_size(2);
return 0;
}
编译并执行这段C++代码,得到如下输出:
Size of uset: 5
Elements in unordered set are: 99 39 6 5 26 4 3 13 2
Found key = 13
umap bucket_count : 11
代码开头插入 5 个元素然后插入 4 个元素到无序 set 的操作与之前没什么不同,但是从之后的输出来看,unreordered_set 中存放的元素顺序与插入顺序并不对应,这对于内部使用哈希表管理元素的无序 set 来说并无不妥。
接着,程序调用了 find() 函数查找 key=13 是否在无序 set 中,从输出来看,程序找到了对应的结果。
最后,程序调用了之前两种 set 没有的两个函数:bucket_count() 和 bucket_size() 函数,这两个函数和 unreordered_set 内部使用的哈希映射有关。
小结
本文主要讨论了C++标准容器类中的三种 set,它们各有特点:set 不允许自己内部有两个相同的元素,而 multiset 则可以,不过这两种序列中的所有元素都是有序的,与之对应的,unreordered_set 中的元素是无序的,其内部元素的顺序与插入顺序没有关系。