C++容器基础之map详解

一、概述

1、是什么

    map是一类关联式容器,关联的本质在于元素值与某个特定的键相关联。增删节点对迭代器影响很小,对于迭代器而言,不可修改键,只能修改其对应的值。map内部自建一棵红黑树,对内部元素有自动排序的功能。

    红黑树:一种二叉查找树,此外在每个节点上增加一个存储位表示节点的颜色,可以是red,也可以是black。通过对一条从根到叶子节点的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长两倍,因此是接近平衡的。作为一个二叉查找树,满足二叉查找树的一般性质。

    二叉查找树,即有序二叉树,有如下性质:

  • 任意节点的左子树不空,则左子树上所有节点的值均小于它的跟节点的值
  • 任意节点的右子树不空,则右子树上所有节点的值均大于它的跟节点的值
  • 任意节点的左、右子树也分别是二叉查找树
  • 没有键值相等的节点

    二叉查找树高度为lgn,一般操作执行时间为O(logn),如果二叉查找树退化为一棵具有n个节点的线性链后,则这些操作最坏情况的运行时间为O(n)。而红黑树增加了着色和相关性质,是的红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(logn)。

    红黑树性质

  • 每个节点要么是red,要么是black
  • 跟节点是black
  • 每个叶子结点都是black
  • 如果一个节点是red,那么它的两个儿子都是black
  • 对于任意节点而言,其到叶子结点的每条路径都包含相同数目的black节点

   这5个性质保证了一棵有n个节点的红黑树始终保持logn的高度。

2、功能

     首先,使用map需要包含头文件#include<map>。自动简历key-value的一一对应关系,key和value可以是任意类型,但是key的类型需要支持<操作符。由上述可知,查找的复杂度基本是logn,如果有1000个记录,则最多需要查询10次,如果有1000000个记录,最多需要查询20次。当然也有添加、删除、修改value、遍历等功能。

二、map的使用

1、插入

    map<int, string> m_str;
    pair<map<int, string>::iterator, bool> is_suc;
    //map的插入有三种方式
    //1、insert(pair<>)
    is_suc = m_str.insert(pair<int, string>(2, "yuhy"));
    //可以用is_suc判断是否插入成功
    if(is_suc.second){
        cout << "successful" << endl;
    }else{
        cout << "failed" << endl;
    }
    //2、insert (value_type)
    m_str.insert(map<int, string>::value_type(5, "scott"));
    //3、用数组方式插入数据
    m_str[1] = "LiMing";

2、遍历

    //遍历方式四种:
    //1、for each
    for(auto it : m_str){
        cout << "key:" << it.first << ", value:" << it.second << endl;
    }
    cout << "-------" << endl;
    //2、前向迭代器
    for(auto it = m_str.begin(); it != m_str.end(); it++){
        cout << "key:" << it->first << ", value:" << it->second << endl;
    }
    cout << "-------" << endl;
    //3、反向迭代器
    for(auto it = m_str.rbegin(); it != m_str.rend(); it++){
        cout << "key:" << it->first << ", value:" << it->second << endl;
    }
    cout << "-------" << endl;
    //4、数组遍历方式
    for(int i=0; i<m_str.size(); i++){
        cout << "value:" << m_str[i] << endl;
    }

3、访问

    //operator[]和at()
    map<int, int> t_map;
    t_map[2] = 2;
    t_map[4] = 4;
    t_map[6] = 6;

    cout << "key=2, value=" << t_map.at(2) << endl;

    for(auto it : t_map){
        cout << "key:" << it.first << ",value:" << it.second << endl;
    }
    t_map.at(2) += 1;
    cout << "key=8, value=" << t_map[8] << endl;
    for(auto it : t_map){
        cout << "key:" << it.first << ",value:" << it.second << endl;
    }

operator[]和at()的区别:

使用map[key],如果key不存在,则会自动为您添加一个键,并对值进行初始化。

使用map.at(key), 会检查key,如果key不存在则会跑出异常。

4、查找

    //查找,两种
    //1、count,查询关键字是否出现,无法判定其位置,返回值0或者1,1表示存在
    cout << "5 是否出现 :" ;
    if(m_str.count(5) == 1){
        cout << "是";
    }else{
        cout << "否";
    }
    cout << endl;
    //2、find,返回值是迭代器,如果不存在则返回end()
    map<int, string>::iterator res_find = m_str.find(5);
    if(res_find == m_str.end()){
        cout << "yuhy 不存在" << endl;
    }else{
        cout << "yuhy key:" << res_find->first << ", value:" << res_find->second << endl;
    }

5、删除

    //删除
    //1、erase(k),删除键为k的元素,并返回删除的个数
    map<int, string>::size_type res_erase = m_str.erase(5);
    cout << "num of erase:" << res_erase << endl;
    //2、erase(it),删除迭代器it指向的元素
    m_str.erase(m_str.find(1));
    //3、erase(it_f, it_e),删除一段范围内的元素
    m_str.erase(m_str.begin(), m_str.end());

6、排序

map默认key是从小到大排序。

扫描二维码关注公众号,回复: 11947968 查看本文章

map定义:

template<class Key,
        class T, 
        class Compare = less<Key>, 
        class Allocator = allocator<pair<const Key, T>>
        > 
class map;

有四个参数,第一个是key,第二个是value,第三个参数提供了默认的less,less是STL中的一个函数对象,调用操作符的类,其对象常常称为函数对象,他们是行为类似函数的对象。表现出一个函数的特征,就是通过“对象名 + (参数列表)”的方式使用一个类,其实质是对operator()操作符的重载。

less的实现:

template <class T>
struct less : binary_function< T , T, bool>{
    bool operator()(const T& x, const T& y) const{
        return x < y;
    }
};

里面仅仅是对()运算符的重载,调用的是T的<运算符。当然,与less相对的还有greater。

方式一:如果key是自定义对象,重写<操作符

class Student{
public:
    std::string name_;
    int age_;
    Student(std::string name, int age):name_(name),age_(age){}
    Student(){}
    bool operator < (const Student &s) const {
        std::cout << "operator : < " << std::endl;
        if(name_ != s.name_){
            return name_ < s.name_;
        }else{
            return age_ < s.age_;
        }
    }

};

    map<Student, string> m_stu;
    m_stu.insert(pair<Student, string>(Student("yyy", 20), "yyy"));
    m_stu.insert(pair<Student, string>(Student("aaa", 20), "aaa"));
    m_stu.insert(pair<Student, string>(Student("yyy", 18), "yyy"));
    cout << "--- 排序 ---" << endl;
    for(auto it : m_stu){
        cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
    }

方式二、重载()

class Comp{
public:
    bool operator () (const Student &left, const Student &right) const {
        std::cout << "operator ()" << std::endl;
        if(left.name_ != right.name_){
            return left.name_ < right.name_;
        }else{
            return left.age_ < right.age_;
        }
    }
};

    map<Student, string, Comp> m_stu;
    m_stu.insert(pair<Student, string>(Student("yyy", 20), "yyy"));
    m_stu.insert(pair<Student, string>(Student("aaa", 20), "aaa"));
    m_stu.insert(pair<Student, string>(Student("yyy", 18), "yyy"));
    cout << "--- 排序 ---" << endl;
    for(auto it : m_stu){
        cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
    }

方式三:

class Student{
public:
    std::string name_;
    int age_;
    Student(std::string name, int age):name_(name),age_(age){}
    Student(){}

    std::string show(){
        return "name:" + name_ + ", age:" + std::to_string(age_);
    }

    bool operator < (const Student &s) const {
        std::cout << "operator : < " << std::endl;
        if(name_ != s.name_){
            return name_ < s.name_;
        }else{
            return age_ < s.age_;
        }
    }

};

bool comp (const Student &left, const Student &right){
    cout << "comp method" << endl;
    if(left.name_ != right.name_){
        return left.name_ < right.name_;
    }else{
        return left.age_ < right.age_;
    }
}

map<Student, string, decltype(comp)*> m_stu(comp);
    m_stu.insert(pair<Student, string>(Student("yyy", 20), "yyy"));
    m_stu.insert(pair<Student, string>(Student("aaa", 20), "aaa"));
    m_stu.insert(pair<Student, string>(Student("yyy", 18), "yyy"));
    cout << "--- 排序 ---" << endl;
    for(auto it : m_stu){
        cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
    }

方式四:对map中的value进行排序

    首先想到的是STL中的sort算法,但是sort只能对序列容器进行排序,只能是线性的(如vector、list、deque)。map是个集合容器,存储的元素是pair,不是线性存储的,所以不能直接用sort。

   但是我们可以将map中的元素放到序列容器中,再对序列容器中的元素进行排序。有一个必要条件,序列容器中的元素必须是可比较的,即实现了<操作的。

    在使用sort的时候,传入比较函数,实现对pair中的value进行比较。

bool cmp_value(const pair<Student, string>& a, const pair<Student, string>& b) {
    return b.second < a.second;
}
    map<Student, string, decltype(comp)*> m_stu(comp);
    m_stu.insert(pair<Student, string>(Student("yyy", 20), "yyy"));
    m_stu.insert(pair<Student, string>(Student("aaa", 20), "aaa"));
    m_stu.insert(pair<Student, string>(Student("yyy", 18), "yyy"));
    cout << "--- 排序 ---" << endl;
    for(auto it : m_stu){
        cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
    }

    vector<pair<Student, string>> vec(m_stu.begin(), m_stu.end());
    sort(vec.begin(), vec.end(), cmp_value);
    for(auto it : vec){
        cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
    }

猜你喜欢

转载自blog.csdn.net/yu532164710/article/details/105214790