[C++ 面试基础知识总结] 关联容器

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: [C++ 面试基础知识总结] 关联容器参考书籍:《C++ Primer》目录C 面试基础知识总结 关联容器目录关联容器类型关联容器概述定义关联容器关键字类型的要求pair关联容器操作关联容器迭代器添加元素删除元素访问元素无序容器关联容器类型标准库共提供了8个关联容器 map 关联数组:保

[C++ 面试基础知识总结] 关联容器

参考书籍:《C++ Primer》

目录

关联容器类型

标准库共提供了8个关联容器
map 关联数组:保存关键字-值对
set 关键字即值,即只保存关键字的容器
multimap 关键字可重复出现的map
multiset 关键字可重复出现的set
unordered_map 用哈希函数组织的map
unordered_set 用哈希函数组织的set
unordered_multimap 用哈希函数组织,关键字可重复出现的map
unordered_multiset 用哈希函数组织,关键字可重复出现的set

map是关键字-值对的集合,通常被称为关联数组。关联数组与正常数组类似,不同之处在于其下标不必是整数,是一个关键字。而set就是关键字的简单集合。

关联容器概述

定义关联容器

    // 初始化map时,必须提供关键字类型和值类型。每个关键字-值对书写格式为{ key,value}
    map<string , int> scores = {{"Summer",80},{"Seven",90},{"Leg",60}};
    set<string> names = {"Summer","Seven","Leg"};

map和set的关键字必须是唯一的,而multimap和multiset没有此限制

   //定义一个有20个元素的vector,保存0-9每个整数的两个拷贝
    vector<int> v;
    for (int i = 0; i != 10; ++i) {
        v.push_back(i);
        v.push_back(i);
    }

    //用vetor初始化set和multiset (关联容器支持容器通用的初始化,赋值和操作)
    set<int> iset(v.begin(),v.end());
    multiset<int> miset(v.begin(),v.end());

    cout << iset.size() << endl;  //结果为10
    cout << miset.size() << endl;  //结果为20

关键字类型的要求

有序关联容器的关键字类型必须定义元素比较的方法,默认情况下,标准库使用关键字类型的<运算符来比较两个关键字。也提供一个自定义的运算符。所提供的操作必须在关键字类型上定义一个严格弱序。即具备以下性质:
1.两个关键字不能同时“小于等于”对方。
2.如果k1“小于等于”k2,且k2“小于等于”k3,那么k1必须“小于等于”k3。
3.如果存在两个关键字,任何一个都不“小于等于”另一个,那么两个关键字“等价”,如果k1“等价于”k2,且k2“等价于”k3,那么k1必须“等价于”k3。

pair

pair定义在头文件utility中,一个pair保存两个数据对象,类似容器,创建pair时必须提供两个类型名,pair的数据成员将具有对应的类型。

    //将一个map容器中的元素保存到一个元素类型为pair的vector中
    map<string , int> scores = {{"Summer",80},{"Seven",90},{"Leg",60}};
    vector<pair<string, int>> v;

    for(const auto &score : scores){
        //初始化一个pair,first和second分别为键值对中的关键字和值
        pair<string, int> p (score.first,score.second);
        v.push_back(p);
    }

    for (const auto &p : v) {
        //pair的first和second分别为pair中的两个数据成员 输出结果Leg 60 Seven 90 Summer 80 (map已根据关键字自动排序)
        cout << p.first << " " << p.second << " ";
    }
    cout << endl;

关联容器操作

关联容器迭代器

map和set的关键字都为常量,不能改变。

    map<string , int> scores = {{"Summer",80},{"Seven",90},{"Leg",60}};
    // mapIt类型为map<string , int>::iterator,指向类型为pair<const string,int>的对象
    auto mapIt = scores.begin();
    while (mapIt != scores.end()) {
        //不能改变map关键字,但可以改变值
        cout << mapIt->first << " " << ++(mapIt++->second) << endl;
    }

    set<string> names = {"Summer","Seven","Leg"};
    //setIt类型为set<string>::iterator,指向类型为const string的对象
    for (auto setIt = names.begin(); setIt != names.end(); ++setIt) {
        //只能读取set关键字,不能改变
        cout << *setIt << endl;
    }

注意:由于关联容器关键字的const属性,不能讲关联容器传递给修改或重排容器元素的算法,所以通常不对关联容器使用泛型算法。

添加元素

调用insert函数可向map或set中添加一个元素或一个范围,若元素关键字已经存在,则不会有任何操作。

    set<string> names = {"Summer","Seven","Leg"};
    names.insert("CannonFly");

    map<string , int> scores = {{"Summer",80},{"Seven",90},{"Leg",60}};
    //以下4种写法等价
    scores.insert({"CannonFly",85});
    scores.insert(make_pair("CannonFly", 85));
    scores.insert(pair<string, int>("CannonFly",85));
    scores.insert(map<string, int>::value_type("CannonFly",85));

insert函数的返回值类型pair<指向指定关键字的迭代器,bool(成功标志)>

例如上述map执行insert后返回的类型为pair

    map<string , int> scores = {{"Summer",80},{"Seven",90},{"Leg",60}};
    auto ret = scores.insert({"CannonFly",85});

可以使用insert的返回值

ret.first ret的第一个成员,是一个map迭代器,指向具有给定关键字的元素
ret.first->  ret的第一个成员解引,提取map中的元素,元素是一个pair
ret.first->first  map中元素的关键字部分
ret.first->second  map中元素的值部分
ret.second  ret的第二个成员,是一个bool值,表示是否插入成功

对于multimap,multiset,insert函数不返回标志是否成功bool值,因为可以插入相同关键字的元素。

删除元素

erase函数可以删除指定关键字的元素,迭代器所指的元素或者某个范围内的所有元素,返回值是实际删除的元素。

    map<string , int> scores = {{"Summer",80},{"Seven",90},{"Leg",60}};
    scores.erase("Summer");  //删除map中关键字为Summer的元素
    scores.erase(scores.begin());  //删除map中第一个元素
    scores.erase(scores.begin(),scores.end());  //删除map中所有元素

访问元素

map和unordered_map可以采取下标的方式对其中与关键字相关联的值进行操作,而set只有关键字而没有与其关联的值,所以不支持下标。map下标的索引是关键字。

    map<string , int> scores = {{"Summer",80},{"Seven",90},{"Leg",60}};
    scores["Summer"] = 85; 
    scores["CannonFly"] = 85;  // map中没有该关键字,则创建一个元素插入到map中,并对关联值初始化。   

除了下标之外,还有其他的查找元素的方法。

    map<string , int> scores = {{"Summer",80},{"Seven",90},{"Leg",60}};
    scores.find("CannonFly");  //查找元素,找到返回该元素的迭代器,未找到返回尾后迭代器
    scores.count("Summer");  //返回元素在容器中出现的次数

    //下面是两种在允许重复关键字的关联容器中输出所有同名关键字的操作
    multiset<string> names = {"Summer","Seven","Leg","Summer"};
    //lower_bound返回第一个不小于给定值的元素,names.upper_bound返回第一个大于给定值的元素
    for (auto begin = names.lower_bound("Summer"), end = names.upper_bound("Summer"); begin != end; ++begin) {
        cout << *begin << endl;
    }
    // equal_range返回一个迭代器pair,表示等于给定值的元素的范围
    for (auto pos = names.equal_range("Summer"); pos.first != pos.second; ++pos.first) {
        cout << *pos.first << endl;
    }

无序容器

无序关联容器不是使用比较运算法来组织元素,而是用一个哈希函数和关键字类型的==运算法。通常无序容器和其对应的有序容器是可以相互替换的,只是由于元素未按顺序存储,无序容器的输出通常会与有序容器不同。

目录
相关文章
|
1月前
|
编译器 C++ Python
【C/C++ 泡沫精选面试题02】深拷贝和浅拷贝之间的区别?
【C/C++ 泡沫精选面试题02】深拷贝和浅拷贝之间的区别?
32 1
|
1月前
|
设计模式 程序员 C++
【C++ 泛型编程 高级篇】C++模板元编程:使用模板特化 灵活提取嵌套类型与多容器兼容性
【C++ 泛型编程 高级篇】C++模板元编程:使用模板特化 灵活提取嵌套类型与多容器兼容性
259 2
|
1月前
|
编译器 C++
深入理解 C++ 语法:从基础知识到高级应用
了解C++基础语法,包括`#include &lt;iostream&gt;`引入输入输出库,`using namespace std`简化命名。`int main()`是程序入口,`cout &lt;&lt; &quot;Hello World!&quot;`用于输出文本。换行可使用`\n`或`endl`。注释使用`//`进行单行注释,`/* */`进行多行注释。
27 0
|
算法 程序员 C语言
【C++ 迭代器】深入探讨 C++ 迭代器:标准与自定义容器中的 begin() 和 cbegin()
【C++ 迭代器】深入探讨 C++ 迭代器:标准与自定义容器中的 begin() 和 cbegin()
50 0
|
1月前
|
算法 编译器 C++
【C++ 模板编程 基础知识】C++ 模板类部分特例化的参数顺序
【C++ 模板编程 基础知识】C++ 模板类部分特例化的参数顺序
21 0
|
1月前
|
敏捷开发 安全 API
C/C++ 工程师面试:如何精彩展示你的项目经验并获得高分
C/C++ 工程师面试:如何精彩展示你的项目经验并获得高分
73 0
|
1月前
|
存储 安全 编译器
【C++ 17 泛型容器对比】C++ 深度解析:std::any 与 std::variant 的细微差别
【C++ 17 泛型容器对比】C++ 深度解析:std::any 与 std::variant 的细微差别
56 1
|
1月前
|
存储 安全 算法
【C++ 17 包裹类 泛型容器 std::any】深入理解与应用C++ std::any:从泛型编程到多态设计
【C++ 17 包裹类 泛型容器 std::any】深入理解与应用C++ std::any:从泛型编程到多态设计
49 1
|
1月前
|
消息中间件 存储 算法
【C/C++ 泡沫精选面试题04】在实际项目中,多进程和多线程如何选择?
【C/C++ 泡沫精选面试题04】在实际项目中,多进程和多线程如何选择?
43 1
|
1月前
|
编解码 缓存 安全
【C/C++ 泡沫精选面试题03】谈谈C/C++ 智能指针?
【C/C++ 泡沫精选面试题03】谈谈C/C++ 智能指针?
31 1