如图所示 ,按大小说明其重要性
那就先说两个最重要的:
vector---数组 list-----链表
list 链表
有数据域和指针域
优点 1可以对任意位置进行快速插入和删除元素 2动态分配存储
缺点 容器遍历速度慢,因为要通过指针域来找元素,比数组慢。占用的空间也比数组大,因为有数据域和指针域。
STL中的链表是一个双向循环链表
支持头部和尾部的插入删除
链表的存储方式不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
list有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的
因为,vector如果满了,就会开辟新内存空间,原有迭代器会失效
可以想象vector是一排连接起来的箱子,list是独立的箱子,放哪都不会影响内存
vector | 随机访问迭代器 |
deque | 随机访问迭代器 |
list | 双向迭代器 |
set/multiset | 双向迭代器 |
map/multimap | 双向迭代器 |
stack | 不支持迭代器 |
queue | 不支持 |
好,言归正传
先从构造函数开始讲,这几类的方式都大差不差,包括构造时候和赋值和删除插入时
先从构造时候说
在list和vector都有四种方式构造函数
默认构造 无参构造
还可通过区间的方式进行构造
n个elem方式构造
拷贝构造
实例说明
vector的
void test01(){
vector<int> v1;//默认构造 无参构造
for(int i=0;i<=10;i++){
v1.push_back(i);
} //掌握好第一个和第四个 构造完后面赋值就好
printVector(v1);
//还可通过区间的方式进行构造
vector<int> v2(v1.begin(),v1.end()) ;
printVector(v2);
//n个elem方式构造
vector<int> v3(10,100);//十个100
printVector(v3);
//拷贝构造
vector<int> v4(v3);
printVector(v4);
}
list的
void test01(){
list<int> l1;
//添加数据
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
//遍历容器
printList(l1);
//区间方式构造
list<int>L2(l1.begin(),l1.end());
printList(L2);
//拷贝构造
list<int>L3(L2);
printList(L3);
//n个elem
list<int>l4(10,1000);
printList(l4);
}
不一样的只是容器名,其他几个也大差不差,一会也一并说了其相似和差异
继续说 赋值方式 两个一样,都是四种
默认赋值
赋值 operator=
assign
assign n个elem方式赋值
//vector赋值
void test01(){
vector<int> v1;//默认赋值
for(int i=0;i<10;i++){
v1.push_back(i);
}
printVector(v1);
//赋值 operator=
vector<int> v2;
v2=v1;
printVector(v2);
//assign
vector<int> v3;
v3.assign(v1.begin(),v1.end());
printVector(v3);
//assign n个elem方式赋值
vector<int> v4;
v4.assign(10,100);
printVector(v4);
}
list赋值
void test01(){
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
list<int>L2;
L2=L1;//operator=赋值
printList(L2);
list<int>L3;
L3.assign(L2.begin(),L2.end()) ;
printList(L3);
list<int>L4;
L4.assign(10,100);
printList(L4);
}
vector和list容器(只有vec)大小
vector和list大小操作
这几个接口
empty size capacity resize
capacity list么有
vector
void test01(){
vector<int> v1;
for(int i=0;i<10;i++){
v1.push_back(i);
}
printVector(v1);
if(v1.empty())//为真 为空
{
cout<<"v1为空"<<endl;
} else{
cout<<"v1不为空"<<endl;
cout<<"v1的容量为: "<<v1.capacity()<<endl;
cout<<"v1的大小为"<<v1.size()<<endl;//容量永远大于大小
}
//重新指定大小
v1.resize(15,100);//利用重载版本,可以指定默认填充值,参数2
printVector(v1);//如果重新指定的比原来长了,默认用0来填充
v1.resize(5);//重新指定的比原来短了,超出的部分会删除掉
printVector(v1);
cout<<"v1的容量为: "<<v1.capacity()<<endl;//容量还是没变
}
list
void test01(){
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
if(L1.empty()){
cout<<"L1为空"<<endl;
}
else{
cout<<"L1不为空"<<endl;
cout<<"L1元素个数"<<L1.size()<<endl;
}
//重新指定大小
L1.resize(10,10000);
// L1.resize(10);//后面默认用0填充
printList(L1);
L1.resize(2);
printList(L1);//其他删除
}
插入和删除
尾删和尾插两个都有 因为在随机访问迭代器中在头部上处理的是下一个容器deque,这里暂且不表 要分清front和x.begin()所表示什么
尾插 v1.push_back(40); L.push_back(10);
尾删 v1.pop_back(); L.pop_back();
insert一样
vector
//插入 第一个参数是 迭代器
v1.insert(v1.begin(),100);
printVector(v1);
v1.insert(v1.end(),200);
printVector(v1);
v1.insert(v1.begin(),2,1000);
printVector(v1);//加了两个1000
list
//insert插入
list<int>::iterator it=L.begin();
L.insert(++L.begin(),1000);//it灵活运用
printList(L);
L.insert(L.end(),2,9);
printList(L);
接下来说删除,删除这稍微不一样
通常就是erase()和clear() list加了一个remove()
//删除 参数也是 迭代器
v1.erase(v1.begin()) ;
printVector(v1);
// v1.erase(v1.begin(),v1.end());//清除区间 删除第三个2到4
v1.clear() ;//也是清除区间
printVector(v1);
list
//删除
it=L.begin();
L.erase(it);
printList(L);//删了200
//移除
L.push_back(10000);
L.push_back(10000);
L.push_back(10000);
printList(L);
L.remove(10000);//删除所有匹配的值
printList(L);//删掉了
//清空
L.clear();
printList(L);//多了一个空行
L.remove(10000);//删除所有匹配的值
printList(L);//删掉了
删除的是所有remove里面的数(1000)
数据存储
vector可以用[]和at()访问 和获取第一个元素v1.front() 获取最后一个v1.back()
void test01(){
vector<int> v1;
for(int i=0;i<10;i++){
v1.push_back(i);
}
//利用中括号方式来访问数组中的元素
for(int i=0;i<v1.size();i++){
cout<<v1[i]<<" ";
}
cout<<endl;
// 和string同理 利用at方式访问元素
for(int i=0;i<v1.size();i++){
cout<<v1.at(i)<<" ";
}
cout<<endl;
//获取第一个元素
cout<<"第一个元素为:"<<v1.front()<<endl;
//获取最后一个
cout<<"最后一个元素"<<v1.back()<<endl;
}
list不支持at()和[],因为链表不是连续空间 而且链表也不支持随机访问,双向迭代器只能前移和后移 这句说的太好了 防止忘记,让我们再次回顾list优点缺点
优点 1可以对任意位置进行快速插入和删除元素 2动态分配存储
缺点 容器遍历速度慢,因为要通过指针域来找元素,比数组慢。占用的空间也比数组大,因为有数据域和指针域。
void test01(){
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
// L1[0]是错误的L.at(0)错误
cout<<"第一个元素为"<<L1.front()<<endl;
cout<<"最后一个元素为"<<L1.back()<<endl;
//验证迭代器是不支持随机访问的
list<int>::iterator it=L1.begin();
it++;//正确 支持双向
it--;
// it+=1;报错 其不支持随机访问 可以用加法来判断是否支持随机访问
//或者写it--看其是否为单向
cout<<*it;
}
这一段着重看,学思想
//验证迭代器是不支持随机访问的
list<int>::iterator it=L1.begin();
it++;//正确 支持双向
it--;
// it+=1;报错 其不支持随机访问 可以用加法来判断是否支持随机访问
//或者写it--看其是否为单向
cout<<*it;
swap()
两个都有swap()属性,但这里list用reverse来表示
vector
void test01(){
vector<int> v1;
for(int i=0;i<10;i++){
v1.push_back(i);
}
cout<<"交换前"<<endl;
printVector(v1);
vector<int> v2;
for(int i=10;i>0;i--){
v2.push_back(i);
}
printVector(v2);
cout<<"交换后"<<endl;
v1.swap(v2);
printVector(v1);
printVector(v2);
}
vector swap()实际用途
//巧用swap()可以收缩内存空间
void test02(){
vector<int> v;
for(int i=0;i<100000;i++){
v.push_back(i);
}
cout<<"v的容量为:"<<v.capacity()<<endl;
cout<<"v的大小为"<<v.size()<<endl;
v.resize(3);//大小变小
cout<<"v的容量为:"<<v.capacity()<<endl;
cout<<"v的大小为"<<v.size()<<endl;//内存变小了 但是容量没变
//巧用swap收缩内存
vector<int>(v).swap(v);
cout<<"v的容量为:"<<v.capacity()<<endl;
cout<<"v的大小为"<<v.size()<<endl; //容量和内存都小了
//解析 其先初用 v创建了一个匿名对象vector<int>(v) 其内存和大小都是3
//然后swap 指针方向交换 所以v内存和大小都为3
//匿名对象指向的大空间 当前行执行完立即回收,所以不存在占空间
}
list
void test01(){
//反转
list<int>L1;
L1.push_back(20);
L1.push_back(10);
L1.push_back(50);
L1.push_back(40);
L1.push_back(30);
printList(L1);
cout<<"反转后"<<endl;
L1.reverse();
printList(L1);
}
接下来是两个独有属性
list排序
//排序
void test02(){
list<int>L1;
L1.push_back(20);
L1.push_back(10);
L1.push_back(50);
L1.push_back(40);
L1.push_back(30);
cout<<"排序前"<<endl;
printList(L1);
// sort(L1.begin(),L1.end());报错
//原因所有不支持随机访问迭代器的容器,不可以用标准算法
//不支持随机访问的迭代器,内部会提供对应的一些算法
cout<<"升序排列"<<endl;
L1.sort();//运行成功 默认从小到大
printList(L1);
cout<<"降序排列"<<endl;
L1.sort(myCompare);//提供函数改变sort顺序
printList(L1);
}
重点是
sort(L1.begin(),L1.end());报错
原因所有不支持随机访问迭代器的容器,不可以用标准算法
不支持随机访问的迭代器,内部会提供对应的一些算法所以 L1.sort();//运行成功 默认从小到大
vector预留空间
统计开辟内存次数 用reserve先开辟内存就不用后面再开辟了
#include <iostream>
#include <vector>
#include<string>
using namespace std;
//reserve(int len)//容器预留len个元素长度,预留位置不初始化,元素不可访问
void test01(){
vector<int> v;
//利用reserve预留空间
v.reserve(100000); //这回运行结果为0
int num=0;//统计开辟内存次数
int*p=NULL;
for(int i=0;i<100000;i++){
v.push_back(i);
if(p!=&v[0]){
p=&v[0];//循环找第一个内存
num++;
}
}
//vector可动态拓展 是找一块更大的空间 把原有的数据拷贝新空间,释放原空间
cout<<num;
}
int main()
{
test01();
system("pause");
}
两个print函数
void printVector(vector<int> &v){
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
cout<<*it<<" ";
}
cout<<endl;
}
如此看来两个除了容器名不同,输出其他都相同
void printList(list<int> &L){
for(list<int>::iterator it=L.begin();it!=L.end();it++){
cout<<*it<<" ";
}
cout<<endl;
}