目录
容器:
容器适配器:
heap——堆(隐式表达) #include<aglorithm>
标准之外:
vector ——顺序表。
概述:vector类似于array,但vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。
迭代器:vector的迭代器是Random Access Iterators,vector迭代器就是普通指针(typedef value_type * iterator)
扩容:当vector扩容时,会发生“重新配置(两倍大小),元素移动,释放空间”等过程。删除时容量不变。
元素操作:对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就会失效。
STL标准规范中指出:插入时,新节点将位于哨兵迭代器的所指节点的前方。
list ——环状双向链表
list的迭代器:双向迭代器,插入和结合不会造成原有的list迭代器失效。删除时只有“指向被删除元素”的迭代器失效。
list的元素操作:remove(const T& value) :移除所有数值为value的元素。
unique() :移除数值连续相同的连续元素,只有连续相同的元素才会被移除,剩下一个。
splice(iterator position,list&x):将x链表接合与position所指位置之前。x必须不同于*this。
transfer(iterator position,iterator first,iterator last):将[first,last)内的元素移动到position之前。(非公开接口)
merge(list& x ):将x合并到*this身上。两个list的内容必须先经过递增排序。
sort():list不能使用STL算法中的sort(),必须使用sort()成员函数。STL算法中的sort()要求随机访问迭代器。
关于sort函数的解析:https://blog.csdn.net/shoulinjun/article/details/19501811 (STL剖析上写得快排,实际上应该是归并排序)
#include<iostream>
#include<list>
using namespace std;
int main()
{
list<int> L = {1,2,2,2,3,4,4,3};
L.unique();
for (auto iter = L.begin(); iter != L.end(); iter++)
cout << *iter << " ";
cout << endl; //输出 1 2 3 4 3;
L.remove(3);
for (auto iter = L.begin(); iter != L.end(); iter++)
cout << *iter << " ";
cout << endl; //输出 1 2 4;
list<int> iList = { 5,6,7,8 };
auto iterPos = find(L.begin(), L.end(), 2);
L.splice(iterPos, iList);
for (auto iter = L.begin(); iter != L.end(); iter++)
cout << *iter << " "; //输出1 5 6 7 8 2 4;
cout << endl;
return 0;
}
deque——双开口数组
概述:deque是一种双开口的连续性线性空间。是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。
中控器:deque用一块连续的map(非STL中的map容器)作为主控。map是一块连续的空间,其中每个元素都是指针,指向一段连续的线性空间,成为缓冲区。缓冲区才是deque的储存空间主体。SGI STL中默认缓冲区大小是512比特。
迭代器:Random Access Iterator,但是不是普通指针。
迭代器中包含:T* cur; 指向当前缓冲区的现行元素。
T* first;指向缓冲区的头。
T* last;指向缓冲区的尾。
map_pointer node;指向map中的特定的节点。
stack——先进后出
概述:栈是一种先进后出的数据结构,它只有一个出口。允许新增元素、移除元素、取得最顶端元素。
SGI STL中:stack以deque作为缺省情况下的stack底部结构。由于stack以底部容器完成其所有工作,从而具有adapter的“修改某物接口,形成另一种风貌”的性质,因此,STL stack 往往不归类为container(容器),而是归类为container adapter(容器适配器)。
stack:不允许遍历,也不提供迭代器。
list也是一种双向开口的数据结构,且list具有stack所有需要的底层容器的函数。所以list也可以作为stack的底层容器。
template<calss T,class Sequence = deque<T> >
class stack{
//friend function
//typedef
protected:
Sequence c;
public:
bool empty() const{return c.empty();}
void pop(){ c.pop_back();}
//...
};
stack<int,list<int>> iStack;
queue——先进先出
概述:queue是一种先进先出的数据结构,它有两个出口。queue允许从顶端移除元素、从底端加入元素、取得最顶端元素。
SGI STL中:queue以deque作为缺省情况下的queue底部结构。由于queue以底部容器完成其所有工作,从而具有adapter的“修改某物接口,形成另一种风貌”的性质,因此,STL queue往往不归类为container(容器),而是归类为container adapter(容器适配器)
queue:不允许遍历,没有迭代器。
可以以list作为queue的底层容器。
heap——堆(隐式表达)
概述:heap不是STL容器组件,是priority queue的助手。优先队列允许用户以任何次序将任何元素推入容器内,但取出时一定是从优先权最高(数值最高的)的元素开始取。binary max heap就具有这样的特性。
binary heap是一种完全二叉树,也就是说,整棵binary tree除了最底层的叶节点之外,是填满的,而最底层的叶节点由左至右不得有空隙。因此:我们可以利用array来存储所有的节点。用array表述tree的方式,称为隐式表达(implicit representation)。
按照元素排列方式,heap可以分为max-heap和min-heap两种。前者每个节点的键值都大于或等于其子节点的键值,后者每个节点的键值都小于或等于其子节点的键值。因此max-heap的最大值在根节点,min-heap的最小值在根节点。STL中所提供的是max -heap。
heap算法:(在头文件<algorithm>中)
- push_heap算法: 执行一个上溯程序:将新节点的值和其父节点比较,如果键值比父节点大,就父子交换位置,如此一直上溯,直到不需要交换或直到根节点为止。
- pop_heap算法:将根节点取走,放入最下层最右边的叶节点,然后执行一个下溯程序(_adjust_heap):将新节点的值和其孩子节点比较,如果 键值比孩子节点的小,将其和较大的孩子节点对调,如此一直下溯,直至不需要交换或者到了叶子节点为止。
- sort_heap算法:持续对整个heap做pop_heap操作,每次将操作范围从后向前缩减一个元素。
- make_heap算法: 从n/2的节点开始执行下溯程序 直到节点0;
priority_queue——优先队列
priority_queue:拥有权值观念的queue。权值最高值排在最前面。缺省情况下以vector为底层容器,利用max-heap完成。是container adapter。没有迭代器。
template<class T,class Sequence = vector<T>,
class Compare = less<typename Sequence::value_type> >
class priority_queue
{
public:
//typedef
protected:
Sequence c; // 底层容器
Compare com;
public:
//member function
};
slist——单链表
SGI STL中提供单链表(single linked list),名为slist。它并不在标准规格内。
slist和list比较:
- 差别:slist的迭代器是单向的,list的迭代器的双向的,slist对的功能是受限的。但是slist所消耗的空间更小,某些操作更快。
- 相同:它们的insert、erase、splice等操作不会造成原有迭代器的失效。(除被移除的当前迭代器)
slist:是一个带头结点的链表。“头结点”位于链表的第一个元素之前,它的data域可以不存储任何信息。
看源码的时候产生的疑问:
1. uninitialized_fill 和 fill 的区别:
uninitialized_fill(未初始化填充) 是要根据value的类型来判断使用哪一种方式填充,uninitialized_fill一般都是用于未初化填充,也就是说内存区间根本没有对象可言.
fill是直接对每个元素进行填充value.如果是POD类型(就是内置类型或普通结构与类(没有指针的成员数据)),就直接调用fill函数.不是POD类型时,就要遍历每个元素进行构造(调用construct函数).那是因为*first = value; 类的赋值操作符必须要*first的对象已经生成.