zoukankan      html  css  js  c++  java
  • STL之关联容器---set, mutilset, map, mutilmap

    STL的容器分为序列容器和关联容器。它们所表达的数据结构各有不同:

    序列容器:vector(变长数组), list(链表), queue(队列), heap(堆算法)等

    关联容器:set/mutilset,map/mutilmap,(都表达二叉树,且都由红黑树实现)

    关联容器是指容器的元素为键值对(key-value),但这四种关联容器的键值对之间略有差异

    一、set/mutilset

    set是集合之意,其键值对的键和值相同,那么set更像普通的二叉树即结点是键值和实值。

    set的键值不允许相同,所以set的value值也没有相同的。而这也是mutilset和set唯一的不同,mutilset可以有相同的键值。

    set是基于红黑树的所以其中的元素会自动排序。

    set常用操作:

    定义set: set<int> a;

    插入元素: a.insert(1);

    删除元素: a.erase(1);

    删除所有元素: a.clear():

    判断容器是否为空: a.empty();

    返回容器当前元素个数: a.size();

    判断元素是否属于集合: if(a.find(1)!=a.end())则属于

    集合的并,交,差:

    set_union(a.begin(), a.end(), b.begin, b.end(), insert_iterator<set<int> >(c,c.begin()));

    set_intersection(a.begin(),a.end(),b.begin(),b.end(),insert_iterator<set<int> >(c,c.begin()));


    set_difference(a.begin(),a.end(),b.begin(),b.end(),insert_iterator<set<int> >(c,c.begin()));

    以下是收集的关于set的问题:

    (1)为何map和set的插入删除效率比用其他序列容器高?

    因为对于关联容器来说,不需要做内存拷贝和内存移动。set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。(用节点表示二叉树结构)

    因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点也OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。

    (2)对set进行增删操作后,迭代器iterator如何变化?

    set的迭代器本质上是const_iterator,这是因为RB-tree的结构依赖于数据的组织,如果允许通过iterator改变set元素值,会严重破坏RB-tree结构。

    iterator这里就相当于指向节点的指针,内存没有变,指针就没有失效,只是指向的内容变化了。进行增删操作后,只影响本身的迭代器,对其它迭代器无影响。

    示例代码:

     1 void test_set()
     2 {
     3     set<int> a;
     4     for (int i=2;i<9;i++)
     5     {
     6         a.insert(i);
     7     }
     8     a.insert(1);
     9     cout<<"set中第一个元素:"<<*a.begin()<<endl;
    10     cout<<"set中元素个数:"<<a.size()<<endl;
    11     int arr[]={9,10,11};
    12     a.insert(arr,arr+2);
    13     set<int>::iterator iter;
    14     for (iter=a.begin();iter!=a.end();iter++)
    15     {
    16         cout<<*iter<<" ";
    17     }
    18     cout<<endl;
    19     a.erase(1);
    20     set<int>::iterator  itl=a.begin();
    21     itl++;
    22     itl++;
    23     a.erase(a.begin(),itl);
    24     for (iter=a.begin();iter!=a.end();iter++)
    25     {
    26         cout<<*iter<<" ";
    27     }
    28     return;
    29 }
    View Code

    结果:

    说明几点:元素1是第二次插入的,却在set第一个位置上,验证了set自动排序(升序);

    在用数组插入时,我的区间是(arr,arr2)即(9,10,11),但是结果中没有11,说明insert()内区间也是左闭右开,即[arr,arr+2);这和下面的erase一样。

    二、map/mutilmap

     map是映射之意,即用键值(key)映射实值数据(value)。

    对比set,map的关联特效更加明显,但类似set,map也不允许有相同的键值,要想有相同键值可用mutilmap。

    可能你会疑问set只有一个相同的值,为何也算关联?这是因为set和map都是相同的数据结构,

    它们都由pair这种结构生成,并根据pair第一个元素值进行排序。

    map的常用操作:

    定义map: map<int,string> b;

    插入数据:b.insert(map<int, string>::value_type(1,"aaa"));

    或数组插入:b[1]="aaa";

    删除数据:b.erase(1);  或b.erase(b.begin(), iter);//注意删除区间是左闭右开

    删除所有数据:b.clear();

    数据遍历方式:迭代器遍历:

    map<int, string>::iterator iter;

    for(iter=b.begin();iter!=b.end();iter++)

    { cout<<iter->first<<" "<<iter->second<<endl;  }

    数组遍历:

    int bsize=b.size();

    for(int i=1;i<=bsize;i++)

    { cout<<b[i]<<endl; }

    示例代码:

     1 void test_map()
     2 {
     3     map<int, string> week;
     4     week.insert(map<int,string>::value_type(1,"monday"));
     5     week.insert(map<int,string>::value_type(2,"tuesday"));
     6     week[3]="wednesday";
     7     week[4]="thursday";
     8     week[5]="friday";
     9     week[6]="saturday";
    10     week[7]="sunday";
    11     map<int, string>::iterator iter;
    12     for (iter=week.begin();iter!=week.end();iter++)
    13     {
    14         cout<<iter->first<<":"<<iter->second<<"  ";
    15     }
    16     cout<<endl;
    17     ///下面演示两种插入方式插入相同键值的不同结果
    18     week.insert(map<int, string>::value_type(1,"errorday1"));
    19     cout<<"insert之后:"<<week[1]<<endl;
    20     week[1]="errorday2";
    21     cout<<"数组插入后:"<<week[1]<<endl;
    22 
    23     week.erase(1);
    24     map<int, string>::iterator itr=week.begin();
    25     itr++;
    26     itr++;
    27     week.erase(week.begin(), itr);
    28     for (iter=week.begin();iter!=week.end();iter++)
    29     {
    30         cout<<iter->first<<":"<<iter->second<<" ";
    31     }
    32 
    33     return;
    34 }
    View Code

    结果:

    说明几点:插入对比可看出insert插入相同键值的数据不会覆盖,而数组方式插入数据会覆盖相同键值的数据。

    掌握这些基本知识后,可以试着用set和map去解决下实际问题。推荐看看这个写的。以后我也会陆续添加容器的应用实例。

  • 相关阅读:
    Codeforces Round #578 (Div. 2) 训练总结及题解
    docker
    使用java遍历Map集合的方式
    SpringCloud集成rabbitmq:org.springframework.amqp.AmqpConnectException: java.net.ConnectException的解决办法
    创建新Docker容器时出现“The container name "/xxx" is already in use by container xxxxxxxxxxx...”问题的解决办法
    springBoot 项目中,使用定时任务报错
    java获取当前日期和前一周、前一月、前一年的日期
    用户行为PV&UV
    使用IDEA开发,多模块依赖中,找不到依赖: 程序包xxx.xxx.xxx不存在的问题
    Java获取本地IP地址和主机名
  • 原文地址:https://www.cnblogs.com/chenzhefan/p/5708620.html
Copyright © 2011-2022 走看看