C++标准模板库里面的容器

ぃ、小莉子 提交于 2020-03-23 13:32:09

 

1. 顺序容器 sequential container

    单一类型元素组成的有序集合

    优:顺序访问元素快

    不足:添加、删除元素性能相对低

              非顺序访问元素性能相对低

    vector、string、array都是连续的内存空间

    

 

 

    容器选择:取决于执行访问的操作多还是插入删除的操作多

    ①一般用vector

    ②空间开销很重要时,不适用链表如list、forward_list

    ③需要随机访问:vector、deque

    ④要在中间进行插入、删除:链表类型如list、forwad_list       

    ⑤只要在头尾插入、删除:deque

    ⑥读取输入时要在中间插入,之后要随机访问:

                       输入时用vector追加数据,在调用sort()函数,以避免在中间添加元素

                       若必须要在中间插入元素,输入时用list,输入完成后将list的内容保存到vector中

    1.1 vector

  https://zh.cppreference.com/w/cpp/container/vector

    (1)vector的增长

 

    vector<int> ivec;
    cout<<"size:"<<ivec.size()<<endl;
    cout<<"capacity:"<<ivec.capacity()<<endl;
    for(int i=0;i<20;i++) {
        ivec.push_back(i);
        cout<<"size:"<<ivec.size()<<endl;
        cout<<"capacity:"<<ivec.capacity()<<endl;
    }

    从输出结果看,这个vector一开始capacity即容量一开始为0,在插入第一个元素后变为1,在打算插入第二个元素时,vector需要新的空间,capacity变为2......

   在capacity变为4,且准备放第5个元素时,vector申请空间(实际分配的空间多余需要的),capacity变成了8

   当vector满了之后需要新空间时,会申请2倍于当前容量的空间。

 

    (2)初始化

vector<int> vec1; //空的vector
vector<int> vec2 = vec1;   //用已有的vector初始化,调用拷贝构造函数
vector<int> vec3(vec1);    //同上
vector<int> vec3(10);      //vec3的size和capacity都是10,里面的值为相应类型的默认值   string不适用
vector<int> vec3(10,99);   //同上,但将值全设置为99
vector<int> vec4{1,2,3};   //列表初始化
vector<int> vec4 = {1,2,3};//同上vector<int> vec5(be,en);   //初始化为迭代器[be,en)间元素的拷贝                   array不适用

  用一个容器初始化另一个容器时,容器类型和元素类型必须相同(const char*可认为与string相同)

 

    (3)其他操作

  ①迭代器

vec.begin();     //首元素的迭代器
vec.end();       //尾元素的下一个位置的迭代器

  ②添加元素

vec.push_back(12);  //在尾端添加元素12

vec.insert(vec.begin(),10);   //在首元素前插入元素10,10成了首元素
vec.insert(vec.end(),20);     //20成了尾端元素

  ③遍历访问

  数组形式:

for(int i=0;i<vec.size();i++) {
     cout<<vec[i];
}

  迭代器方式:

vector<int>::iterator i;
for(i=vec.begin();i!=vec.end();i++) {
    cout<<*i;
}

  

vec.front();   //返回首元素的引用,要求vec不为空
vec.back();   //返回尾端元素的引用,要求vec不为空

  ④反向遍历

  reverse_iterator rbegin()     rever_iterator rend()

reverse_iterator ri;
for(ri=vec.rbegin();ri!=vec.rend();ri++) {
    cout<<*ri;
}

  ⑤删除元素

iterator erase(iterator pos)   删除pos所指元素
iterator erase(iterator first,iterator last)  删除[first,last)区间里的元素void clear();  //调用了 erase(vec.begin(),vec.end())  删除vec对象里的所有元素vec.pop_back();   //删除尾端元素

  ⑥整个vec的交换

    利用swap算法

vector<int> vec1 = {1,2,3};
vector<int> vec2 = {4,5,6};
swap(vec1,vec2);

 

  ⑦属性

bool empty()  是否为空
size_type size()   容器中实际元素个数
size_type max_size()  系统容许的vector容器最大元素个数
size_type capacity()    当前可容纳的元素个数 

 

  1.2 deque

  在尾部或头部插入、删除元素(O(1)复杂度)

  deque元素数据用分块的线性结构存储,每个deque块一般512字节,因此每个deque块能容纳的元素个数取决于每个元素的大小

  所有的deque用一个Map块管理,每个Map数据项记录各deque块首地址M_first、尾地址M_last、当前访问地址M_cur,还有M_node记录当前deque块在Map中的地址

 

 

 

  

 

 

    (1)创建、初始化

deque<int> d;    //没有元素的deque对象,实际有了一个deque块只是首尾迭代器重合
deque<int> d(10);//有10个元素的对象,元素有默认值
deque<int> d(10,1); //创建有10个初始值为1的deque对象
deque<int> d2(d);  //通过已有的deque对象初始化
deque<int> d3(d.begin(),d.begin()+5);  //将迭代器区间[first,last)里的元素复制到d3对象来初始化

    (2)添加元素

void push_back(const T&);  //向尾端添加一个元素
void push_front(const T&);  //向头部添加一个元素
iterator insert(iterator pos,const T&x);  //向pos位置之前插入元素x

 

    (3)遍历访问

  数组形式、迭代器形式,同vector

  反向遍历,同vector

     (4)删除元素

void pop_front();  //删除deque第一个元素
void pop_back();  //删除最后一个元素
iterator erase(iterator pos);  //删除pos所指位置元素
iterator erase(iterator first,iterator last);  //删除 [first,last)
void clear();   //删除所有元素

    (5)

  没有capacity方法

 

  1.3  list双向链表

  向任意位置插入、删除,复杂度都是O(1)

  list的初始化、添加元素、删除都类似vector、deque

  遍历只能用迭代器

    (1)

  ①list交换---swap

  ②list的归并

void splice(iterator pos,list& x) //将x的链表归并到当前链表的pos位置之前,x将被清空
void splice(iterator pos,list&,iterator i) //将一个list迭代器i所指元素归并到当前list链表中,并将被归并的元素从原链中删除
void merge(list &x) //将x的链表归并到当前链表中,并清空x的链表。只有当前链表和被归并的x链表元素事先都是升序排列的,这个函数才有意义,归并后也是升序

  例程:

list<int> list1;
for(int j=1;j<=3;j++) {
    list1.push_back(j);
}

list<int> list2;
list2.push_back(11);
list2.push_back(20);
list2.push_back(30);
list2.merge(list1);

//list1清空了,list2现在是按升序排列(1,2,3,11,20,30)

 

  ③list排序

list1.sort();  //升序排序

  ④连续重复元素删除,只保留一个

list1.unique();

 

 

1.4  forward_list 单向链表

  在单向链表中添加、删除元素比双向链表要麻烦,需要两个迭代器:一个指向要处理的元素、一个指向其前驱

  对一个元素删除时,应对其前面的迭代器调用 erase_after

flist.befor_begin();   //首元素之前不存在的元素迭代器(用来删除首元素),对此迭代器不能解引用

flist.insert_after(iterp,x);//在迭代器iterp后插入元素x
flist.insert_after(iterp,n,x);//同上,插入n个

flist.insert_after(iterp,itera,iterb); //在iterp之后插入[itera,iterb) itera、iterb不能指向flist内
flist.insert_after(iterp,{1,2,3});//同上。  返回指向最后一个插入元素的迭代器   iterp不能是尾后迭代器end()

emplace_after(iterp,x);  //用x在iterp指定位置后创建一个元素,返回指向新元素的迭代器

flist.erase_after(iterp);  //删除iterp后面的一个元素
flist.erase_after(iterb,itere); //删除[iterp,itere)  返回被删元素之后的迭代器

 

  两个例子:遍历删除list和forw_list中的元素

 1 list<int> list1 = {1,2,3,4,5,6};
 2 list<int>::iterator cur = list1.begin();
 3 while(cur != list1.end()) {
 4     if(*cur % 2) {
 5         cur = list1.erase(cur);  //erase返回被删除元素原来位置的下一个位置
 6     }
 7     else {
 8         ++cur;
 9     }
10 }
11 
12 //forward_list需要两个迭代器操作
13 forward_list<int> flist = {1,2,3,4,5,6};
14 forward_list<int>::iterator flistpre = flist.before_begin();
15 forward_list<int>::iterator flistcur = flist.begin();
16 while(flistcur != flist.end()) {
17     if(*flistcur % 2) {
18         flistcur = flist.erase_after(flistpre);
19     }
20     else {
21         flistpre = flistcur;
22         ++flistcur;
23     }
24 }

 

 

 

 

1.5 array

 

1.6 string

    (1)初始化

    (2)添加

void push_back(char c); //添加字符c
string & append(const char *s); //追加字符串s
string & append(const string & s); //追加s对象的字符串
iterator insert(iterator pos,const char &c);//在指定位置之前插入字符c

    (3)访问

  有数组形式、前向迭代器、反向迭代器

    (4)删除

 

    (5)替换

string & replace(size_type pos,size_type n,const char *s);//从pos开始的n个字符替换为字符串s
string & replace(size_type pos,size_type n,size_type n1,char c);//从pos开始的n个字符,替换为n1个字符c
string & replace(iterator first,iterator last,const char *s);//将[first,last)间的字符替换为字符串s

     (6)查找

size_type find(const char *s,size_type pos=0);//从pos位置开始查找子串s,返回位置索引或-1
size_type find(char c,size_type pos=0); //从pos位置开始找字符c的索引位置 无则返回-1
size_type rfind(const char *s,size_type pos=string::npos);//从pos位置开始反向查找
size_type rfind(char c,size_type pos=string::npos);//从pos位置开始反向查找
size_type find_first_of(const char *s,size_type pos=0);//从pos开始查找第一个位于子串s的字符
size_type find_first_not_of(const char *s,size_type pos=0);//查找第一个不在s里面的字符的位置
size_type find_last_of(const char *s,size_type pos=string::npos);//从pos开始查找最后一个位于子串s的字符
size_type find_last_not_of(const char *s,size_type pos=string::npos);//查找最后一个不在s里面的字符的位置

    (7)比较

int compare(const stirng &s); //相等,0   大于s,1   小于s,-1
int compare(size_type pos,size_type n,const string &s); //当前字符串从pos开始的n个字符 与字符串s比较
int compare(const char *s);//当前字符串与字符串s比较

    (8)字符串对象转C字符数组

const char* c_str() 

 

   

1.7  注意事项

  添加或删除元素后迭代器会失效,必须在进行改变容器的操作后都重新定位迭代器。

  循环结束条件,需要每次判断迭代器不等于end()  而不能实现保存end()的值,因为添加、删除操作后end()变化了

 

  

  

2. 关联容器 associative container

  set或者map

  关键值是否能重复:能重复的带 multi

  元素有序或无序:无序的带 unordered           无序容器用 哈希函数(散列) 组织元素,有序容器用 红黑树 组织

  

  

  2.1  map

  键值不重复、有序的关联容器,红黑树实现

  有前向和反向迭代器 iterator、const_iterator、reverse_iterator、const_reverse_iterator

    (1)创建、初始化

map<string,int> map1; //空的key对象为string,value为int,键值比较函数对象是默认的 less
map<string,int,greater<string>> map2; //空的map对象,键值比较函数对象是greater
map<string,int,my_func> map3; //创建空对象,并设置自定义的键值比较函数
map<string,int> map2(map2);   //拷贝构造函数,利用已有对象创建另一个

pair<string,int> p1("a",1);
pair<string,int> p2("b",2);
pair<string,int> parray[] = {p1,p2};
map<string,int> map4(parray,parray+2); //利用迭代器[first,last)创建

  设置自定义的键值比较函数对象的初始化:

struct strLess2 {
    bool operator()(string s1,string s2) const {
        int a = s1.compare(s2);
        return (a<0); //string对象按字典升序排列
    }
};
map<string,int,strLess2> mapa;
mapa.insert({"xy",1});
mapa.insert({"ab",2});

 

    (2)添加元素

  添加元素,可插入单个,也可以通过迭代器区间的方式插入。关键在于插入的数据元素是pair类型。map是键值无重复的,无法插入已有的键值

map1.insert({"abc",1});
map1.insert(make_pair("abc",1));
map1.insert(pair<string,int>("mn",1));
map1.insert(map<string,int>::value_type("mnb",1));

  insert、emplace插入单个元素,返回一个pair,其first成员是迭代器,指向具有给定键值的元素,second成员是false表示插入失败(map中已有此键值),是true表示插入成功。

   比如对于string做键,int做值的map1,返回类型是 pair<map<string,int>::iterator,bool>

 

    (3)删除元素

  删除某迭代器位置上的元素、等于某键值的元素、迭代器区间上的元素、容器中所有元素

void erase(iterator pos);
size_type erase(const key_type& key); //返回1
void erase(iterator first,iterator last); //[first,last)
void clear();

    (4)遍历访问

  可通过数组形式和迭代器形式

  ①数组形式:以一个关键值作为下标进行访问或操作对应的值,如果此键值不在map中,则会创建此键值对

map1["ab"];  //返回键值为"ab"的对应的值,如果没有此键,则添加一个键并初始化值
map1.at("ab"); //若此键不在map中,则抛出out_of_range异常

  用数组形式访问返回左值,可读可写。一般解引用迭代器返回的类型和下标运算符返回类型一致,但map不同:下标操作返回mapped_type对象,解引用迭代器返回value_type对象

  下标和at操作只适用于非const的map和unordered_map

  ②反向遍历

    (5)搜索 O(log n)

iterator find(const key_type& key) const;  //返回第一个键为key的元素,若没有此键,则返回尾后迭代器
int count(const key_type& key) const;  //键为key的元素的数量,对不允许键重复的容器,返回0或1
iterator lower_bound(key); //返回迭代器,指向第一个键不小于key的元素
iterator upper_bound(key); //返回迭代器,指向第一个键大于key的元素
iterator equal_range(key); //返回迭代器pair,表示关键字等于k的元素范围,若无此key,pair两个成员都是end()

                    

  2.2 multimap

  红黑树实现,只是键值可重复

  操作类似map,但不能用数组下标访问元素

 

  2.3 set

  与map类似,也是红黑树实现(根大于左孩子,小于右孩子),元素检索使用二叉搜索树的中序遍历,效率比vector、deque、list等顺序容器,由于中序遍历有序,因此set容器元素有序

  set的初始化、添加元素、迭代器遍历都类似map,只是set只有键,而不是键值对

  插入元素返回pair对象,提供所插入元素迭代器位置和成功标志

  2.4 multiset

  类似set

  插入元素返回相应的迭代器位置,而不是含成功标志的pair对象

 

  2.5 unordered_map

  利用哈希函数实现,搜索平均性能比map好,占用内存可能比map高(C++ STL中还有一个hash_map,和unordered_map很像,只不过unordered_map是C++ 11标准)

  无序容器在存储上组织成一组桶,每个桶保存0个或多个元素。无序容器使用哈希函数将元素映射到桶,若是multimap、multiset则有相同关键值的元素在同一个桶中。访问元素时,先计算哈希值找到桶。

  无序容器有管理桶的成员函数:查询容器状态、强制容器重组

 

     

 

  其他如创建、添加、遍历、删除等接口类似map

 

  无序容器对键的要求:

    无序容器重载 == 运算符来比较元素

    可使用内置类型,还包括指针、string、智能指针作为键(都对其提供了对应的hash模板)。如想定义关键字为自定义类型,需要自己写hash模板

   

  2.6 unordered_set、unordered_multimap、unordered_multiset

 

 

3. 容器适配器

  stack、queue、priority_queue

  通过现有的序列容器实现(stack使用deque),可看作适配器,将一种容器转换成另一种容器

  

      

 

 

 

  3.3  priority_queue 优先队列容器

  队列中最大的元素位与队首,每次出队时是最大元素先出队(底层是vector实现)

 

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!