目录
前言
因为set和map中涉及到一些新的概念,所以会在前面做一些必要的铺垫以便后面的叙述,并不是说离题了。
关于set
再谈容器,先问一个问题:何为容器?《STL源码剖析》一书中是这么解释的——容器,置物之所也。根据“数据在容器中的排列”特性,容器可以分为序列式容器和关联式容器两种。
序列式容器,其底层为线性序列的数据结构,里面存储的是数据本身。
关联式容器,其底层是红黑树(一种平衡搜索树),里面存储的是<key, val>结构的键值对,也是存储数据本身的。set和map均为关联式容器。
键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。举个例子,用过和没用过英汉字典的都知道,每个单词都有对应的中文翻译(我们假设每个单词和中文之间是一一对应的),当我们找到hello的时候,也就找到了它的中文翻译“你好”。形如<hello, 你好>这样,都可以看做是键值对。
以下是从SGI-STL中提取的关于键值对的定义,STL库中的实现远比这复杂,因为STL库要考虑的因素很多,我们只是抽出其核心。
namespace pcz
{
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核心功能是有的
int main()
{
pcz::pair<string, string> p("left","左边");
return 0;
}
set的介绍
有了前面的铺垫以后,我们进入正题。想要准确了解set的特性,就避不开文档。下面我们先看看文档中是怎么介绍set的。这里多说一嘴,有些同学可能对这些英文文档很反感,但我想说的是计算机毕竟是老外搞出来的,很多主流的论坛比如stackoverflow等都是老外的,接入这些论坛我们必不可少要接触英语。所以呢,英语还是要好好重视。当然,下面我也用中文列出了文档中提到关于set的特性。
Sets are containers that store unique elements following a specific order.
1)set是按照特定顺序存储唯一元素的容器。
In a set, the value of an element also identifies it (the value is itself the key, of type T), and each value must be unique. The value of the elements in a set cannot be modified once in the container (the elements are always const), but they can be inserted or removed from the container.
2)在set中,元素的值唯一标识它,key就是value,value就是key,而且每一个值都是唯一的。
3)set中元素的值不可修改,一旦元素进入set中,它就总是常量,但是可以从容器中插入和删除它们。
Internally, the elements in a set are always sorted following a specific strict weak ordering criterion indicated by its internal comparison object (of type Compare).
4)在内部,set总是按照其内部比较对象所指示的特定严格弱排序准则进行排序。
set containers are generally slower than unordered_set containers to access individual elements by their key, but they allow the direct iteration on subsets based on their order.
5)set容器一般在通过key访问单个元素时比unordered_set容器慢,但是它们允许根据顺序对子集直接进行迭代。
Sets are typically implemented as binary search trees.
6)set通常是以二叉搜索树实现。
上面翻译完了,下面我们说一点细节:
> set在底层实际存放的是<value, value>构成的键值对,所以说key就是value,value就是key。
> set中插入数据时,只需要插入value即可,不需要构建键值对。
> set中的元素不可以重复,因此可以使用set去重。
> 使用set的迭代器遍历内部的元素,得到的是有序序列(中序)
> set中的元素默认按照小于来比较
> set中查找某个元素,时间复杂度为(以2为底),就相当于二分查找
> set的底层是用红黑树来实现的
set的使用
有了上面的了解后,我们接下来看看set的使用,下面将介绍set中常用的函数。
set的构造
函数声明 | 功能介绍 |
set (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type()); | 构造空的set |
set (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); | 用[first,last)区间中的元素构造set |
set (const set& x); | set的拷贝构造 |
int arr[] = { 1, 2, 3, 4, 5 };
set<int> st1; //构造空的set
set<int> st2(arr, arr + sizeof(arr) / sizeof(int));//区间构造
set<int> st3(st2); //拷贝构造
set<int> st4(st2.begin(), st2.end()); //迭代器区间构造
set中的常用方法
> begin() 返回指向第一个元素的迭代器
> end() 返回指向最后一个元素的后面理论元素的迭代器(下面我会单独解释此处)
> clear() 删除set中的所有元素,size置为0
> empty() 判断set是否为空,为空返回true,不为空返回false
> rbegin() 返回指向最后一个元素的迭代器
> rend() 返回指向第一个元素之前的理论元素的迭代器
> size() 返回set中元素的个数
> max_size() 返回set中可以容纳的最大元素个数,实际上可能并不一定可以容纳这么多
关于end()的解释:
我们来看看文档:
Returns an iterator referring to the past-the-end element in the set container.The past-the-end element is the theoretical element that would follow the last element in the set container.
大概是这么说的:end()返回一个指向the past-the-end元素的迭代器,the past-the-end元素是跟在最后一个元素后面的理论元素。
下面,我将being()、end(),rbegin()、rend()所对应的位置用一张图表示,希望对你有所帮助。
#include <iostream>
#include <set>
using namespace std;
int main()
{
int arr[] = { 1,5,9,2,7,3 };
set<int> st1(arr, arr + sizeof(arr) / sizeof(int));
cout << "第1个元素:" << *st1.begin() << endl;
cout << "最后一个元素:" << *(--st1.end()) << endl;
cout << "rbegin()返回值:" << *st1.rbegin() << endl;
cout << "当前元素个数:" << st1.size() << endl;
cout << "可容纳的最大元素个数:" << st1.max_size() << endl;
st1.clear();
cout << endl << "清楚所有元素后:" << endl;
cout << "当前元素个数:" << st1.size() << endl;
cout << "可容纳的最大元素个数:" << st1.max_size() << endl;
return 0;
}
运行结果:
第1个元素:1
最后一个元素:9
rbegin()返回值:9
当前元素个数:6
可容纳的最大元素个数:214748364
清楚所有元素后:
当前元素个数:0
可容纳的最大元素个数:214748364
count
#include <iostream>
#include <set>
using namespace std;
int main()
{
int arr[] = { 1,5,9,2,7,3 };
set<int> st1(arr, arr + sizeof(arr) / sizeof(int));
cout << "5出现的次数:" << st1.count(5) << endl;
cout << "8出现的次数:" << st1.count(8) << endl;
return 0;
}
运行结果:
5出现的次数:1
8出现的次数:0
在set中,因为每一个值都是唯一的,所以count的返回结果只有两种:0或1。根据它的返回值,有时候也可以用count来判断一个元素是否在set中。
insert
① pair<iterator,bool> insert (const value_type& val);
将val插入到set中。如果插入成功,返回指向插入位置的迭代器和true。如果插入失败,也就是说set中已经存在要插入的val,那么将返回指向val的迭代器和false。
② void insert (InputIterator first, InputIterator last);
将区间[first,last)中的元素插入到set中。
③ iterator insert (iterator position, const value_type& val);
指定位置插入元素。说是怎么说,但是实际上并不一定按照我们指定的位置插入,而是会遵循搜索树的规则进行插入。
#include <iostream>
#include <set>
using namespace std;
int main()
{
int arr[] = { 1, 3, 2, 6, 7, 9 };
set<int> st;
set<int>::iterator it = st.begin();
st.insert(it, 10); //指定位置插入
st.insert(arr, arr + sizeof(arr) / sizeof(int));//区间插入
st.insert(8); //单个值插入
cout << "set中的元素:";
for (auto val : st)
{
cout << val << " ";
}
cout << endl;
return 0;
}
运行结果:
set中的元素:1 2 3 6 7 8 9 10
erase
① iterator erase (const_iterator position); 删除指定位置的元素。
② size_type erase (const value_type& val); 删除指定元素,并返回删除的元素个数
③ iterator erase (const_iterator first, const_iterator last); 删除迭代器区间中的元素
#include <iostream>
#include <set>
using namespace std;
int main()
{
int arr[] = { 1, 5, 9, 2, 3, 8, 6, 0 };
set<int> st(arr, arr + sizeof(arr) / sizeof(int));
cout << "begin()指向的元素为:";
cout << *st.begin() << endl;
cout << "st.erase(st.begin())后剩余元素:";
st.erase(st.begin());//删除指定位置的元素
for (auto val : st)
cout << val << " ";
cout << endl;
cout << "st.erase(9)后剩余元素:";
st.erase(9);//删除指定元素
for (auto val : st)
cout << val << " ";
cout << endl;
set<int>::iterator first = st.begin();
set<int>::iterator second = first;
second++;
second++;
cout << endl;
cout << "first:" << *first << " " << "second:" << *second << endl;
cout << "st.erase(first, second)后剩余元素:";
st.erase(first, second);//删除迭代器区间内的元素
for (auto val : st)
cout << val << " ";
cout << endl;
return 0;
}
运行结果:
begin()指向的元素为:0
st.erase(st.begin())后剩余元素:1 2 3 5 6 8 9
st.erase(9)后剩余元素:1 2 3 5 6 8
first:1 second:3
st.erase(first, second)后剩余元素:3 5 6 8
find()
iterator find (const value_type& val);
如果找到指定值,则返回指定元素的迭代器;否则,返回值与end()的返回值一样,都是指向最后一个元素后面的理论元素。
以上就是关于set的介绍及使用,原本打算和map一块讲的,但是set占的篇幅远比想象的要多得多,继续讲map的话整体的篇幅就太长了,就先到这吧,下一篇再讲map。
完~