一、关联式容器
我们已经了解了STL中的部分容器,比如vector、list、deque等,这些容器被称为序列式容器,其底层为线性序列的数据结构,里面存储的是元素本身。
关联式容器也是用来存储数据的,与序列式容器不同,关联式容器里面存储的是Key - Value结构的键值对,在数据检索时比序列式容器效率高。
二 、键值对
键值对是用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量Key和Value,Key代表键值, Value代表与Key对应的信息。
键值对在STL中被定义成一个pair类型,如下所示:
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)
{}
};
三、树状结构的关联式容器
STL中对关联式容器实现了两种不同结构的版本,以适应不同的场景:树状结构和哈希结构。这里主要介绍树状结构为底层的关联式容器。
树状结构的关联式容器主要有4种:map、set、multimap、multiset。
以上四种容器的共同点是:其底层都是使用**平衡二叉搜索树(即红黑树)**实现的,容器中的元素是一个有序的序列。
3.1 set
###3.1.1 set的使用
set文档介绍
- set是按照一定次序存储元素的容器。
- 在set中元素的Value就是Key,并且每个Value必须是唯一的。
- 在内部,set的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序的。
- set容器通过Key访问某个单个元素的效率通常会比unordered_set容器要慢,但是它允许按顺序对子集进行迭代。
- set的底层是由红黑树实现的。
注意:
- 与map/multimap不同,map/multimap中存储的是真正的键值对pair<Key, Value>,set中只放Value但是在底层存放的是<value, value>的键值对。
- set在插入元素的时候,只需要插入Value即可,不需要构造键值对。
- set中的元素不可以重复(可以用set进行去重)。
- 使用set的迭代器遍历set中的元素,可以得到有序序列。
- set中的元素默认按小于比较,也就是升序排列。
- set中查找某个元素的效率是O(logN)。
- set中的元素不允许被修改。
- set的底层是由二叉搜索树(红黑树)实现的。
set的迭代器走的是中序遍历,是一个双向迭代器,set:去重+排序。
3.1.2 set的使用
1.set的模版参数
以上就是set的模版参数,我们来看下第一个模版参数,T:set中存放的元素类型,在底层的存储实际上是<value, value> 的键值对。
第二个模版参数Compare:set中用于比较的仿函数,set中的元素默认按照小于进行比较。
第三个模版参数:set中元素空间的管理方式,使用STL提供的空间配置器进行管理。
2.set的构造函数
函数声明 | 功能介绍 |
---|---|
explicit set (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); | 构造空的set |
template set (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& = allocator_type()); | 使用[first, last)的迭代器区间中的元素进行构造set |
set (const set& x); set (const set& x, const allocator_type& alloc); | set的拷贝构造 |
void test_set()
{
set<int> s1;//构造一个空set
vector<int> v1;
for (int i = 10; i >= 0; i--)
{
v1.push_back(i);
}
set<int> s2(v1.begin(), v1.end());//迭代器区间构造
for (auto e : s2)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_set();
return 0;
}
3.set的迭代器
函数声明 | 功能介绍 |
---|---|
iterator begin() | 返回set起始位置元素的迭代器 |
iterator end() | 返回set最后一个元素后一个位置的迭代器 |
const_iterator cbegin() const | 返回set起始位置的const迭代器 |
const_iterator cend() const | 返回set最后一个元素的后一个位置的const迭代器 |
reverse_iterator rbegin() | 返回set的第一个反向迭代器,也就是最后一个元素的后一个位置的迭代器,相当于end() |
reverse_iterator rend() | 返回set的最后一个反向迭代器,也及时第一个元素位置的迭代器,相当于begin() |
const_reverse_iterator crbegin() const | 返回set的第一个反向const迭代器,也就是最后一个元素的后一个位置的const迭代器,相当于cend() |
const_reverse_iterator crend() const | 返回set的最后一个反向const迭代器,也及时第一个元素位置的const迭代器,相当于cbegin() |
void test_set1()
{
set<int> s1{ 1, 2, 3, 4, 5, 7, 0, 8 ,9 ,10, 12 };
set<int>::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
auto it1 = s1.rbegin();
while (it1 != s1.rend())
{
cout << *it1 << " ";
it1++;
}
cout << endl;
}
迭代器补充:
函数声明 | 函数功能 |
---|---|
iterator lower_bound(x) | 返回 >= x 的值的位置的迭代器(左边界) |
iterator upper_bound(y) | 返回 > y 的值的位置的迭代器 (右边界) |
void test_set2()
{
set<int> s1{ 1, 2, 3, 4, 5, 7, 0, 8 ,9 ,10, 12 };
set<int>::iterator it = s1.lower_bound(2);//左边界
set<int>::iterator it1 = s1.upper_bound(10);//右边界
//相当于输出2 - 10之间的数字
while (it != it1)
{
cout << *it << " ";
it++;
}
cout << endl;
}
4.set的容量接口
函数声明 | 功能介绍 |
---|---|
bool empty() const | 检测set是否为空,如果为空就返回true,否则返回false |
size_type size() const | 返回set中的有效元素个数 |
void test_set1()
{
set<int> s1{ 1, 2, 3, 4, 5, 7, 0, 8 ,9 ,10, 12 };
cout << s1.empty() << endl;
cout << s1.size() << endl;
}
5.set的修改操作接口
函数声明 | 功能介绍 |
---|---|
pair<iterator, bool> insert(const value_type& x) | 在set中插入元素x,实际插入的是<x, x>构成的键值对,如果插入成功,就返回<该元素在set中的位置的迭代器,true>, 如果插入失败,就代表x在set中已经存在,返回<x在se中的位置的迭代器,false> |
void erase(iterator pos) | 删除set中在pos位置的元素 |
size_type erase(const key_type& x) | 删除set中值为x的元素,返回删除的元素的个数 |
void erase(iterator first, iterator last) | 删除set中[first, last)区间中的元素 |
void swap(set<Key, Compare, Allocator>& st) | 交换两个set中的元素 |
void clear() | 将set中的元素清空 |
iterator find(const Key_type& x) const | 返回set中值为x的元素的位置 |
size_type count(const Key_type& x) const | 返回set中值为x的元素的个数 |
void test_set3()
{
set<int> s1;
s1.insert(1);
s1.insert(3);
s1.insert(1);
s1.insert(5);
s1.insert(7);
s1.insert(10);
s1.insert(11);
s1.insert(6);
s1.insert(12);
s1.insert(13);
s1.insert(19);
s1.insert(100);
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
s1.erase(100);//按值删除
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
s1.erase(s1.begin());//按位删除
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
s1.erase(s1.begin(), ++s1.begin());//按迭代器区间删除
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
auto it1 = s1.find(6);//查找值为位置返回值所在位置的迭代器
cout << *it1 << endl;
cout << s1.count(6) << endl;//返回值在set中出现的次数
set<int> s2{ 1, 2, 3, 4, 5 };
s1.swap(s2);//交换两个set中的元素
for (auto e : s1) cout << e << " ";
cout << endl;
for (auto e : s2) cout << e << " ";
cout << endl;
s1.clear();
cout << s1.empty();
cout << endl;
s2.clear();
cout << s2.empty();
cout << endl;
}
3.2 multiset
multiset文档介绍
multiset 大致与set相同,但是multi系列允许键值冗余,也就是说multiset中的数据可以重复。
multiset也可以走中序遍历进行排序,但是不能进行去重。
接口方面和set基本相同。这里就不在重复介绍了。
关于set的使用的介绍到这里就结束了。