ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2020-03-23 12:56:33  阅读:196  来源: 互联网

标签:容器 迭代 iterator 元素 pos C++ vector type 模板


 

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实现)

 

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

标签:容器,迭代,iterator,元素,pos,C++,vector,type,模板
来源: https://www.cnblogs.com/taoXiang/p/12243385.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有