###前言:(序列式容器和关联式容器)
前面我们已经学习的部分的STL的容器,例如string、list、vecto、deque、array、forward_list。这些都是序列式容器,逻辑结构为线性序列,两个数据之间没用很强的关联性;
关联式容器 也是来存储数据的,它的逻辑结构是非线性序列的 ,两个数据之间的关联性很强 ,交换一下或者改变一下,那么他们之间的存储结构就被破坏了;关联式容器有map/set系列的和unordered_map和unordered_map系列的。
一、set
1、set类的介绍
set是存储数据的容器,它的存储数据的规则按照红黑树的规则;左子树的节点数据都小于根节点的数据,右子树的节点数据都大于根节点的数据大小;
声明:
cpp
template < class T, // set::key_type/value_type
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T> // set::allocator_type
>
class set;
- T是set底层的关键字类型;
- set默认支持的是小于的比较,less<T>,可以传greater<T>;
- set底层存储的数据的内存是从空间适配器中申请的,可以自己实现内存池,那么就传给第三个参数;
- 一般来说,我们不需要传后面的两个参数
- 底层是红黑树,增删查的时间复杂度是O(logN);
- 底层迭代器走的是平衡二叉搜索树的中序遍历,所以迭代器对应的数据是有序的,
2、set的构造和迭代器
set的迭代器是双向的迭代器,所以支持反向迭代器;
迭代器走的是中序,所以默认遍历按照升序顺序;
set的iterator和const_iterator都不错支持修改数据,因为修改数据可能会破坏底层搜索树的结构
###代码示例:
cpp
#include<iostream>
#include<set>
using namespace std;
void Print(set<int>& s)
{
for (auto i : s)
{
cout << i << " ";
}
cout << endl;
}
void test1()
{
//无参构造
set<int> s1;
Print(s1);
//迭代器区间构造
int arr[] = { 5,6,7,1,2,3,4 };
set<int> s2(arr, arr + 7);
set<int> s3(s2.begin(), s2.end());
Print(s2);
Print(s3);
//拷贝构造
set<int> s4(s3);
Print(s4);
//列表构造
set<int> s5({ 9,1,3,4,5,7,6,8,2 });
Print(s5);
}
cpp
void test2()
{
set<int> s({1,2,3,4,9,8,7,6,5});
//正向迭代器
set<int>::iterator i = s.begin();
while (i != s.end())
{
cout << *i << " ";
i++;
}
cout << endl;
//反向迭代器
set<int> ::reverse_iterator ri = s.rbegin();
while (ri != s.rend())
{
cout << *ri << " ";
ri++;
}
cout << endl;
}
3、set的增删查
1、插入insert
set不支持插入相同的值,若是插入相同的值,那么就插入失败;
插入接口原型声明:
// 单个数据插⼊,如果已经存在则插⼊失败
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
###代码示例:
cpp
void test3()//插入单个值
{
set<int> s1;
s1.insert(2);
s1.insert(3);
s1.insert(8);
s1.insert(1);
for (auto i : s1)
{
cout << i << ' ';
}
cout << endl;
//相同元素不插入
pair<set<int>::iterator,bool> ret =s1.insert(8);
for (auto i : s1)
{
cout << i << ' ';
}
cout << endl;
}
cpp
void test4()//插入一段列表
{
set<int> s;
for (int i = 10; i <= 100; i += 10)
{
s.insert(i);
}
for (auto i : s)
{
cout << i << ' ';
}
cout << endl;
s.insert({ 300,200,100,10 });
for (auto i : s)
{
cout << i << ' ';
}
cout << endl;
}
2、查找find
原型:
iterator find(const value_type& val)const;
若是查找成功则返回这个数据对应的迭代器;若是查找失败则返回end迭代器。
###代码示例:
cpp
void test5()
{
set<int> s;
for (int i = 10; i <= 100; i += 10)
{
s.insert(i);
}
for (auto i : s)
{
cout << i << ' ';
}
cout << endl;
//查找一个存在的值
set<int>::iterator pos1 = s.find(10);
if (pos1 != s.end())
cout << *pos1 << endl;
else
cout << "该数据不存在" << endl;
//查找一个不存在的值
set<int>::iterator pos2 = s.find(101);
if (pos2 != s.end())
cout << *pos2 << endl;
else
cout << "该数据不存在" << endl;
3、删除erase
原型:
1、void erase(iterator pos);
2、size_type erase(const value_type& val);
3、void erase(iterator first,iterator last);
- 第一个就是删除 迭代器指向位置的值;
- 第二个删除指定的值,返回类型size_type代表的是删除了多少个这个指定的值,set没有相同的值,那么就是删除成功返回1,反之返回0;
- 第三个删除指定迭代器区间的数据。
###代码示例:
cpp
void test6()
{
set<int> s = { 4,2,7,2,8,5,9 };
cout << "原set数据:" << endl;
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
// 删除最⼩值
cout << "删除最小值:" << endl;
s.erase(s.begin());//迭代按中序走,开始的就是最小的值
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
cout << "删除指定值x,输入x:" << endl;
// 直接删除x
int x;
cin >> x;
int num = s.erase(x);//指定一个值删除
if (num == 0)
{
cout << x << "不存在!" << endl;
}
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
cout << "删除迭代器区间的值:" << endl;
set<int> ::iterator first = s.begin();
set<int> ::iterator last = s.end();
s.erase(first, --last);//结果只剩下最后一个数据
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
4、set的lower_bound和upper_bound
原型
iterator lower_bound(const value_type& val)const;
iterator upper_bound(const value_type& val)const;
- 第一个是返回大于或者等于val值的迭代器;
- 第二个时返回大于val值的迭代器。
###代码示例:
cpp
void test7()
{
set<int> s;
for (int i = 10; i <= 100; i += 10)
{
s.insert(i);
}
for (auto i : s)
{
cout << i << ' ';
}
cout << endl;
set<int>::iterator it_low = s.lower_bound(20);
cout << *it_low << endl;
set<int>::iterator it_up = s.upper_bound(50);
cout << *it_up << endl;
//删除掉这两个迭代器之间的数据,左闭右开
s.erase(it_low, it_up);
for (auto i : s)
{
cout << i << ' ';
}
}
二、multiset
multiset和set几乎完全一致,只是multiset支持冗余,也就是支持存储相同的值。所以它的增删查也会随之发生小变化;
头文件和set相同
- insert:能插入相同的值;
- find:查找到要找的值,那就去它的左子树看看有无相同的值,若是有就返回左子树的节点迭代器,只到左子树中没有这个值为止;也就是中序中的第一个;
- erase:指定删除一个值,那么也会删除所有与之相同的值。
###具体代码:
cpp
void test8()
{
multiset<int> s1{ 4,2,7,2,4,8,4,5,4,9 };
for (auto i : s1)
{
cout << i << " ";
}
cout << endl;
cout << "输入需要查找的值:" << endl;
int x = 0; cin >> x;
multiset<int>::iterator i = s1.find(x);
if (i != s1.end())
cout <<"这个值存在:" << *i << endl;
else
cout << "该数据不存在" << endl;
//与set不同,multiset的count会返回实际个数
cout <<"数据个数:" << s1.count(4) << endl;
//erase会删除所有相同的值
s1.erase(4);
auto j = s1.begin();
while (j != s1.end())
{
cout << *j << " ";
j++;
}
}