C++ STL常见容器(vector list deque 顺序容器篇)
c++中有两种类型的容器:顺序容器和关联容器。
顺序容器主要有:vector、list、deque等。其中vector表示一段连续的内存地址,基于数组的实现,list表示非连续的内存,基于链表实现。deque与vector十分相似,采用dynamic array来管理元素,提供随机访问,但是deque的dynamic array头尾两端都开放,可以在头尾两端快速安插和删除。
关联容器主要有map和set等。map是key-value形式的,set是单值,只保存key,且map和set都会根据关键字自动排序,因为底层实现都是红黑树(RB-tree),红黑树是平衡二叉搜索树,两者皆是根据key来排序,自然也就不允许有重复的key(multimap multiset允许可重复key),也不允许修改key值。
除此之外,实际上还有三个容器适配器:stack,queue和priority_queue。stack和queue基于deque实现,priority_queue基于vector实现。适配器会对容器的接口进行重新封装,这样做一方面可以通过改装实现一些特定属性,例如stack的先进后出,就是将deque两端开放封装成一端开放,即隐藏deque的部分接口(push_front,pop_front等),另一方面其实正如适配器的本身的意义一样,提高容器的适用度,容器适配器stack或者queue都是常用的数据结构,具备基本的pop push等功能。
容器类自动申请和释放内存,我们无需new和delete操作
vector
记得我第一次见到这个容器时,我们管它叫做“向量”,也确实这个容器就像是向量一样可以存储多种数据类型的数据,且如同向量一样可以增长,实际上它的底层实现是数组,之所以可以做到增大,是因为在新增数据的时候,若数组满了,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,在插入新增的元素。
下面写了几个,用法比较简单,但是如果想了解更多可以查看它的源码,里面很多地方的实现非常简洁巧妙。
#include <iostream> #include <vector> using namespace std; int main() { vector<int> vec; //vector():创建一个空vector vector<int> vec2(10); //vector(int nSize):创建一个vector,元素个数为nSize vector<int> vec3(10,1); //vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t vector<int> vec4(vec3); //vector(const vector&):复制构造函数 int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; vector<int> vec5(a,a+10); //vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中 //1.push_back 在数组的最后添加一个数据 //2.pop_back 去掉数组的最后一个数据 vec5.at(5); //3.at 得到编号位置的数据 //for(vector<int>::iterator iter = vec5.begin(); iter != vec5.end();iter++) for(auto iter = vec5.begin(); iter != vec5.end();iter++) cout<<*iter<<" "; cout<<endl; //4.begin 得到数组头的指针 //5.end 得到数组的最后一个单元+1的指针 cout<<vec5.front()<<endl; //6.front 得到数组头的引用 cout<<vec5.back()<<endl; //7.back 得到数组的最后一个单元的引用 //8.max_size 得到vector最大可以是多大 cout<<vec5.capacity()<<endl; //9.capacity 当前vector分配的大小 //10.size 当前使用数据的大小 //11.resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值 //12.reserve 改变当前vecotr所分配空间的大小 auto ite = vec5.begin();//ite指向begin advance(ite, 3);//ite向右移动3位,指向begin+3,即3 ite = vec5.erase(ite);//begin+3被删除后,其后的数组向前平移一位,ite依然指向begin+3,但此时是数字4 //13.erase 删除指针指向的数据项 //14.clear 清空当前的vector //15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1) //16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1) cout<<vec5.empty()<<endl; //17.empty 判断vector是否为空 vec2.swap(vec5); //18.swap 与另一个vector交换数据 //19.insert 插入数据,向__position前插入__x,具体细节就不展开了,看参数命名大概能想到 /** * insert(const_iterator __position, const value_type& __x) insert(const_iterator __position, value_type&& __x) insert(const_iterator __position, initializer_list<value_type> __l) insert(const_iterator __position, size_type __n, const value_type& __x) insert(const_iterator __position, _InputIterator __first, _InputIterator __last) */ return 0; }
list
list 容器是由双向链表实现的。所以不能实现快速随机访问,具体表现为不能通过at()和[ ]访问。
记一下list几个特殊操作。
函数名 | 描述 |
---|---|
push_front | 将元素插入开头 |
push_back | 将元素插入结尾 |
pop_front | 删除开头元素 |
pop_front | 删除结尾元素 |
splice | 合并两个链表 |
merge | 合并两个排序链表,若两个链表有序合并后仍有序 |
unique | 删除链表内重复的元素 |
remove | 删除链表内指定的元素 |
remove_if | 删除满足条件的元素 |
sort | 将链表排序 |
reverse | 将链表反序 |
#include <iostream> #include <list> using namespace std; // a predicate implemented as a function: bool single_digit (const int& value) { return (value<10); } int main() { list<int> lis(10,13); int arr[5] = {9, 18, 5, 3, 11}; list<int> lis2(arr, arr+5); lis.sort(); lis2.sort(); lis.merge(lis2); // 在__position之前插入另一个链表 // lis.splice(lis.begin(), lis2); // lis.unique(); // lis.sort(); lis.remove(9);//去掉9 lis.remove_if(single_digit);//去掉小于10的数字 // 简单的说就是将链表内的元素依次经过_Predicate检查,若返回真则去掉该元素 // 当然,实际上这个函数可以类似于python中map函数那样使用,对链表内的元素依次加工,但并不推荐这样做 /** * lis.remove_if(_Predicate); * * @brief Remove all elements satisfying a predicate. * @tparam _Predicate Unary predicate function or object. * * Removes every element in the list for which the predicate * returns true. Remaining elements stay in list order. Note * that this function only erases the elements, and that if the * elements themselves are pointers, the pointed-to memory is * not touched in any way. Managing the pointer is the user's * responsibility. */ for(auto iter = lis.begin(); iter != lis.end();iter++) cout<<*iter<<" "; return 0; }
deque
deque最大的特点就是两端开放,这一点和list相似,但是内部的实现是截然不同的,deque底层数据结构是分段数组,具体为:
deque由一些独立的区块组成,第一区块朝某方向扩展,最后一个区块朝另一方向扩展。它允许较为快速地随机访问但它不像vector一样把所有对象保存在一个连续的内存块,而是多个连续的内存块,并且维护护一个存放这些数组首地址的索引数组,如下图所示:
由图可知,实际上deque的随机访问效率会低于vector,因为vector是连续的,而deque是分段的。deque不像vector那样当内存不足时需要数据转移,deque会申请新的分段存储新的数据,并将新的分段首地址放入索引数组内。
deque的各项操作只有一下两点和vector不同:
deque不提供容量操作:capacity()和reverse()。
deque直接提供函数完成首尾元素的插入和删除。
其他均与vector相同。
如果想了解更全面的C++STL方面的知识可以点击链接。cplusplus参考手册
- 点赞
- 收藏
- 分享
- 文章举报
- C++的STL容器之顺序性容器vector、list、deque
- c++几种常见STL容器比较和分析 hashmap, map, vector, list
- C++基础与技巧【顺序容器】 (三大顺序容器~vector, list, deque)
- STL顺序容器vector,list和deque
- C++顺序容器vector、deque、list
- stl 顺序容器vector(priority_queue),顺序容器List,顺序容器deque(queue, stack)详解
- C++顺序容器vector、deque、list
- 【STL】STL之顺序容器:vector、deque、list、forward_list、array、string
- STL 笔记(一) 顺序容器 vector、list、deque常用函数
- Effective C++ (8) 顺序容器vector,list,deque
- C++容器-2顺序容器vector、list、deque的使用
- C++ 9.1 顺序容器-----vector、list、deque
- C++ STL 容器(vector,list,deque,stack,queue,priority_queue,set,map)
- C++ STL基本容器string,vector,list,deque,map..[转]
- stl 顺序容器vector(priority_queue),顺序容器List,顺序容器deque(queue, stack)详解
- C++语法基础--顺序容器(一)--vector,list,deque
- STL顺序容器【vector】【deque】【list】
- C++ STL 提供了3个序列容器 :vector, deque, list
- C++顺序容器vector,deque,list
- C++中防止STL中迭代器失效——map/set等关联容器——vector/list/deque等序列容器—如何防止迭代器失效—即erase()的使用