STL中的映射 - map

作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

简介

映射(Map)是STL的一个关联容器,它提供一对一(key - value)的数据处理能力。它的特点是增加和删除节点对迭代器的影响很小,对于迭代器来说,可以修改实值,而不能修改key。map内部数据结构是一颗红黑树(一 种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的

map中根据key值快速查找记录,查找的复杂度基本是Log(N),即如果有1000个记录,最多查找10次,1,000,000个记录,最多查找20次。

如何把自定义的类或者结构当做map的key

已知map内部数据结构是一颗红黑树,并且这颗树具有对数据自动排序的功能。注意这里的排序仅仅是针对map中的key进行排序的,而value是在确定了key之后通过红黑树的翻转获取到的

所以如果我们想把自己定义的类或者结构作为map的key的话,那么就必须要满足两个条件:

  1. 这个类是可比较的。用代码来解释的话就是需要我们重载比较运算符 ‘<’ ,或者定义一个比较操作符,还可以为用户自定义类型特化std::less;
  2. ==key在容器内是需要相互拷贝的,所以还需要支持拷贝运算以及重载赋值运算符 ‘=’ ==;
#include <iostream>
#include <string>
#include <map>
using namespace std;

class Student
{
public:
    Student(string stu_name, int stu_age): name(stu_name), age(stu_age) {}
    Student(const Student& stu)
    {
        name = stu.name;
        age = stu.age;
    }

    string getName() { return name; }
    void setName(string stu_name) { name = stu_name; }

    int getAge() { return age; }
    void setAge(int stu_age) { age = stu_age; }

    bool operator < (const Student& stu) const { return age < stu.age; }

    string print() const
    {
        return "name: " + name + ", age: " + std::to_string(age);
    }

private:
    string name;
    int age;
};

int main()
{
    map<Student, int> studentMap;
    studentMap.insert(pair<Student, int>(Student("billy", 18), 1));
    studentMap.insert(map<Student, int>::value_type(Student("ben", 20), 2));
    Student stu("kitty", 19);
    studentMap[stu] = 3;

    map<Student, int>::iterator iter;
    for(iter = studentMap.begin(); iter != studentMap.end(); iter++)
        cout << "key: " << iter->first.print() << ", value: " << iter->second << endl;

    return 0;
}

运行结果:
key: name: billy, age: 18, value: 1
key: name: kitty, age: 19, value: 3
key: name: ben, age: 20, value: 2

根据运行结果可以看出,map中的key是根据age从小到大排序的。
这里遇到一个bug记录一下:

error: passing ‘const Student’ as ‘this’ argument of discards qualifiers [-fpermissive]

解析:将一个const变量传入了一个非const函数
解决方法:

  1. 修饰的变量的const去掉
  2. 将非const函数改成const函数

常用函数

  1. 插入数据
    map中插入数据有三种方式,上述例子中正好都用到了,那么我们就基于上述例子来看一下三种方式有什么不同。
int main()
{
    map<Student, int> studentMap;
    studentMap.insert(pair<Student, int>(Student("billy", 18), 1));
    studentMap.insert(map<Student, int>::value_type(Student("ben", 20), 2));
    Student stu("kitty", 19);
    studentMap[stu] = 3;

    studentMap.insert(pair<Student, int>(Student("billy", 18), 4));
    studentMap.insert(map<Student, int>::value_type(Student("ben", 20), 5));
    studentMap[stu] = 6;

    map<Student, int>::iterator iter;
    for(iter = studentMap.begin(); iter != studentMap.end(); iter++)
        cout << "key: " << iter->first.print() << ", value: " << iter->second << endl;

    return 0;
}

运行结果:
key: name: billy, age: 18, value: 1
key: name: kitty, age: 19, value: 6
key: name: ben, age: 20, value: 2

根据运行结果我们可以得出结论:两种 insert 函数的效果是完全一样的,当插入一个新的数据时,如果map中已有这个关键字时,insert操作是插入不了的;而用数组方式就不同了,它会覆盖该关键字对应的值

  1. 遍历
    map的遍历主要是通过迭代器和数组的形式(不建议使用数组的形式来遍历map);
int main()
{
    map<int, Student> studentMap;
    studentMap.insert(pair<int, Student>(1, Student("billy", 18)));
    studentMap.insert(map<int, Student>::value_type(2, Student("ben", 20)));
    Student stu("kitty", 19);
    studentMap[3] = stu;

    map<int, Student>::iterator iter;	// 正向迭代器
    for(iter = studentMap.begin(); iter != studentMap.end(); iter++)
        cout << "key: " << iter->first << ", value: " << iter->second.print() << endl;

    cout << endl;

    map<int, Student>::reverse_iterator re_iter;	//反向迭代器
    for(re_iter = studentMap.rbegin(); re_iter != studentMap.rend(); re_iter++)
        cout << "key: " << re_iter->first << ", value: " << re_iter->second.print() << endl;

    cout << endl;

    for(int i = 1; i <= studentMap.size(); i++)
    {
        cout << studentMap[i].print() << endl;
    }

    return 0;
}

运行结果:
key: 1, value: name: billy, age: 18
key: 2, value: name: ben, age: 20
key: 3, value: name: kitty, age: 19

key: 3, value: name: kitty, age: 19
key: 2, value: name: ben, age: 20
key: 1, value: name: billy, age: 18

name: billy, age: 18
name: ben, age: 20
name: kitty, age: 19
  1. 查找数据
    map中查找数据共有3种方法,分别是 count 函数、find 函数以及 lower_bound 函数和 upper_bound 函数
  • count:用来判定关键字在map中是否出现过,没有出现返回0,有出现则返回1。该函数的缺点是无法定位数据出现的位置;
  • find:定位数据出现的位置并返回一个迭代器,当数据出现时返回数据所在位置的迭代器,如果没有查找到数据,则返回end函数的迭代器;
  • lower_bound:返回map中第一个大于或等于key的迭代器;
  • upper_bound:返回map中第一个大于key的迭代器
int main()
{
    map<int, Student> studentMap;
    studentMap.insert(pair<int, Student>(1, Student("billy", 18)));
    studentMap.insert(map<int, Student>::value_type(2, Student("ben", 20)));
    Student stu("kitty", 19);
    studentMap[3] = stu;

    if ( studentMap.count(1) ) {
        cout << "It's been found !" << endl;
    } else {
        cout << "Do not Find !" << endl;
    }

    if ( studentMap.count(4) ) {
        cout << "It's been found !" << endl;
    } else {
        cout << "Do not Find !" << endl;
    }

    map<int, Student>::iterator iter = studentMap.find(2);
    if( iter != studentMap.end() ) {
        cout << "It's been found and the value is: " << iter->second.print() << endl;
    } else {
        cout << "Do not Find !" << endl;
    }

    map<int, Student>::iterator lowerIter = studentMap.lower_bound(3);
    if( lowerIter != studentMap.end() ) {
        cout << "It's been found and the value is: " << lowerIter->second.print() << endl;
    } else {
        cout << "Do not Find !" << endl;
    }

    map<int, Student>::iterator upperIter = studentMap.upper_bound(3);
    if( upperIter != studentMap.end() ) {
        cout << "It's been found and the value is: " << upperIter->second.print() << endl;
    } else {
        cout << "Do not Find !" << endl;
    }

    return 0;
}

运行结果:
It's been found !
Do not Find !
It's been found and the value is: name: ben, age: 20
It's been found and the value is: name: kitty, age: 19
Do not Find !
  1. 删除数据
    map中删除数据主要用的是 erase 函数,参数可以是迭代器或者直接传key,也可以传入一个起始迭代器和一个终止迭代器来实现范围删除;
int main()
{
    map<int, Student> studentMap;
    studentMap.insert(pair<int, Student>(1, Student("billy", 18)));
    studentMap.insert(map<int, Student>::value_type(2, Student("ben", 20)));
    Student stu("kitty", 19);
    studentMap[3] = stu;

    map<int, Student>::iterator eraseIter = studentMap.find(1);
    studentMap.erase(eraseIter);

    int ret = studentMap.erase(2);
    if (ret) {
        cout << "delete successful !" << endl;
    }

    //studentMap.erase(studentMap.begin(), studentMap.end());	// 此方法等价于 clear()
    map<int, Student>::iterator iter;
    for(iter = studentMap.begin(); iter != studentMap.end(); iter++)
        cout << "key: " << iter->first << ", value: " << iter->second.print() << endl;
        
    return 0;
}

运行结果:
delete successful !
key: 3, value: name: kitty, age: 19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值