顺序容器:vector;list;deque(双向口队列);queue(单向口队列);stack(栈 );heap(堆);priority_queue (按键值大小排序)
关联容器:set;map;multiset;multimap;
set和map 的insert/erase
set.insert(int x);//直接插入数值
set.insert(iterator position,int x);// 第一位为迭代器位置
set.insert(iterator first,iterator last);//迭代器区域
set.erase(int x);// 直接删除数值
set.erase(iterator position);// 迭代器位置
set.erase(iterator first,iterator last);
iterator set.find(int x);// 返回迭代器索引。使用关联式容器自带的find函数比STL只会循序搜索高效
int set.count(int x); // 返回数值,几个
iterator set.lower_bound(int x); // 返回迭代器索引
iterator set.upper_bound(int x);
vector的insert/erase
// insert必须要有位置的迭代器
vector.insert(iterator pos,elem); //在pos位置前插入一个elem元素的拷贝,返回新数据的位置。
vector.insert(iterator pos,n,elem); //在pos位置前插入n个elem数据,无返回值。
vector.insert(iterator pos,beg,end); //在pos位置前插入[beg,end)区间的数据,无返回值
// erase参数都是迭代器
vec.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
vec.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
顺序容器
0、容器的赋值、swap、assign
vector容器并不像数组一样,可以通过下标操作进行赋值或者添加元素,vector容器的下标操作只是用来访问的。
顺序容器的赋值操作:
Swap
vector<string> svec1(10);
vector<string> svec2(24);
swap(svec1,svec2);
结果:svec1中将包含24个string元素,svec2中10个。且操作很快。
1、要求两个相同类型容器
2、元素本身并未交换,swap只交换了两个容器的内部数据结构。
指向容器的迭代器、引用和指针在swap之后不会失效,仍指向操作之前指向的那些元素,但是这些元素属于不同容器了。
一、顺序容器操作
顺序容器不依赖于元素的值,而是与元素加入容器的位置相对应。
参数貌似都是迭代器类型。
1、添加元素
c.push_back(t) 在c的尾部创建一个值为t的元素
c.push_front(t) 在c的头部创建一个值为t的元素
c.insert(p,t) 迭代器p指向的元素之前创建一个值为t的元素,返回指向新添加的元素的迭代器。
insert实现了在容器的任意位置添加元素。第一个元素是指定要插入元素的迭代器位置。第二个元素为范围或者值元素。
2、访问元素
c.back() 返回c中尾元素的引用。若c为空,函数行为未定义
c.front() 返回c中首元素的引用。若c为空,函数行为未定义
c[n] 返回c中下标为n的元素的引用,n是一个无符号整数。若n>c.size(),则函数的行为未定义
c.at[n] 返回下标为n的元素的引用。如果下标越界,则抛出一个out_of_range异常
注意:容器内访问元素的成员函数(back、front、[]、.at())返回的都是引用。
auto &u=c.back()需要将变量定义为引用类型。
3、删除元素
c.pop_back() 删除c中尾元素,若c为空,则函数行为未定义,函数返回void
c.pop_front() 删除c中首元素,若c为空,则函数行为未定义,函数返回void
c.erase(p) 删除迭代器p所指的元素,返回以指向被删除元素之后的迭代器,
若p指向尾元素,则返回尾后迭代器。若p是尾后迭代器,则函数的行为未定义
c.erase(b,e) 删除迭代器b和e所指定范围内的元素,返回一个指向最后一个被删除元素之后元素的迭代器,
若e本身就是尾后迭代器,则函数也返回尾后迭代器
c.clear() 删除c中的所以元素,返回void
4、改变容器大小
c.resize(n) 调整c的大小为n个元素,如果n<c.size()多余元素被丢弃,若小于需要添加初始化的新元素。
5、迭代器失效问题
向容器内添加、删除元素都会导致所有指向容器的迭代器、引用和指针失效。
添加元素:
vector和string:插入位置之前元素的迭代器、引用和指针仍然有效,之后的失效。
deque:插入到除了首尾位置之外的任何位置都会失效。
删除元素:
vector和string:插入位置之前元素的迭代器、引用和指针仍然有效,之后的失效。
总结:
1、但是对于list来说,仍然有效。
2、需要保证每次改变容器操作后都正确的重新定位迭代器。
vector<int> vi = { 1, 2, 3, 5, 6 };
auto iter = vi.begin();
while (iter != iter.end()){
if (*iter % 2)//奇数
{
iter = vi.insert(iter, *iter);//在iter向前插入现有数值,然后iter指向新的位置
iter += 2;//需要跳过新位置和之前位置,到下一个数值
}
else{//偶数
iter = vi.erase(iter);//删除后指向下一位置
}
}
iter=...就是对于新位置的一种更新。
3、循环中重新调用end()
因为end()是会变化的。
关联容器
关联容器支持通过键值来高效的查找和读取元素,这是它和顺序容器最大的区别。两种基本的关联容器类型是map和set。map的元素以键-值对的形式组织:键用作元素在map中的索引,而值则表示所存储和读取的数据。set仅包含一个键,并有效的支持关于某个键是否存在的查询。
一.pair类型
pair类似于容器,也是一个模板。定义在头文件utility中,数据成员是public的,两个成员命名为first 和 second .
pair<T1, T2> p;
pair<T1, T2> p(v1, v2);
p.first//访问成员对象
p.second
p=make_pair(v1, v2);//创建新的pair对象
因为pair的声明比较长,如果需要多次用到,可以使用别名:
typedef pair<string,int> Book;
Book book1;
Book book2;
二.map类型
map是键-值对的集合。关键字起到索引的作用,值则表示与索引相关联的数据。
map就是存储pair类型对象的容器,模板。
1.map对象的定义
#include<map>
map<int,string> map1;
map<int,string> map2(map1);
map<int,string> map3(map1.begin(),map1.end());
2.访问map容器的元素
在map容器中,包含着三种类型的对象,分别是key_type,mapped_type,value_type,分别表示,键的数据类型,值得数据类型,map数据元素pair的数据类型。key-value , mapped_type是pair元素,由于不能改变关键字,具体为pair <const key_type, mapped_type>
map<int,string> map1;
//定义迭代器类型
map<int,string>::iterator iter=map1.begin();
//可以通过first,second成员访问键和值
//map<int,string>::key_type,键不可以更改
cout<<iter->first;
//map<int,string>::mapped_type,可以更改。
cout<iter->second+1;
3、通过insert添加元素
m.insert(e)
m.insert(beg,end)
m.insert(iter,e)
map<string ,int> map1;
//方法一
map1.insert(map<string,int>::value_type("one",1));
//方法二,使用make_pair
map1.insert(make_pair("two",2);
//方法三:也可以使用别名typtdef简略写法
typedef map<string,int>::value_type mapType;
map1.insert(mapType("threee",3));
//简单方法,列表初始化
map1.insert({world,1});
map的insert返回值为pair<map<string,int>::iterator, bool>
set的insert返回值为pair<set<int>::iterator, bool>
两部分:first成员是迭代器,指向具有给定关键字的元素。second是一个bool,指出元素是否插入(若已存在,返回false)
map<string,int> word_count;
string word;
while(cin>>word){
auto ret=word_count.insert({word,1});
if(!ret.second)//如果不存在,要插入
++ret.first->second;//增加计数器
}
4、使用下标访问和添加元素
map使用下标和vector类似,返回的都是下标关联的值,但是map的下标是键而不是递增的数字。
map<string,int> map1;
map1["one"]=1;
①、判断map1是否存在one的键,得出不存在。
②、新建一个pair<string,int>对象。
③、将新建的对象插入map1中。
④、修改map1中one对应的值为1。
string key;
while(cin>>key)
{
++map1[key];
}
5、通过find,count查找元素
map中下标读取元素的缺点是当不存在该元素时会自动添加,有时这是我们不希望看到的。
m.count(k) 返回m中k出现次数
m.find(k) 如果m容器中存在按k索引的元素,则返回指向该元素的迭代器。
map<string,int> map1;
if(map1.count("one")==1)
cout<<map1["one"];
map<string,int>::iterator iter=map1.find("one");
if(iter!=map1.end())
cout<<iter->second;
三.set类型
set遇到相同的值只保存一个.
与不能改变map的关键字一样,set 的关键字也是const 的,不能改变。
#include <iostream>
#include <set>
using namespace std;
//通过容器vector生成的类ivec
vector<int> ivec;
for(vector<int>::size_type i=0;i!=10;++i)
{
ivec.push_back(i);
ivec.push_back(i);
}
//用ivec初始化set
set<int> iset(ivec.begin(),ivec.end());
cout<<ivec.size()<<endl; //输出20
cout<<iset.size()<<end; //输出10
四.multimap和multiset的使用
它们的每个键都可以对应多个实例。由于一个键对应多个实例,因此,不能使用下标反问他们的元素,除此之外,其他操作和mapset没有任何不同。
添加元素
multimap<string,int> map1;
map1.insert(make_pair("one",1));
map1.insert(make_pair("one",2));