• C++容器——zhuan


    容器简介

    STL标准容器类简介
    标准容器类      说明
    顺序性容器
    vector       相当与数组,从后面快速的插入与删除,直接访问任何元素
    deque       双队列,从前面或后面快速的插入与删除,直接访问任何元素
    list       双链表,从任何地方快速插入与删除
    关联容器
    set       快速查找,不允许重复值
    multiset      快速查找,允许重复值
    map       一对一映射,基于关键字快速查找,不允许重复值
    multimap      一对多映射,基于关键字快速查找,允许重复值
    容器适配器
    stack       后进先出
    queue       先进先出
    priority_queue      最高优先级元素总是第一个出列

    所有标准库共有函数
    默认构造函数      提供容器默认初始化的构造函数。
    复制构造函数      将容器初始化为现有同类容器副本的构造函数
    析构函数      不再需要容器时进行内存整理的析构函数
    empty       容器中没有元素时返回true,否则返回false
    max_size      返回容器中最大元素个数
    size       返回容器中当前元素个数
    operator=      将一个容器赋给另一个容器
    operator<      如果第一个容器小于第二个容器,返回true,否则返回false,
    operator<=      如果第一个容器小于或等于第二个容器,返回true,否则返回false
    operator>      如果第一个容器大于第二个容器,返回true,否则返回false
    operator>=      如果第一个容器大于或等于第二个容器,返回true,否则返回false
    operator==      如果第一个容器等于第二个容器,返回true,否则返回false
    operator!=      如果第一个容器不等于第二个容器,返回true,否则返回false
    swap       交换两个容器的元素

    其中operator>,operator>=,operator<,operator<=,operator==,operator!=均不适用于priority_queue

    顺序容器和关联容器共有函数
    begin      该函数两个版本返回iterator或const_iterator,引用容器第一个元素
    end      该函数两个版本返回iterator或const_iterator,引用容器最后一个元素后面一位
    rbegin      该函数两个版本返回reverse_iterator或const_reverse_iterator,引用容器最后一个元素
    rend      该函数两个版本返回reverse_iterator或const_reverse_iterator,引用容器第一个元素前面一位
    erase      从容器中清除一个或几个元素
    clear      清除容器中所有元素

    下表显示了顺序容器和关联容器中常用的typedef,这些typedef常用于变量、参数和函数返回值的一般性声明。
    value_type      容器中存放元素的类型
    reference      容器中存放元素类型的引用
    const_reference 容器中存放元素类型的常量引用,这种引用只能读取容器中的元素和进行const操作
    pointer      容器中存放元素类型的指针
    iterator      指向容器中存放元素类型的迭代器
    const_iterator      指向容器中存放元素类型的常量迭代器,只能读取容器中的元素
    reverse_iterator      指向容器中存放元素类型的逆向迭代器,这种迭代器在容器中逆向迭代
    const_reverse_iterator      指向容器中存放元素类型的逆向迭代器,只能读取容器中的元素
    difference_type      引用相同容器的两个迭代器相减结果的类型(list和关联容器没有定义operator-)
    size_type      用于计算容器中项目数和检索顺序容器的类型(不能对list检索)

    容器的比较

    vector      (连续的空间存储,可以使用[ ]操作符)快速的访问随机的元素,快速的在末尾插入元素,但是在序列中间岁间的插入,删除元素要慢,而且如果一开始分配的空间不够

    的话,有一个重新分      配更大空间,然后拷贝的性能开销
    deque (小片的连续,小片间用链表相连,实际上就是一个每小片指针的数组,因为知道类型,所以还是可以使用【】,只是速度没有vector快)快速的访问随机的元素,快速

    的在开始和末尾插入元素,随机的插入,删除元素要慢,空间的重新分配要比vector快
    list       (每个元素间用链表相连)访问随机元素不如vector快,随机的插入元素比vector快,对每个元素分配空间,所以不存在空间不够,重新分配的情况

    set 内部元素唯一,用一棵平衡树结构来存储,因此遍历的时候就排序了,查找也比较快的哦。
    map 一对一的映射的结合,key不能重复。

    1、vector
        连续存储结构,每个元素是在内存上是连续的;
        支持高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下;
    2、deque
        连续存储结构,即其每个元素在内存上也是连续的,类似于vector,不同之处在于,deque提供了两级数组结构,第一级完全类似于vector,代表实际容器;另一级维护容器的首位地址。
       这样,deque除了具有vector的所有功能外,还支持高效的首端插入/删除操作。
    3、list
        非连续存储结构,具有双链表结构,每个元素维护一对前向和后向指针,因此支持前向/后向遍历。
        支持高效的随机插入/删除操作,但随机访问效率低下,且由于需要额外维护指针,开销也比较大。

        list支持push_front(),push_back(),pop_front(),pop_back() 
        list不支持'[]'和at(),因为它不是连续存放的. 
        标准模板库里比大小都用<,比等于用== 


    4、vector V.S. list V.S. deque:
        a、若需要随机访问操作,则选择vector;
        b、若已经知道需要存储元素的数目, 则选择vector;
        c、若需要随机插入/删除(不仅仅在两端),则选择list
        d、只有需要在首端进行插入/删除操作的时候,才选择deque,否则都选择vector。
        e、若既需要随机插入/删除,又需要随机访问,则需要在vector与list间做个折中。
        f、当要存储的是大型负责类对象时,list要优于vector;当然这时候也可以用vector来存储指向对象的指针,同样会取得较高的效率,但是指针的维护非常容易出错,因此不推荐使用。

    5、capacity V.S size
        a、capacity是容器需要增长之前,能够盛的元素总数;只有连续存储的容器才有capacity的概念(例如vector,deque,string),list不需要capacity。
        b、size是容器当前存储的元素的数目。
        c、vector默认的容量初始值,以及增长规则是依赖于编译器的。

    6、用vector存储自定义类对象时,自定义类对象须满足:
        a、有可供调用的无参构造函数(默认的或自定义的);
        b、有可用的拷贝赋值函数(默认的或自定义的)

    7、迭代器iterator 
        a、vector与deque的迭代器支持算术运算,list的迭代器只能进行++/--操作,不支持普通的算术运算。

    8. pair类型

     
       pair模板类用来绑定两个对象为一个新的对象,该类型在<utility>头文件中定义。pair类型提供的操作如下表:
     
     pair<T1, T2> p1;  创建一个空的pair对象,它的两个元素分别是T1和T2类型,采用值初始化
     pair<T1, T2> p1(v1, v2);

     创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2

     make_pair(v1, v2)  以v1和v2值创建一个新的pair对象,其元素类型分别是v1和v2的类型
     p1 < p2  字典次序:如果p1.first<p2.first或者!(p2.first < p1.first)&& p1.second<p2.second,则返回true
     p1 == p2  如果两个pair对象的first和second成员依次相等,则这两个对象相等。
     p.first  返回p中名为first的(公有)数据成员
     p.second  返回p中名为second的(公有)数据成员
    关联容器
     
       关联容器共享大部分顺序容器的操作,但不提供front, push_front, back, push_back以及pop_back操作。
       具体而言,有顺序容器中的:前三种构造函数;关系运算;begin, end, rbegin和rend操作;类型别名;swap和赋值操作,但关联容器不提供assign函数;clear和erase函数,但erase函数返回void类型;关于容器大小的操作,但resize函数不能用于关联容器。
     

    map类型
       map类型定义在头文件<map>中。map是键-值对的集合,通常看作关联数组:可使用键作为下标来获取一个值。map类定义内部定义的类型有key_type, mapped_type, value_type,如下表所示:
     
     map<K, V>::key_type  在map容器内,用做索引的键的类型
     map<K, V>::mapped_type  在map容器中,键所关联的值的类型
     map<K, V>::value_type  map的值类型:一个pair类型,它的first元素具有
    const map<K, V>::key_type类型,而second元素
    则为map<K, V>::mapped_type类型
     
    注意:map的元素类型为pair类型,且键成员不可修改。其它类型别名与顺序容器一样。
     
    map对象的定义
     
     map<K, V> m;  创建一个名为m的空map对象,其键和值的类型分别为K和V
     map<K, V> m(m2);  创建m2的副本m,m与m2必须有相同的键类型和值类型
     map<k, V> m(b, e);  创建map类型的对象m,存储迭代器b和e标记的范围内所有元素的副本。元素的类型必须能转换为pair<const k, v>
     
    键类型的约束
     
       在使用关联容器时,它的键不但有一个类型,而且还有一个相关的比较函数。默认情况下,标准库使用键类型定义的 < 操作符来实现键的比较。这个比较函数必须满足:当一个键和自身比较时,结果必定是false;当两个键之间都不存在“小于”关系时,则容器将之视为相同的键。也就是说,map内的元素按键值升序排列。
     
    operator[]
     
     A::reference operator[](const Key& key);
     []操作符返回键key所关联的值的引用;如果该键key不存在,则向map对象添加一个新的元素,元素的键为key,所关联的值采用值初始化。(要特别留意这个副作用)
     
    注:map下标操作符返回的类型(mapped_type&)与对map迭代器进行解引用获得的类型(value_type)不相同。

       例如:
          map <string, int> wordCount;   // empty map
          word_count["Hello"] = 1;
       上面的代码首先创建一个空的map对象,然后执行下列步骤:
       1)在wordCount中查找键为“Hello”的元素,没有找到;
       2)将一个新的键-值对插入到wordCount中,其中,键为“Hello”,值为0
       3)读取新插入的键-值对的值,并将它的值赋为1。
    应用实例,下面的程序用来统计一篇英文文章中单词出现的频率:

    #include <iostream>
    #include <map>
    using namespace std;

    int main()
    { 
        map<string, int> wordCount;
        string word;
        while (cin >> word)
            ++wordCount[word];
        
        for (map<string, int>::iterator it = wordCount.begin(); it != wordCount.end();++it)
            cout<<"Word: "<<(*it).first<<" \tCount: "<<(*it).second<<endl;
        
        return 0;
    }

     
    map::insert
     
     m.insert(e)    e是一个用在m上的value_type类型的值,如果键(e.first)不在m中,则插入e到m中;如果键已经在m中存在,则保持m不变。
       该函数返回一个pair类型对象,如果发生了插入动作,则返回pair(it, true);否则返回pair(it, false)。其中,it是指向键为e.first那个元素的迭代器。
     m.insert(beg, end)

     beg和end是标记元素范围的迭代器,其中的元素必须为value_type类型的键-值对。对于该范围内的所有元素,如果它的键在m中不存在,则将该键及其关联的值插入到m。返回void类型。

     m.insert(iter, e)  insert(e),并以iter为起点搜索新元素的位置。返回一个迭代器,指向m中键为e.first的元素。
     
    注:当需要插入一个map元素时,一是可以用map::value_type来构造一个pair对象,另外,也可以用make_pair来构造这个对象。
     
    查找元素
     
     m.count(k)  返回m中k的出现次数(0或1)
     m.find(k)  如果容器中存在键为k的元素,则返回指向该元素的迭代器。
     如果不存在,则返回end()值。
     
    删除元素
     
     m.erase(k)  删除m中键为k的元素,返回size_type类型的值,表示删除的元素个数(0或1)
     m.erase(p)  从m中删除迭代器p所指向的元素。p必须指向m中确实存在的元素,而且不能等于e.end()。返回void类型
     m.erase(b, e)  从m中删除[b, e)范围内的元素,返回void类型
    set类型
       set类型定义于<set>头文件中。set容器支持大部分map容器的操作,如:构造函数;insert操作;count和find操作;erase操作。两个例外情况是:set不支持下标操作符,而且没有定义mapped_type类型。与map一样,set容器存储的键也必须是唯一的,而且不能修改。
     

    multimap和multiset类型
       map和set容器中,一个键只能对应一个实例。而multiset和multimap类型则允许一个键对应多个实例。
     
       multimap和multiset所支持的操作分别与map和set的操作相同,只有一个例外:multimap不支持下标运算。为了顺序一个键可以对应多个值这一特性,map和mulitmap,或set和multiset中相同的操作都以不同的方式做出了一定的修改。
     
    元素的添加和删除
     
       map和set容器中的insert和erase操作同样适用于multimap和multiset容器,实现元素的添加和删除。
     
       由于键不要求是唯一的,因此每次调用insert总会添加一个元素。
     
       而带有一个键参数的erase将删除拥有该键的所有元素,并返回删除元素的个数;而带有一个或一对迭代器参数的erase版本只删除指定的元素,并返回void类型。
     
    查找元素
     
       在map和set容器中,元素是有序存储的(升序),同样multimap和multiset也一样。因此,在multimap和multiset容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放,即迭代遍历时,可保证依次返回特定键所关联的所有元素。
     
       要查找特定键所有相关联的值,可以有下面三种方法:
       1)配合使用find和count来查找:count函数求出某键出现的次数,而find操作返回指向第一个键的实例的迭代器。
     
       2)使用lower_bound和upper_bound函数:这两个函数常用于multimap和multiset,但也可以用于map和set容器。所有这些操作都需要传递一个键,并返回一个迭代器。
     m.lower_bound(k)  返回一个迭代器,指向键不小于k的第一个元素
     m.upper_bound(k)  返回一个迭代器,指向键大于k的第一个元素
     m.equal_range(k)  返回一个迭代器的pair对象;它的first成员等价于
     m.lower_bound(k),而second成员则等价于
     m.upper_bound(k)
     
    注意:形成的有效区间是[lower_bound(k), upper_bound(i)),是个半开半闭区间。
         lower_bound返回的迭代器不一定指向拥有特定键的元素。如果该键不在容器中,则lower_bound返回在保持容器元素顺序的前提下该键应被插入的第一个位置。
         若键不存在,返回的迭代器相同。
       3)使用equal_range,其实质跟法2)相同。
     
    //vector例子
    #include <iostream>
    using namespace std;
    #include
    <vector>

    int main()
    {
    vector
    <int> vi;
    cout
    << "Input scores,end by -1:";
    int s;
    int m=0;
    for(;;){
    cin
    >> s;
    if(s==-1) break;
    vi.push_back(s);
    if(s>m)
    m
    = s;
    }
    int inc = 100-m;
    for(int i=0; i<vi.size(); i++)
    vi[i]
    += inc;
    for(int i=0; i<vi.size(); i++)
    cout
    << vi[i] << ' ';
    cout
    << endl;
    try{
    cout
    << "vi[1000]=" << vi[1000] << endl;
    cout
    << "vi.at(1000)=" << vi.at(1000) << endl;
    }
    catch(exception& e){
    cout
    << e.what() << endl;
    }
    }

    存取首尾元素

    front()与back()操作,取后一个和最前一个元素,注意其返回是引用,其而是左值(l_value),因此可以赋值. 做类似于vecobj.front() = 3;的操作,但要保证front空间有效,否则形为无法预测。 

    使用assign

    assign可以改变大小和初值,大小是随意的,不受开始时大小的限制,如果设置为0,则清空。

    assign(5,0)把vector改为5个大小,并用0添充

    assign(iax+3,iax+5); 从数组第4到5个填充,注意左闭右开,即可取到iax[3]与iax[4]

    使用insert

    insert(it, x),在it前插入一个元素x

    insert(it,first,last),在it前插入一个序列[first,last)左闭右开

    使用erase

    erase(it)删除在it处的元素,返回值为下一元素。如果intVec.erase(intVec.end());并不会报错,如果删除一个序列[first,last),使用erase(first,last)

    //list.cc
    #include <iostream>
    using namespace std;
    #include
    <list>

    int main()
    {
    int cpp[5] = {3,5,6,8,9};
    int java[8] = {1,2,3,4,5,6,7,8};
    int Unix[4] = {3,4,5,6};
    list
    <int> li;
    li.insert(li.begin(),cpp,cpp
    +5);
    li.insert(li.begin(),java,java
    +8);
    li.insert(li.begin(),unix,unix
    +4);
    li.sort();
    //排序
    li.unique();//删除相邻的重复的元素,只留一份
    list<int>::iterator it = li.begin();
    while(it!=li.end())
    cout
    << *it++ << ' ';
    cout
    << endl;
    }

      二、操作

    1.resize & clear

    使用resize(n)改变大小,使用resize(n, val)如果需要用T(val) 来填满空闲值。

    2.front ()& back()

    如果listObj非常量对象,返回是一个左值函数

    3.插入操作

    insert(iterator it , val)

    insert(iterator it, first, last)

    insert(iteratot it, n, x)//插入n个x

    4.移除 [按值删]

    remove(x); //vector.erase(integrator it)

    //map.cc
    #include <iostream>
    #include
    <map>
    using namespace std;
    #include
    <string>

    int main()
    {
    map
    <int, string> m;
    m.insert(make_pair(
    1001,"李明"));
    m.insert(make_pair(
    1002,"王国"));
    m.insert(make_pair(
    1005,"失小"));
    m[
    1006] = "刘华";
    m.insert(make_pair(
    1001,"娜娜"));
    map::iterator it;
    it
    = m.begin();
    while(it!=it.end()){
    cout
    << it->first << ':' << it->second << endl;
    ++ it;//会比it++性能高
    }
    }

    对于set,map来说重复的插入被忽略.

    //multimap
    //一切操作都是对key而言的
    //key一定要支持小于运算符,不然是不能进行排序的
    #include <iostream>
    using namespace std;
    #include
    <map>
    #include
    <string>

    int main()
    {
    typedef multimap
    <string,string> M;
    M mss;
    M::iterator ib,ie;
    mss.insert(make_pair(
    "a","b"));
    mss.insert(make_pair(
    "c","d"));
    mss.insert(make_pair(
    "e","f"));
    mss.insert(make_pair(
    "c","p"));
    mss.insert(make_pair(
    "a","m"));
    mss.insert(make_pair(
    "c","d"));
    mss.insert(make_pair(
    "a","p"));
    ib
    = mss.begin();
    ie
    = mss.end();
    while(ib!=ie){
    cout
    << ib->first <<',' << ib->second << endl;
    ++ ib;
    }
    //end while
    cout << "a count : " << mss.count("a") << endl;
    cout
    << "c count : " << mss.count("c") << endl;
    ib
    = mss.lower_bound("c");
    ie
    = mss.upper_bound("c");
    while(ib!=ie){
    cout
    << ib->first <<',' << ib->second << endl;
    ++ ib;
    }
    //end while

    }
    //end main

    //set
    //同样的插入同样被忽略
    #include <iostream>
    using namespace std;
    #include
    <set>

    int main()
    {
    set<int> si;
    int userid[5] = {1,3,3,4,5};
    for(int i=0;i<5; i++)
    si.insert(userid[i]);
    set<int>::iterator it;
    it
    = si.begin();
    while(it!=si.end())
    cout
    << *it++ << ' ';
    cout
    << endl;
    cout
    << "user 3 : " << (si.find(3)!=si.end()) << endl;
    cout
    << "user 9 : " << (si.find(9)!=si.end()) << endl;
    }

    //multiset
    //用的是平衡二叉树,所以它的查找效率是相当高的.
    #include <iostream>
    using namespace std;
    #include
    <set>

    template
    <typename Iter>
    void show(Iter ib, Iter ie)
    {
    while(ib!=ie)
    cout
    << *ib++ << ' ';
    cout
    << endl;
    }
    int main()
    {
    int a[5] = {5,1,7,5,1};
    multiset
    <int> pids(a,a+5);
    show(pids.begin(),pids.end());
    pids.insert(
    7);
    pids.insert(
    7);
    pids.insert(
    7);
    pids.erase(pids.find(
    5));
    show(pids.begin(),pids.end());
    cout
    << "end process 7..." << endl;
    pids.erase(
    7);
    show(pids.begin(),pids.end());
    }
    //end main

      

     


  • 相关阅读:
    PHP学习
    python获取命令行参数 启动文件
    SQLServer中char、varchar、nchar、nvarchar的区别
    VBA
    python 爬虫资料
    python乱码问题之爬虫篇
    angularjs component
    通过jQuery Ajax使用FormData对象上传文件
    directive完成UI渲染后执行JS
    交易日志
  • 原文地址:https://www.cnblogs.com/xiaoxxy/p/2163378.html
Copyright © 2020-2023  润新知