美文网首页
(十四)C++篇-关联容器map和set

(十四)C++篇-关联容器map和set

作者: GoodTekken | 来源:发表于2022-06-22 23:00 被阅读0次

关联容器和顺序容器的本质差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素。

关联容器(Associative containers) 支持通过键来高效地查找和读取元素。两个基本的关联容器类型是 map set。map 的元素以键-值(key-value)对的形式组织:键用作元素在 map 中的索引,而值则表示所存储和读取的数据。set仅包含一个键,并有效地支持关于某个键是否存在的查询。

一般来说,如果希望有效地存储不同值的集合,那么使用 set 容器比较合适,而 map 容器则更适用于需要存储(乃至修改)每个键所关联的值的情况。在做某种文本处理时,可使用 set 保存要忽略的单词。而字典则是 map 的一种很好的应用:单词本身是键,而它的解释说明则是值。

set 和 map 类型的对象所包含的元素都具有不同的键,不允许为同一个键添加第二个元素。如果一个键必须对应多个实例,则需使用 multimap 或 multiset,这两种类型允许多个元素拥有相同的键。

1,关联容器类型
类型 定义
map 关联数组:元素通过键来存储和读取
set 大小可变的集合,支持通过键实现的快速读取
multimap 支持同一个键多次出现的 map 类型
multiset 支持同一个键多次出现的 set 类型

pairs:在 utility 头文件中定义
pairs 类型提供的操作

函数 定义
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 两个 pair 对象之间的小于运算,其定义遵循字典次序:如果 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 的(公有)数据成员
2,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>

在 map 的接口时,需谨记 value_type 是 pair 类型,它的值成员可以修改,但键成员不能修改。

3,map 类定义的类型
函数 定义
map<K,V>::key_type 在 map 容器中,用做索引的键的类型
map<K,V>::mapped_type 在 map 容器中,键所关联的值的类型
map<K,V>::value_type 一个 pair 类型,它的 first 元素具有 const map<K,V>::key_type 类型,而 second 元素则为 map<K,V>::mapped_type 类型
4,map 容器提供的 insert 操作
函数 定义
m.insert(e) e 是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中, 则插入一个值为 e.second 的新元素;如果该键在 m 中已存在,则保持 m 不变。该函数返回一个pair 类型对象,包含指向键为 e.first 的元素的 map 迭代器,以及一个 bool 类型的对象,表示是否插入了该元素
m.insert(beg,end) beg 和 end 是标记元素范围的迭代器,其中的元素必须为m.value_type 类型的键-值对。对于该范围内的所有元素,如果它的键在 m 中不存在, 则将该键及其关联的值插入到 m。返回 void 类型
m.insert(iter,e) e 是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中,则创建新元素,并以迭代器 iter 为起点搜索新元素存储的位置。返回一个迭代器,指向 m 中具有给定键的元素
5,查找并读取 map 中的元素

下标操作符给出了读取一个值的最简单方法:
map<string,int> word_count;
int occurs = word_count["foobar"];
但是,使用下标存在一个很危险的副作用:如果该键不在 map 容器中,那
么下标操作会插入一个具有该键的新元素。

不修改 map 对象的查询操作

函数 定义
m.count(k) 返回 m 中 k 的出现次数
m.find(k) 如果 m 容器中存在按 k 索引的元素,则返回指向该元素的迭代器。如果不存在,则返回超出末端迭代器
6,从 map 对象中删除元素

erase 函数返回被删除元素的个数。对于 map 容器,该值必然是 0 或 1。
如果返回 0,则表示欲删除的元素在 map 不存在。

函数 定义
m.erase(k) 删除 m 中键为 k 的元素。返回 size_type 类型的值,表示删除的元素个数
m.erase(p) 从 m 中删除迭代器 p 所指向的元素。p 必须指向 m 中确实存在的元素,而且不能等于 m.end()。返回 void
m.erase(b,e) 从 m 中删除一段范围内的元素, 该范围由迭代器对 b 和 e 标记。b 和 e 必须标记 m 中的一段有效范围: 即 b 和 e 都必须指向 m中的元素或最后一个元素的下一个位置。而且,b 和 e 要么相等(此时删除的范围为空),要么 b 所指向的元素必须出现在 e 所指向的元素之前。返回 void 类型
7,multimap 和 multiset 类型

map 和 set 容器中,一个键只能对应一个实例。而 multiset 和 multimap类型则允许一个键对应多个实例。例如,在电话簿中,每个人可能有单独的电话号码列表。在作者的文章集中,每位作者可能有单独的文章标题列表。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)

关联容器共享了顺序容器的许多操作。除此之外,关联容器还定义一些新操作,并对某些顺序容器同样提供的操作重新定义了其含义或返回类型,这些操作的差别体现了关联容器中键的使用。

关联容器的元素可用迭代器访问。标准库保证迭代器按照键的次序访问元素。begin 操作将获得拥有最小键的元素,对此迭代器作自增运算则可以按非降序依次访问各个元素。

测试代码(map):

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

int main()
{
    map<string, int> word_count;
    string word;
    cout << word_count["Anna"]<<endl; // print 0
    ++word_count["Anna"];
    cout << word_count["Anna"] << endl; // print 1
    return 0;
}

测试代码(set):

#include<stdio.h>
#include<set>
using namespace std;
int main()
{
    set<int> st;
    for (int i = 1; i <= 5; i++)
    {
        st.insert(i);
    }
    for (set<int>::iterator it = st.begin(); it != st.end(); it++)
    {
        printf("%d ", *it);
    }
    printf("\r\n");
    return 0;
}

输出结果:

tekken@tekken:~/C++WS$ ./a.out 
1 2 3 4 5 

测试代码(mutimap):

第一种输入表示要添加一个学生的信息,碰到这种输入,就记下学生的姓名、id 和分数。
第二种输入表示要查询分数为 score 的学生的信息,碰到这种输入,就输出已有记录中分数比查询分数低的最高分获得者的姓名、学号和分数。如果有多个学生满足条件,则输出学号最大的学生的信息。如果找不到满足条件的学生,则输出“Nobody”。

#include <iostream>
#include <map>  //使用multimap需要包含此头文件
#include <string>
using namespace std;
class CStudent
{
public:
    struct CInfo  //类的内部还可以定义类
    {
        int id;
        string name;
    };
    int score;
    CInfo info;  //学生的其他信息
};
typedef multimap <int, CStudent::CInfo> MAP_STD;
int main()
{
    MAP_STD mp;
    CStudent st;
    string cmd;
    while (cin >> cmd) 
    {
        if (cmd == "Add") 
        {
            cin >> st.info.name >> st.info.id >> st.score;
            mp.insert(MAP_STD::value_type(st.score, st.info));
        }
        else if (cmd == "Query") 
        {
            int score;
            cin >> score;
            MAP_STD::iterator p = mp.lower_bound(score);
            if (p != mp.begin()) 
            {
                --p;
                score = p->first;  //比要查询分数低的最高分
                MAP_STD::iterator maxp = p;
                int maxId = p->second.id;
                for (; p != mp.begin() && p->first == score; --p) 
                {
                    //遍历所有成绩和score相等的学生
                    if (p->second.id > maxId) 
                    {
                        maxp = p;
                        maxId = p->second.id;
                    }
                }
                if (p->first == score) 
                { //如果上面的循环因为 p == mp.begin()
                    //而终止,则p指向的元素还要处理
                    if (p->second.id > maxId) 
                    {
                        maxp = p;
                        maxId = p->second.id;
                    }
                }
                cout << maxp->second.name << " " << maxp->second.id << " "<< maxp->first << endl;
            }
            else  //lower_bound 的结果就是 begin,说明没有分数比查询分数低
                cout << "Nobody" << endl;
        }
    }
    return 0;
}

输入案例:

Add Jack 12 78
Query 78
Query 81
Add Percy 9 81
Add Marry 8 81
Query 82
Add Tom 11 79
Query 80
Query 81

输出结果:

Nobody
Jack 12 78
Percy 9 81
Tom 11 79
Tom 11 79

测试代码(mutiset):

#include <iostream>
#include <set>  //使用multiset须包含此头文件
using namespace std;
template <class T>
void Print(T first, T last)
{
    for (; first != last; ++first)
    cout << *first << " ";
    cout << endl;
}
class A
{
private:
    int n;
public:
    A(int n_) { n = n_; }
    friend bool operator < (const A & a1, const A & a2)
    { return a1.n < a2.n; }
    friend ostream & operator << (ostream & o, const A & a2)
    { o << a2.n; return o; }
    friend class MyLess;
};
class MyLess
{
public:
    bool operator() (const A & a1, const A & a2)  //按个位数比较大小
    { return (a1.n % 10) < (a2.n % 10); }
};
typedef multiset <A> MSET1;  //MSET1 用“<”运算符比较大小
typedef multiset <A, MyLess> MSET2;  //MSET2 用 MyLess::operator() 比较大小
int main()
{
    const int SIZE = 6;
    A a[SIZE] = { 4, 22, 19, 8, 33, 40 };
    MSET1 m1;
    m1.insert(a, a + SIZE);
    m1.insert(22);
    cout << "1)" << m1.count(22) << endl;  //输出 1)2
    cout << "2)"; Print(m1.begin(), m1.end());  //输出 2)4 8 19 22 22 33 40
    MSET1::iterator pp = m1.find(19);
    if (pp != m1.end())  //条件为真说明找到
        cout << "found" << endl;  //本行会被执行,输出 found
    cout << "3)"; cout << *m1.lower_bound(22)<< "," << *m1.upper_bound(22) << endl; //输出 3)22,33
    pp = m1.erase(m1.lower_bound(22), m1.upper_bound(22));
    //pp指向被删元素的下一个元素
    cout << "4)"; Print(m1.begin(), m1.end());  //输出 4)4 8 19 33 40
    cout << "5)"; cout << *pp << endl;  //输出 5)33
    MSET2 m2;  //m2中的元素按n的个位数从小到大排序
    m2.insert(a, a + SIZE);
    cout << "6)"; Print(m2.begin(), m2.end());  //输出 6)40 22 33 4 8 19
    return 0;
}

输出结果:

tekken@tekken:~/C++WS$ ./a.out 
1)2
2)4 8 19 22 22 33 40 
found
3)22,33
4)4 8 19 33 40 
5)33
6)40 22 33 4 8 19 

相关文章

  • C++大厂面试真题

    C++标准库的map和set有什么区别,如何实现的? map和set都是C++的关联容器,其底层实现都是红黑树。 ...

  • (十四)C++篇-关联容器map和set

    关联容器和顺序容器的本质差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存...

  • BAT面试 STL常见面试题

    请你来说一下map和set有什么区别,分别又是怎么实现的?参考回答:map和set都是C++的关联容器,其底层实现...

  • C++ 容器梳理

    C++ 容器包括 顺序存储结构:vector list dequeue关联存储结构:set map multise...

  • C++ 关联容器

    11.1关联容器概述 关联容器有map和set两大类,map是关键字和值得映射,set是关键字的简单集合,它们分别...

  • STL与泛型编程第二周笔记 GeekBand

    1.关联容器map与set 关联容器(Associative containers)支持通过键来高效地查找和读取元...

  • 第11章 关联容器

    关联容器分类:set还是map、关键字是否重复、关键字是否有序。 11.1 使用关联容器 map类型通常被称为关联...

  • C++标准库学习(二):关联容器

    关联容器支持通过键来高效地查找和读取元素,两个基本的关联容器类型是map和set。map的元素以键-值(key-v...

  • 2020-07-04-《STL学习-001》

    vector、deque、list是序列式容器 set、map是关联式容器 HashTable:哈希表

  • STL细节

    容器分为三种1.序列性容器 vector list deque2.关联型容器 set multiset map...

网友评论

      本文标题:(十四)C++篇-关联容器map和set

      本文链接:https://www.haomeiwen.com/subject/rknsvrtx.html