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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: [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月前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
1月前
|
C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
24 5
|
1月前
|
存储 C++ 索引
|
2月前
|
存储 C++ 容器
开发与运维数组问题之C++标准库中提供数据容器作为数组的替代如何解决
开发与运维数组问题之C++标准库中提供数据容器作为数组的替代如何解决
43 5
|
1月前
|
安全 编译器 容器
C++STL容器和智能指针
C++STL容器和智能指针
|
1月前
|
C++ 容器
C++中自定义结构体或类作为关联容器的键
C++中自定义结构体或类作为关联容器的键
32 0
|
1月前
|
存储 缓存 NoSQL
【C++】哈希容器
【C++】哈希容器
|
1月前
|
关系型数据库 C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树
24 0
|
2月前
|
安全 程序员 C++
C++一分钟之-C++中的并发容器
【7月更文挑战第17天】C++11引入并发容器,如`std::shared_mutex`、`std::atomic`和线程安全的集合,以解决多线程中的数据竞争和死锁。常见问题包括原子操作的误用、锁的不当使用和迭代器失效。避免陷阱的关键在于正确使用原子操作、一致的锁管理以及处理迭代器失效。通过示例展示了如何安全地使用这些工具来提升并发编程的安全性和效率。
32 1
|
2月前
|
C语言 C++ 开发者
C++基础知识(一:命名空间的各种使用方法)
C++在C的基础上引入了更多的元素,例如类,类的私密性要比C中的结构体更加优秀,引用,重载,命名空间,以及STL库,模板编程和更多的函数,在面向对象的编程上更加高效。C语言的优势则是更加底层,编译速度会更快,在编写内核时大多数都是C语言去写。 在C++中,命名空间(Namespace)是一种组织代码的方式,主要用于解决全局变量、函数或类的命名冲突问题。命名空间提供了一种封装机制,允许开发者将相关的类、函数、变量等放在一个逻辑上封闭的区域中,这样相同的名字在不同的命名空间中可以共存,而不会相互干扰。