## 二、STL容器II

### 2.1 queue 容器

#### 2.1.1 queue概念

> Queue 是一种先进先出(First In First Out,FIFO)的数据结构，它有两个出口，queue
> 容器允许从一端新增元素，从另一端移除元素。

![image-20230804074147635](./readme.assets/image-20230804074147635.png)

#### 2.1.2 queue 没有迭代器

Queue 所有元素的进出都必须符合”先进先出”的条件，只有 queue 的顶端元素，才
有机会被外界取用。Queue 不提供遍历功能，也不提供迭代器。



#### 2.1.3 常用API

##### 2.1.3.1 构造函数

```c++
queue<T> queT;  //queue 采用模板类实现，queue 对象的默认构造形式：
queue(const queue &que);//拷贝构造函数
```



##### 2.1.3.2 存取、插入和删除

```c++
void push(elem);   //往队尾添加元素
void pop();        //从队头移除第一个元素
T back();       //返回最后一个元素
T front();      //返回第一个元素
```



##### 2.1.3.3 赋值与大小

```c++
queue& operator=(const queue &que);//重载等号操作符

empty();//判断队列是否为空
size();//返回队列的大小
```



### 2.2 list 容器

#### 2.2.1 list 概念

list(链表)是一种物理存储单元上非连续、非顺序的存储结构，数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表由一系列结点（链表中每一个元素称为结点）组成，结点可以在运行时动态生成。

每个结点包括两个部分：

```
1）存储数据元素的数据域，
2）存储下一个结点地址的指针域
```

list与vector的比较：

```
1） 相对于vector 的连续线性空间，list 就显得负责许多，每次插入或者删除一个元素，就是配置或者释放一个元素的空间。不浪费多余的空间，且插入与移除元素的操作是常数时间（稳定）。
2）list和vector 是两个最常被使用的容器, 但list是由双向链表实现的。
3）list插入操作和删除操作都不会造成原有 list 迭代器的失效。 【重要特性】
```

<img src="./readme.assets/image-20230804075335829.png" width="400">

list特点：

```
1）采用动态存储分配，不会造成内存浪费和溢出
2）链表执行插入和删除操作十分方便，修改指针即可，不需要移动大量元素
3）链表灵活，但是空间和时间额外耗费较大
```

#### 2.2.2 list 的迭代器

List 不能像 vector 一样以普通指针作为迭代器，因为其节点不能保证在同一块连续的内存空间上。。List 迭代器必须有能力指向 list 的节点，并有能力进行正确的递增、递减、取值、成员存取操作。**递增**时指向下一个节点，**递减**时指向上一个节点，**取值**时取的是节点的数据值，**成员取用**时取的是节点的成员。

另外，list 是一个双向链表，迭代器必须能够具备前移、后移的能力，所以 list 容器提供的是 Bidirectional Iterators.（双向的迭代器）。

 List 有一个重要的性质，插入操作和删除操作都不会造成原有 list 迭代器的失效。

<font color=red>【注意】list的迭代器，不支持`+n`操作。</font>

#### 2.2.3 list 数据结构

> list 容器不仅是一个双向链表，而且还是一个循环的双向链表。

#### 2.2.4 常用API

##### 2.2.4.1 构造函数

```c++
list<T> lstT;//list 采用采用模板类实现,对象的默认构造形式：
list(beg,end);//构造函数将[beg, end)区间中的元素拷贝给本身。
list(n,elem);//构造函数将 n 个 elem 拷贝给本身。
list(const list &lst);//拷贝构造函数
```



##### 2.2.4.2 插入和删除

```c++
push_back(elem);//在容器尾部加入一个元素
pop_back();//删除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front();//从容器开头移除第一个元素
insert(pos,elem);//在 pos 位置插 elem 元素的拷贝，返回新数据的位置。
insert(pos,n,elem);//在 pos 位置插入 n 个 elem 数据，无返回值。
insert(pos,beg,end);//在 pos 位置插入[beg,end)区间的数据，无返回值。
clear();//移除容器的所有数据

erase(beg,end);//删除[beg,end)区间的数据，返回下一个数据的位置。
erase(pos);//删除 pos 位置的数据，返回下一个数据的位置。
remove(elem);//删除容器中所有与 elem 值匹配的元素
```

##### 2.2.4.3 大小

```c++
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(num);//重新指定容器的长度为 num, 若容器变长，则以默认值填充新位置。如果容器变短，则末尾超出容器长度的元素被删除
resize(num, elem); 
```

##### 2.2.4.4 赋值

```c++
assign(beg, end);   //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);   //将 n 个 elem 拷贝赋值给本身。
list& operator=(const list &lst);  //重载等号操作符
swap(lst);   //将 lst 与本身的元素互换。
```

##### 2.2.4.5 读取

```
front();//返回第一个元素。
back();//返回最后一个元素
```

##### 2.2.4.6 反转和排序

```
reverse();   //反转链表
sort();     //list 排序
```



### 2.3 set/multiset 容器

#### 2.3.1 set概念

set 的特性是所有元素都会根据元素的键值自动被排序。

set 的元素即是键值又是实值， 不允许两个元素有相同的键值。

set 的 iterator 是一种 const_iterator， 不允许修改set的键值。

set 拥有和 list 某些相同的性质，当对容器中的元素进行插入操作或者删除操作的
时候，操作之前的迭代器，在操作完成之后依然有效，被删除的那个元素的迭代器必然是一个例外。

#### 2.3.2 set数据结构

multiset 特性及用法和 set 完全相同，唯一的差别在于它允许键值重复。set 和multiset 的底层实现是红黑树，红黑树为平衡二叉树的一种。

二叉树就是任何节点最多只允许有两个字节点。分别是左子结点和右子节点：

<img src="./readme.assets/image-20230804083406386.png" width="300">

二叉搜索树，是指二叉树中的节点按照一定的规则进行排序，使得对二叉树中元素访问更加高效:

<img src="./readme.assets/image-20230804083459911.png" width=400>

二叉搜索树的放置规则是：

任何节点的元素值一定大于其左子树中的每一个节点的元素值，并且小于其右子树的值。因此从根节点一直向左走，一直到无路可走，即得到最小值，一直向右走，直至无路可走，可得到最大值。那么在二叉搜索树中找到最大元素和最小元素是非常简单的事情。

如上图所示：那么当一个二叉搜索树的左子树和右子树不平衡的时候，那么搜索依据上图表示，搜索 9 所花费的时间要比搜索 17 所花费的时间要多，由于我们的输入或者经过我们插入或者删除操作，二叉树失去平衡，造成搜索效率降低。

<img src="./readme.assets/image-20230804083751044.png" width=400>

#### 2.3.3 常用API

##### 2.3.3.1 构造函数

```c++
set<T> st;//set 默认构造函数：
mulitset<T> mst; //multiset 默认构造函数:
set(const set &st);//拷贝构造函数
```

##### 2.3.3.2 赋值和大小

```c++
set& operator=(const set &st);//重载等号操作符
swap(st);//交换两个集合容器

size();//返回容器中元素的数目
empty();//判断容器是否为空
```



##### 2.3.3.3 插入和删除

```
insert(elem);    //在容器中插入元素。
clear();        //清除所有元素
erase(pos);     //删除 pos 迭代器所指的元素，返回下一个元素的迭代器。
erase(beg, end);  //删除区间[beg,end)的所有元素 ，返回下一个元素的迭代器。
erase(elem);     //删除容器中值为 elem 的元素。
```

##### 2.3.3.4 查找

```c++
find(key);  //查找键 key 是否存在,若存在，返回该键的元素的迭代器；若不存在，返
回 set.end();
count(key);//查找键 key 的元素个数
lower_bound(keyElem);//返回第一个 key>=keyElem 元素的迭代器。
upper_bound(keyElem);//返回第一个 key>keyElem 元素的迭代器。
equal_range(keyElem);//返回容器中 key 与 keyElem 相等的上下限的两个迭代器。
```

##### 2.3.3.5 set 排序规则

> set自动排序， 但可以改变它的排序规则，默认从小到大。

自定义排序规则，可以使用struct或class, 声明 `bool operator(v1, v2)`仿函数重载。



#### 2.3.4 对组(pair)

对组(pair)将一对值组合成一个值，这一对值可以具有不同的数据类型，两个值可
以分别用 pair 的两个公有属性 first 和 second 访问。

```
类模板：template <class T1, class T2> struct pair
```

用法一：

```c++
pair<string, int> pair1(string("name"), 20);
cout << pair1.first << endl; 
cout << pair1.second << endl;
```

用法二：

```c++
pair<string, int> pair2 = make_pair("name", 30);
cout << pair2.first << endl;
cout << pair2.second << endl;
```

用法三：

```c++
pair<string, int> pair3 = pair2;
cout << pair3.first << endl;
cout << pair3.second << endl;
```



### 2.4 map/multimap 容器

Map 的特性是所有元素都会根据元素的键值自动排序。

Map 所有的元素都是pair,同时拥有实值和键值，pair 的第一元素被视为键值，第二元素被视为实值，map 不允许两个元素有相同的键值。

multimap 和 map 的操作类似，唯一区别 multimap 键值可重复。
map 和 multimap 都是以红黑树为底层实现机制。