1.vector 底层数据结构为数组 ,支持快速随机访问
2.list 底层数据结构为双向链表,支持快速增删
3.deque 底层数据结构为一个中央控制器和多个缓冲区,详细见STL源码剖析P146,支持首尾(中间不能)快速增删,也支持随机访问。deque是一个双端队列(double-ended queue),也是在堆中保存内容的.它的保存形式如下:
[堆1] --> [堆2] -->[堆3] --> …
每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品.
4.stack 底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时
5.queue 底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时(stack和queue其实是适配器,而不叫容器,因为是对容器的再封装)
6.priority_queue 的底层数据结构一般为vector为底层容器,堆heap为处理规则来管理底层容器实现
7.set 底层数据结构为红黑树,有序,不重复
8.multiset 底层数据结构为红黑树,有序,可重复
9.map 底层数据结构为红黑树,有序,不重复
10.multimap 底层数据结构为红黑树,有序,可重复
11.hash_set 底层数据结构为hash表,无序,不重复
12.hash_multiset 底层数据结构为hash表,无序,可重复
13.hash_map 底层数据结构为hash表,无序,不重复
14.hash_multimap 底层数据结构为hash表,无序,可重复
包装了一个char* ,尽量使用string而不是char*
查找(从头到尾方向):
str.find("abc",3); //查找"abc"第一次出现的位置,从位置3开始搜索
查找(从尾到头方向):
str.rfind("abc",9); //查找"abc"最后一次出现的位置,从9开始查找str最后一次出现的地方,
注意位置是向前移动!
访问:
使用[]访问出界会使程序down掉,而使用at成员函数访问出界则是抛异常out_of_range
拼接:
str += "abc";
str.append("def");
替换:
str.replace(0,2,"abcd"); //将0开始的2个字符替换为"abcd"
比较:
str.compare("abcd");
子串:
str1 = str.substr(0,5); //返回由0开始的5个字符组成的字符串
插入:
str.insert(2,"abc"); //从2位置插入"abc"
删除:
str.erase(0,5); //删除从0开始的5个字符
定义:
vector vec;
int arr[] = {1,2,3,4};
vector v(arr,arr+sizeof(arr)/sizeof(int)); //原型是vector(v.begin(), v.end())
插入元素:
vec.push_back(element);
vec.insert(iterator, element);
删除元素:
vec.pop_back();
vec.erase(iterator); //删除操作,返回下一个元素的迭代器
赋值:
vec.assign(v1.begin(), v1.end());
v2 = vec;
改变元素的值(注意,是改变!不是初始化):
vec[i] = j;
遍历容器:
for(auto it = vec.begin(); it != vec.end(); ++it) {......}
取元素:
vec.front();
vec.top();
vec.back();
其他:
vec.empty(); //判断是否空
vec.size(); // 实际元素
vec.capacity(); // 容器容量
vec.begin(); // 获得首迭代器
vec.end(); // 获得尾迭代器
vec.clear(); // 清空
vec.resize(10,0); //改变动态数组的大小为10个,多出来的用0进行初始化,注意缩小空间时是
不改变容量的!增加空间时,容量和大小都增大到同一个值
vec.swap(v1); //将v1、vec的元素交换,注意这里改变的是数据指针的指向
**巧用swap进行内存收缩**
vector(n).swap(n); //这里我们用n初始化一个匿名对象,然后将匿名对象的数据
与n的数据进行交换,匿名对象在执行完之后会自动销毁,实现n的内存收缩
**reserve成员函数(预留空间)**
如果你知道容器大概要存储的元素个数,建议使用reserve预留空间,减少数据的拷贝次数
- reserver与resized的区别
- reserve是容器预留空间,但在空间内不真正创建元素对象(没有初始化、访问
不到),所以在没有添加新的对象之前,不能引用容器内的元素
- resize是改变容器的大小,且在重建对象,因此在调用这个函数之后就可以
引用容器内的元素了
线性表,数组实现,动态数组,单口容器。
支持随机访问。
插入删除操作需要大量移动数据。
需要连续的物理存储空间。
每当大小不够时,重新分配内存(*2)(1~2倍时间复杂度比较低),并复制原内容,事实上,vector的增长倍数是会变化的。
插入元素
尾后插入:size < capacity时,首迭代器不失效尾迭代失效(未重新分配空间),size == capacity时,所有迭代器均失效(需要重新分配空间)。
中间插入:size < capacity时,首迭代器不失效但插入元素之后的所有迭代器失效,size == capacity时,所有迭代器均失效。
删除元素
尾后删除:只有尾迭代失效。
中间删除:删除位置之后的所有迭代失效。
vector是个动态数组,当空间不足的时候插入新元素,vector会重新申请一块更大的内存空间,将旧空间数据拷贝到新空间,然后释放旧空间。
vector是单口容器,所以在尾端插入和删除元素效率较高,在指定位置插入,势必会引起数据元素移动,效率较低。
vector 常用来保存需要经常进行随机访问的内容,并且不需要经常对中间元素进行添加删除操作。
如果你需要高效的随机存取,而不在乎插入和删除的效率,使用 vector。
deque 是一种双向开口的连续线性空间,元素也是在堆中。所谓双向开口,意思是可以在队尾两端分别做元素的插入和删除操作。deque 和 vector 的最大差异,一在于 deque 允许于常数时间内对起头端进行元素的插入或移除操作,二在于deque没有所谓容量观念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接在一起。换句话说,像 vector 那样“因旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在 deque 是不会发生的。它的保存形式如下:
[堆1] --> [堆2] -->[堆3] --> …
deque 是由一段一段的定量连续空间构成。一旦有必要在 deque 的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个 deque 的头端或尾端。deque 的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。避开了“重新配置,复制,释放”的轮回,代价则是复杂的迭代器架构。因为有分段连续线性空间,就必须有中央控制器,而为了维持整体连续的假象,数据结构的设计及迭代器前进后退等操作都颇为繁琐。
deque 采用一块所谓的 map 作为主控。这里的 map 是一小块连续空间,其中每个元素都是指针,指向另一段连续线性空间,称为缓冲区。缓冲区才是 deque 的存储空间主体。( 底层数据结构为一个中央控制器和多个缓冲区)SGI STL 允许我们指定缓冲区大小,默认值 0 表示将使用 512 bytes 缓冲区。
定义:
deque d;
deque d2(d1.begin(), d1.end())
deque d(10,5); //初始化为10个5
插入元素:
d.push_back(element);
d.push_front(element);
d.insert(iterator, element);
删除元素:
d.pop_back();
d.pop_front();
赋值:
d.assign(d1.begin(), d1.end());
d2 = d;
d.swap(d1);
遍历容器:
for(auto it = d.begin(); it != d.end(); ++it) {......}
取元素:
d.front();
d.back();
d.at(0);
d[0];
其他:
d.empty(); //判断是否空
d.size(); // 实际元素
d.resize(10,0); // 重新指定容器的长度为10,若容器变长,则以0填充新位置,如果变短,
则末尾超出容器长度的元素被删除
d.begin(); // 获得首迭代器
d.end(); // 获得尾迭代器
d.clear(); // 清空
**补充:**
sort(d.begin(), d.end(), mycompare); //algorithm下的排序函数,默认
是从小到大排序,可以提供一个函数(比较规则)
//这里的sort只能对能够进行随机访问的容器使用,因此list容器是不能使用的,
list有自己的排序函数
deque 在开始和最后添加元素都一样快,并提供了随机访问方法,像vector一样使用 [] 访问任意元素,但是随机访问速度比不上vector快,因为它要内部处理堆跳转。
deque 也有保留空间。另外,由于 deque 不要求连续空间,所以可以保存的元素比 vector 更大,这点也要注意一下。还有就是在前面和后面添加元素时都不需要移动其它块的元素,所以性能也很高。
如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
stack 是一种先进后出(First In Last Out , FILO)的数据结构。它只有一个出口,stack 允许新增元素,移除元素,取得最顶端元素。但除了最顶端外,没有任何其它方法可以存取stack的其它元素,stack不允许遍历行为,没有迭代器。s.top()、s.push()、s.pop()
以某种容器( 一般用 list 或 deque 实现,封闭头部即可,不用 vector 的原因应该是容量大小有限制,扩容耗时)作为底部结构,将其接口改变,使之符合“先进后出”的特性,形成一个 stack,是很容易做到的。deque 是双向开口的数据结构,若以 deque 为底部结构并封闭其头端开口,便轻而易举地形成了一个stack。因此,SGI STL 便以 deque 作为缺省情况下的 stack 底部结构,由于 stack 系以底部容器完成其所有工作,而具有这种“修改某物接口,形成另一种风貌”之性质者,称为 adapter(配接器),因此,STL stack 往往不被归类为 container(容器),而被归类为 container adapter(容器配接器)。
queue 是一种先进先出(First In First Out,FIFO)的数据结构。它有两个出口,queue 允许新增元素,移除元素,从最底端加入元素,取得最顶端元素。但除了最底端可以加入,最顶端可以取出外,
没有任何其它方法可以存取 queue 的其它元素。它与stack一样不支持遍历行为。q.front()、q.back()、q.push()、q.pop()
以某种容器( 一般用 list 或 deque 实现,封闭头部即可,不用 vector 的原因应该是容量大小有限制,扩容耗时)作为底部结构,将其接口改变,使之符合“先进先出”的特性,形成一个 queue,是很容易做到的。deque 是双向开口的数据结构,若以 deque 为底部结构并封闭其底部的出口和前端的入口,便轻而易举地形成了一个 queue。因此,SGI STL 便以 deque 作为缺省情况下的 queue 底部结构,由于 queue 系以底部容器完成其所有工作,而具有这种“修改某物接口,形成另一种风貌”之性质者,称为 adapter(配接器),因此,STL queue 往往不被归类为container(容器),而被归类为 container adapter。
stack 和 queue 其实是适配器,而不叫容器,因为是对容器的再封装。
相对于 vector 的连续空间,list 就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间,元素也是在堆中。因此,list 对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素移除,永远是常数时间。STL 中的list 底层是一个双向链表,而且是一个环状双向链表。这个特点使得它的随即存取变的非常没有效率,因此它没有提供 [] 操作符的重载,即它是不支持随机存取的。
大部分函数与之前的一样,这里就列举几个比较特殊的:
删除匹配的元素:
l.remove(10); //删除容器中所有与10匹配的元素
list反转:
l.reverse(); //反转链表,比如l包含1,3,5元素,反转后就变成5,3,1了
list排序:
l.sort(); //list排序,默认从小到大,一样可以传递函数(排序规则),
如l.sort(mycompare);
如果你喜欢经常添加删除大对象的话,那么请使用 list;
要保存的对象不大,构造与析构操作不复杂,那么可以使用 vector 代替。
list<指针> 完全是性能最低的做法,这种情况下还是使用 vector<指针> 好,因为指针没有构造与析构,也不占用很大内存
STL中并没有把heap作为一种容器组件,heap的实现亦需要更低一层的容器组件(诸如list,array,vector)作为其底层机制。Heap是一个类属算法,包含在algorithm头文件中。虽然STL中关于heap默认调整成的是大顶堆,但却可以让用户利用自定义的compare_fuction函数实现大顶堆或小顶堆。heap的低层机制vector本身就是一个类模板,heap基于vector便实现了对各种数据类型(无论基本数据类型还是用户自定义的数据类型)的堆排(前提是用户自定义的数据类型要提供比较机制compare_fuction函数)。
STL里面的堆操作一般用到的只有4个。他们就是:
make_heap();、pop_heap();、push_heap();、sort_heap();他们的头函数是algorithm
建立堆:void make_heap(first_pointer,end_pointer,compare_function);
一个参数是数组或向量的头指针,第二个向量是尾指针。第三个参数是比较函数的名字。
在缺省的时候,默认是大根堆。(下面的参数都一样就不解释了)
作用:把这一段的数组或向量做成一个堆的结构。范围是(first,last),这里的end是
最后一个元素的下一个位置。
弹出元素:void pop_heap(first_pointer,end_pointer,compare_function);
作用:pop_heap()不是真的把最大(最小)的元素从堆中弹出来。而是重新排序堆。
它把first和last交换,然后将[first,last-1)的数据再做成一个堆。
放入元素:void push_heap(first_pointer,end_pointer,compare_function);
作用:push_heap()假设由[first,last-1)是一个有效的堆,然后,再把堆中的新元
素加进来,做成一个堆。
对堆进行排序:void sort_heap(first_pointer,end_pointer,compare_function);
作用是sort_heap对[first,last)中的序列进行排序。它假设这个序列是有效堆。
(当然,经过排序之后就不是一个有效堆了)
priority_queue 优先队列,其底层是用堆来实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。在优先队列中,没有 front() 函数与 back() 函数,而只能通过 top() 函数来访问队首元素(也可称为堆顶元素),也就是优先级最高的元素。基本操作有:
empty() 如果队列为空返回真
pop() 删除对顶元素
push() 加入一个元素
size() 返回优先队列中拥有的元素个数
top() 返回优先队列对顶元素
priority_queue 默认为大顶堆,即堆顶元素为堆中最大元素(比如:在默认的int型中先出队的为较大的数)。基本数据类型(int,double,char等可以直接使用的数据类型),优先队列对他们的优先级设置一般是数字大的优先级高,因此队首元素就是优先队列内元素最大的那个(如果是 char 型,则是字典序最大的)。
如果我们想要用小顶堆的话需要增加使用两个参数:
priority_queue< int, vector, greater > q; // 小顶堆
priority_queue< int, vector, less > q; // 大顶堆 从大到小
//基本数据类型我们可以直接使用greater和less,自己的一些类型可以传递一个自己的
比较仿函数进去
下面两种优先队列的定义是等价的:
priority_queue< int > q;
priority_queue< int,vector,less >;
其中第二个参数( vector ),是来承载底层数据结构堆的容器,第三个参数( less ),则是
一个比较类,less 表示数字大的优先级高,而 greater 表示数字小的优先级高。