使用序列式容器:
容器是STL的基础,容器分2种:
——序列式容器(sequential container)
——关联式容器(associative container)
序列式容器会强调元素的次序,依次维护第一个元素、第二个元素……,直到最后一个元素,面向序列式容器的操作主要是迭代操作。
序列式容器的创建和元素的访问:
使用序列式容器,须包含相关的头文件,vector、list及deque分别对应:
#include <vector>
#include <list>
#include <deque>
创建序列式容器的对象,大体有5种方式:
(1)创建空的容器,此时容器中的元素个数为0。
vector<int> obV;
list<float> obL;
deque<double> obD;
(2)vector<double> obV(10); //vector型对产生特定大小的容器,此时容器中的元素被创建,编译器使用默认值为元素隐式初始化,像int、float和double等内建的数据类型会被初始化为0,对于类对象元素,将调用其无参构造函数(用户定义的或编译器缺省提供的)或每个参数都有默认值的构造函数。象obV中含10个double型元素, 初始化为0
list<int> obL(20); //list型对象obL中含20个int型元素, 初始化为0
deque<float> obD(30); //deque型对象obD中含30个float型元素, 初始化为0
(3)在(2)的基础上更进一步,创建特定大小的容器,并且为其中的每个元素指定初始值,此时在元素多少的参数后增加一个参数。
vector<int> obV(10,8); //10个int型元素, 每个都初始化为8
list<double> obL(20,3.1); //20个double型元素, 每个都初始化为3.1
deque<string> obD(30,"Hello"); //30个string型元素, 每个都初始化为"Hello"
(4)根据已有同类型的容器创建新容器,并将其中的元素完全复制过来,设obV1、obL1和obD1都是现成的容器,里面存储的数据均为int型,则可用下述命令创建新容器
vector<int> obV2(obV1); //或vector<int> obV2=obV1;
list<int> obL2(obL1); //或list<int> obL2=obL1;
deque<int> obD2(obD1); //或deque<int> obD2=obD1;
(5)通过一对迭代器(可暂时理解为指针),以使编译器决定元素的个数和初值,这对迭代器用以标识一组元素区间。
int sz[5]={11,2,3,4,5};
vector<int> obV(sz, sz+5);
list<int> obL(sz, sz+5);
deque<int> obD(sz, sz+5);
vector和deque类的容器创建后就可以通过容器名[下标]或容器名.at(序号)的形式对元素进行随机访问(这是因为这2种类模板对下标运算符[]进行了重载);也支持迭代器访问。
但list类的容器不支持下标运算符[],无法使用[]对元素进行随机访问。但支持双向迭代器访问,如:
list<int>::iterator iter = obL.begin();
#include <iostream>
#include<deque>
using namespace std;
int main()
{
double arr[5]={1,2,3,4,5};
deque<double> deque1(arr,arr+5); //迭代器的区间[ )
//随机访问迭代器
for(int idx=0;idx!=deque1.size();++idx)
{
cout << deque1[idx]<<" ";
}
cout<<endl;
//双向迭代器,双向队列,两边都可以输入输出
deque<double>::iterator it = deque1.begin();
for(;it!=deque1.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;
it = deque1.end();
--it;
for(;it!=deque1.begin()-1;--it)
{
cout<<*it<<" ";
}
cout<<endl;
//c++11 新特性编译要加上 -std=c++11
for(auto & elem : deque1)
cout<<elem<<" ";
cout<<endl;
return 0;
}
|
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<float> list1(3,5);
//list 没有重载下标运算符,所以list不支持随机访问
list<float>::iterator it;
for(it = list1.begin();it!=list1.end();++it)
{
(*it) += 2;
cout<< *it <<" ";
}
cout<<endl;
list<float> list2(4,9);
//进行交换,并没有移动数据,只是交换了指针而已
list1.swap(list2);
for(it = list1.begin();it != list1.end();++it)
{
(*it) += 2;
cout<< *it <<" ";
}
cout<<endl;
return 0;
}
|
序列式容器中元素的插入和删除:
在创建普通数组时,需要指定元素的个数,元素的插入和删除很繁琐。但在序列式容器中,只要调用操作函数,所有的事情都由STL类库自动完成,而且容器对象都能随着元素的插入和删除自动地增大或缩小。
下面介绍5类相关函数:
(1)在容器尾部进行插入和删除(list、deque和vector都适用):
void push_back(t)和void pop_back(void)
(2)在容器头部进行插入和删除(list和deque适用,vector不适用):
void push_front(t)和void pop_front(void)
(3)获取容器头部和尾部元素(list、deque和vector都适用):
front(void)和back(void)
(4)在容器中间插入元素。有如下3种重载形式:
1、将元素t插到p之前,返回的迭代器指向被插入的元素。
iterator insert(iterator p, elemType t);
2、在p之前插入n个t,无返回值。
void insert(iterator p, int n, elemType t);
3、在p之前插入[first, last)之间的所有元素。插入的多是一个数组
void insert(iterator p, iterator first, iterator last);
(5)erase删除操作,有2种重载形式:
1、删除迭代器p所指向的元素,返回p指向的下一个迭代器:
iterator erase(iterator p);
2、删除[frist,last)之间的所有元素,返回last指向的下一个迭代器:
iterator erase(iterator first, iterator last);
(6)clear操作:用于将容器对象清空。
void clear(void); //但是容器capacity不会变,size变成0
对于std::list还有一种删除方法: 成员函数 remove/remove_if
#include <iostream>
#include <vector>
using namespace std;
template <typename Container>
void display (Container & c)
{
//typename关键字告诉编译器iterator是一个类类型,而不是其他的
typename Container::iterator it; //VS不加也可以
for(it = c.begin(); it != c.end(); ++it)
{
cout<<*it<<" ";
}
cout<<endl;
}
|
int main()
{
vector<int> vec(5,0);
display(vec);
vector<int>::iterator it = vec.end(); //这里注意,vec的范围[ ),所以这里it指向的是最后一个元素的后面
it=vec.insert(it,1);
display(vec);
cout<<"*it="<<*it<<endl;
vec.insert(it,2,3);
display(vec);
it=vec.begin();
int arr[3]={1,2,3};
vec.insert(it,arr,arr+3);
display(vec);
// cout<<*it<<endl; //这里不能输出,因为前面的insert没有返回指针,这里泄露了
cout<<"------------删除开头元素------------"<<endl;
vec.erase(it=vec.begin());
for(auto & elem:vec)
cout<<elem<<" ";
cout<<endl;
cout<<"------------------------------------"<<endl;
//c++新特性,编译加上 -std=c++11
vector<int> vec2 = {1,2,3,4,5,6,7,8,9};
display(vec2);
return 0;
}
|
#include<iostream>
#include<vector>
using namespace std;
template<class container>
void print(container& v)
{
cout<<"capacity="<<v.capacity()<<" size="<<v.size()<<endl;
}
int main()
{
vector<int> ve = {1,2,3}; // C++11新特性
print(ve);
ve.resize(10); // 更改空间大小10,没有数值的空间赋0,这时候size和capacity都是10
print(ve);
for(auto& elem:ve)
cout<<elem<<" ";
cout<<endl;
ve.resize(100);
print(ve);
ve.clear(); //清空有效元素,size变0
print(ve);
ve.push_back(4);
ve.push_back(5);
ve.push_back(6);
print(ve);
ve.resize(2);
print(ve);
for(auto& elem:ve)
cout<<elem<<" ";
cout<<endl;
//vector<int>(ve).swap(ve); // 用ve里面的有效数值初始化一个临时vector,swap交换指针,这时候ve的指针就交换了指向临时变量的空间,达到了删除没有存放数值的空间
//删除没有存放数据的单元,释放空间
//表示创建一个临时的vector<int>(vec) 这个临时的vec会去拷贝vec中的元素,然后再交换,这样vec的大小就变成3了,执行完后释放临时的vec
ve.shrink_to_fit(); // 等价上面
print(ve);
cout<<"-------------"<<endl;
|
vector<int> ve2;
print(ve2);
ve2.reserve(10); // 开辟空间,区别resize更改空间大小
print(ve2);
ve2.push_back(1);
print(ve2);
return 0;
}
//reserve只是开辟空间,resize更改空间大小,改大了,多出空间全部初始化0,改小了就从尾删除多出的空间,元素也相应删除//reserve改的是capacity,resize改的是size |
vector容器:
vector是数组的类表示,它提供了自动管理内存的功能,可以动态改变vector对象的长度,并随着元素的增删而增大或缩小,提供了对元素的随机访问,和数组一样,在vector尾部添加和删除元素(push_back和pop_back)的时间是固定的,但在vector中间或头部增删元素(insert,erase)的时间和复杂度线性正比于vector容器对象中元素的多少。
deque容器:
deque表示双端队列(double-ended queue),deque容器对象支持下标随机访问,在deque头部和尾部添加删除元素时的时间都是固定的,因此,如果有很多操作是针对序列的头部位置的,建议使用deque容器。但是,如果是在deque的中间进行元素的增删处理,操作的复杂度和时间正比于deque对象中元素的多少。
list容器:
list类模板表示双向链表,除了首尾元素外,list容器对象中的每个元素都和前面的元素相链接。list不支持下标随机访问,只能通过迭代器双向遍历。和vector和deque不同的是,在list的任何位置增删元素的时间都是固定的。
#include<iostream>
#include<list>
using namespace std;
bool removeIf(int n)
{
return n>10;
}
int main()
{
list<int> l1 = { 1,100,2,3,10,1,11,-1,12 };
for(auto elem:l1)
cout<<elem<<" ";
cout<<endl;
l1.remove(1); // 删除所有1
for(auto elem:l1)
cout<<elem<<" ";
cout<<endl;
l1.remove_if([](int n){return n>10;}); // lambda表达式
//l1.remove_if(removeIf); //和上面一样的效果,删除所有大于10的元素
for(auto elem:l1)
cout<<elem<<" ";
cout<<endl;
return 0;
}
|