序列式容器
STL序列式容器
序列式容器,即以线性排列来存储某一制定类型的数据,该类容器并不会自动对存储元素按照值的大小进行排序。序列式容器大致包括array,vector, deque,list,forward_list等,除此之外,stack和queue本质上也属于序列容器,不过是在deque的基础上形成,故更习惯称他们为容器适配器。
array
array是C++11标准中新增的序列容器,在普通数组的基础上添加了一些成员函数和全局函数。array容器的大小是固定的,无法动态的扩展或收缩。
初始化array容器:
//array容器以类模板的形式定义在<array>头文件中,位于命名空间std中,使用该容器需要引入<array>头文件,并默认使用std命名空间 #include <array> using namespace std; //array<T,N>类模板中,T用以指明容器中的存储的具体数据类型,N用于指明容器的大小,需要注意的是N必须是常量。 //初始化操作 array<double,10> values;//各元素值不确定,不会默认初始化 array<double,10> values{};//容器元素被默认初始化为0 array<double,10> values{1.0,2.0};//剩余八个元素被默认初始化为 0.0
array容器中包含at()这种成员函数,使得操作元素比普通数组更安全。
用法示例:
#include <iostream> #include <array> using namespace std; int main() { std::array<int,4> values{}; //初始化容器为{0,1,2,3} for(int i = 0; i<values.size();i++){ values.at(i) = i; } //使用get()重载函数输出指定位置元素 cout<< get<3>(values)<<endl; //cout<<values.at(3)<<endl; //如果容器不为空,输出容器内所有元素 if(!values.empty()){ for(auto val = values.begin();val<values.end();val++){ cout<< *val <<" "<<endl; } } //三种访问元素的方式 cout<< "values[0] is: "<< values[0]<<endl; cout<< "values[1] is: "<< values.at(1)<<endl; cout<< "values[2] is: "<< get<2>(values)<<endl; return 0; }
访问array容器内单个元素:
容器名[]:没有做边界检查(性能开销),使用越界的索引去访问或者存储元素不会被检测到
容器名.at(索引):可确定索引是否越界
vector
vector 实现的是一个动态数组,即可以进行元素的插入或者删除,动态占用内存空间。在尾部插入或者删除元素时间复杂度为O(1),在容器头部或者中部插入或删除元素时间复杂度为线性阶O(n).vector 容器以类模板vector
#include <iostream> using namespace std; //创建空的vector容器 vector<double> values;//空的vector容器 values.reserve(20);//设置容器内存分配,至少容纳20个元素,不影响已存储的元素也不会生成任何元素 //创建时指定初始值和元素个数 vector<int> primes{2,3,4,5}; //创建时指定元素个数,但初始值默认为0 vector<int> primes(20); //创建时指定元素个数和初始值 vector<double> values(20,1.0);//20个元素的值均为1.0 //复制其他容器元素 vector<int> values1(10,1); vector<int> values2(values1); vector<int> values2(values.begin(),values.begin()+2);
vector一些成员函数的用法:
#include <iostream> #include <vector> using namespace std; int main(){ //初始化一个空的vector 容器 vector<char> value; //向value容器尾部添加s,t,l value.push_back('s'); value.push_back('t'); value.push_back('l'); //调用size()成员函数输出容器的元素个数 printf("元素个数为:%d\n",value.size()); //使用迭代器遍历容器 for(auto it = value.begin();it!=value.end();it++){ cout<<*it<<" "; } cout << endl; //向容器开头插入字符 value.insert(value.begin(),'r'); cout<<"首个元素为"<<value.at(0)<<endl; return 0; }
访问元素
1.容器名[索引]:未进行边界检测,可能发生越界访问的错误
容器名.at(索引):会进行边界检测
2.容器名.front():容器第一个元素的引用
容器名.back(): 容器最后一个元素的引用
3.容器名.data():首个元素的指针
添加元素
push_back() 和 emplace_back()
两者底层实现机制不同,push_back()向容器尾部添加元素时,首先会创建这个元素,然后再将元素拷贝或者移动到容器中;而emplace_back()实现时直接在容器尾部创建这个元素,省去拷贝和移动元素的过程。
插入元素
指定位置是指迭代器指定的位置
insert():指定位置之前插入一个或者多个元素
emplace():指定位置之前插入一个元素
删除元素
deque
双端队列容器,deque擅长在序列尾部增加或者删除元素(时间复杂度为O(1),不擅长在序列中间添加或删除元素,该容器可以根据需要修改自身的容量或大小。不同的是deque擅长在序列头部添加或者删除元素,时间复杂度也是O(1),deque容器中存储元素不能保证所有元素都存储在连续的内存空间中。
deque以模板类deque
// 创建空的deque容器,创建之后可以做添加或者删除元素的操作 deque<int> d; // 创建一个具有n个元素的deque,默认为0 deque<int> d(10); // 建一个具有n个元素的deque且指定初始值 deque<int> d(10,5); // 可以通过拷贝原有deque容器创建新的deque容器 deque<int> d1(5); deque<int> d2(d1);
用法示例:
#include <iostream> #include <deque> using namespace std; int main() { //初始化一个空deque容器 deque<int> q; //向d容器尾部依次添加1,2,3 d.push_back(1); d.push_back(2); d.push_back(3); //向d容器头部直接添加0 d.push_front(0); //调用size函数输出容器存储字符个数 cout<<"元素个数为"<<d.size()<<endl; //使用迭代器遍历容器 for(auto it = d.begin();it<d.end();it++){ cout<<*it<<" "; } cout << endl; return 0; }
向deque容器添加元素时,deque会申请更多的内存空间,同时其包含的所有元素可能会被复制或者移动到新的内存地址,从而导致之前创建的迭代器失效。
访问元素时同样有容器名[索引] 和at的方法,其区别前已说明。同样的front()和back()两个成员函数分别返回容器第一个和最后一个元素的引用。但不存在data()成员函数,因为deque存储元素时并不会将元素存储在连续的内存空间中。
list
双向链表容器,list容器的元素分散存储在内存空间中,而非必须存储在一整块连续的内存空间。各个元素的顺序依靠指针维系,据此可以在序列已知的位置快速插入或者删除元素,但是访问元素需要遍历容器。
list容器以模板类list头文件中,如何创建list容器:
//创建空的list容器 list<int> values; //创建一个包含n个元素的list容器 list<int> values(10); //创建包含n个元素的容器并指定初始值 list<int> values(10,3); //通过拷贝或者指定区域来创建新的容器 list<int> value1(10,4); list<int> value2(value1); list<int> value2(value1.begin()+2,value1.end()-1);
list容器的重点在于其迭代器为双向迭代器而不是随机访问迭代器,故不支持通过下标访问list容器内指定位置的元素,仅支持++p,p++,p--,--p,*p,p1==p2以及p1!=P2等运算符。
list在进行插入(insert),接合(splice)等操作时,都不会造成原有的list迭代器失效,甚至删除操作,只有指向被删除元素的迭代器失效。
splice()成员方法移除元素的方式是,将存储该元素的节点从list容器底层的链表中摘除,然后再链接到当前 list容器底层的链表中,这意味着使用splice()成员方法将x容器中的元素添加到当前容器的同时,该元素会从x容器中删除。
删除元素:
根据元素所在位置删除:earse()
根据元素的值删除:remove()
forward_list
单链表,效率高
容器适配器
容器适配器就是将不适用的序列式容器变得适用,通过封装某个序列式容器,并重新组合该容器中包含的成员函数,使其满足某些特定场景的需要。容器适配器的本质上还是容器,只不过此容器模板类的实现,利用了大量其他基础容器模板类中已经写好的成员函数。
STL提供三种容器适配器,包括stack栈适配器,queue队列适配器,以及priority_queue优先权队列适配器。
stack
stack栈适配器是一种单端开口的容器,该容器实际上模拟的是栈存储结构,stack 适配器的开头端通常称为栈顶,数据的存取只能在栈顶处操作。栈中存储的元素满足后进先出的准则。满足条件的基础容器有vector,deque,list,默认使用的基础容器是deque。
stack容器的创建:以模板类stack<T,Container = deque
#include <iostream> #include <stack> using namespace std; //创建不包含元素的stack适配器 stack<int> values; //定义一个使用list基础容器的stack 适配器 stack<int,list<int>> values; //使用基础容器初始化 list<int> values{1,2,3}; stack<int,list<int>> my_stack(values);//此时栈顶元素为3,同时stack第二个模板参数必须显式指定为list<int>,和存储类型保持一致,否则使用默认的deque容器将无法用list容器去初始化stack适配器 //用一个stack适配器初始化另一个stack适配器,需要它们存储的元素类型以及底层采用的基础容器类型相同 list<int> values{1,2,3}; stack<int,list<int>> mystack1(values); stack<int,list<int>> mystack2(mystack1); //stack<int,list<int>> mysatck2=mystack1;
部分成员函数用法示例
#include <iostream> #include <stack> #include <list> using namespace std; int main() { //构建stack容器适配器 list<int> values{1,2,3}; stack<int,list<int>> mystack(values); //查看mystack里面存储元素的个数 cout<<"size of mystack"<<mystack.size()<<endl; //将mystack中存储的元素依次弹栈,直到其为空 while(!mystack.empty()){ cout<<mystack.top()<<endl; mystack.pop(); } return 0; }
queue
queue容器适配器有两个开口,一个用来专门输入数据,一个用来专门输出数据。queue成为队列适配器,具有先进先出的特点。
queue容器适配器以模板类queue<T,Container=deque
queue的创建
#include<iostream> #incude<queue> using namespace std; //创建空的queue容器适配器 queue<int> values; //手动指定基础容器的类型,存储的数据类型必须和queue容器适配器存储的元素类型数据一致。queue容器适配器底层容器可以选择deque和list queue<int,list<int>> values; //用基础容器初始化queue容器适配器 deque<int> values{1,2,3}; queue<int> myqueue(values); //用一个queue容器适配器初始化另一个容器适配器 deque<int> values{1,2,3}; queue<int> myqueue1(values); queue<int> myqueue2(myqueue1);
用法示例
#include <iostream> #include <queue> #include <list> using namespace std; int main() { //构建 queue 容器适配器 std::deque<int> values{ 1,2,3 }; std::queue<int> my_queue(values);//{1,2,3} //查看 my_queue 存储元素的个数 cout << "size of my_queue: " << my_queue.size() << endl; //访问 my_queue 中的元素 while (!my_queue.empty()) { cout << my_queue.front() << endl; //访问过的元素出队列 my_queue.pop(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)