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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器镜像服务 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;
    }

无序容器

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

目录
相关文章
|
3天前
|
存储 C++ 容器
C++标准库中提供了哪些数据容器作为数组的替代
C++标准库中提供了哪些数据容器作为数组的替代
14 5
|
10天前
|
安全 程序员 C++
C++一分钟之-C++中的并发容器
【7月更文挑战第17天】C++11引入并发容器,如`std::shared_mutex`、`std::atomic`和线程安全的集合,以解决多线程中的数据竞争和死锁。常见问题包括原子操作的误用、锁的不当使用和迭代器失效。避免陷阱的关键在于正确使用原子操作、一致的锁管理以及处理迭代器失效。通过示例展示了如何安全地使用这些工具来提升并发编程的安全性和效率。
14 1
|
23天前
|
C语言 C++ 开发者
C++基础知识(一:命名空间的各种使用方法)
C++在C的基础上引入了更多的元素,例如类,类的私密性要比C中的结构体更加优秀,引用,重载,命名空间,以及STL库,模板编程和更多的函数,在面向对象的编程上更加高效。C语言的优势则是更加底层,编译速度会更快,在编写内核时大多数都是C语言去写。 在C++中,命名空间(Namespace)是一种组织代码的方式,主要用于解决全局变量、函数或类的命名冲突问题。命名空间提供了一种封装机制,允许开发者将相关的类、函数、变量等放在一个逻辑上封闭的区域中,这样相同的名字在不同的命名空间中可以共存,而不会相互干扰。
|
16天前
|
Java 应用服务中间件 持续交付
Java面试题:简述Docker等容器化技术的原理及其在Java应用部署中的作用。
Java面试题:简述Docker等容器化技术的原理及其在Java应用部署中的作用。
26 0
|
23天前
|
C++
C++基础知识(二:引用和new delete)
引用是C++中的一种复合类型,它是某个已存在变量的别名,也就是说引用不是独立的实体,它只是为已存在的变量取了一个新名字。一旦引用被初始化为某个变量,就不能改变引用到另一个变量。引用的主要用途包括函数参数传递、操作符重载等,它可以避免复制大对象的开销,并且使得代码更加直观易读。
|
23天前
|
算法 编译器 C++
C++基础知识(三:哑元和内联函数和函数重载)
在C++编程中,"哑元"这个术语虽然不常用,但可以理解为在函数定义或调用中使用的没有实际功能、仅作为占位符的参数。这种做法多见于模板编程或者为了匹配函数签名等场景。例如,在实现某些通用算法时,可能需要一个特定数量的参数来满足编译器要求,即使在特定情况下某些参数并不参与计算,这些参数就可以被视为哑元。
|
23天前
|
C++
C++基础知识(四:类的学习)
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
|
23天前
|
自然语言处理 程序员 C++
C++基础知识(五:运算符重载)
运算符重载是C++中的一项强大特性,它允许程序员为自定义类型(如类或结构体)重新定义标准运算符的行为,使得这些运算符能够适用于自定义类型的操作。这样做可以增强代码的可读性和表达力,使得代码更接近自然语言,同时保持了面向对象编程的封装性。
|
23天前
|
存储 编译器 C++
C++基础知识(六:继承)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
23天前
|
存储 编译器 C++
C++基础知识(七:多态)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。