红黑树下探玄机:C++ map&multimap 的幕后之旅

目录

map

map的构造

swap

[operator =](#operator =)

begin&end

empty&size

insert

erase

clear

find

count

[operator []](#operator [])

lower_bound

upper_bound

equal_range

multimap

multimap的insert

multimap的erase

总结


map

  • 与set不同,set中只放value,但在底层实际存放的是由<value, value>构成的键值对,map/multimap中存储的是真正的键值对<key, value>
  • map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素
  • 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的
    内容 , 键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
cpp 复制代码
 typedef pair<const key, T> value_type
  • class key : 键值对 pair 中key的类型
  • class T: 键值对 pair 中value的类型
  • Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比
    较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户
    自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
  • Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的
    空间配置器注意:在使用map时,需要包含头文件 - #include<map>

map的构造

  • empty :无参数的构造
  • range : 使用一段迭代器区间构造
  • copy : 拷贝构造
cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串") };

	map<string, string> m1;
	map<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	map<string, string> m3(m2);

	return 0;
}

swap

cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串") };

	map<string, string> m1;
	map<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	m1.swap(m2);

	return 0;
}

operator =

cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串") };

	map<string, string> m1;
	map<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	m1=m2;

	return 0;
}

begin&end

  • map的迭代器的使用与之前的set有所不同,map中存储的是一个类的对象pair,那么迭代器it进行解引用拿到的是pair这个类的对象,那么我们想要访问的是pair这个类对象中存储的first(key),second(value),这里进行解引用拿不到我们想要的值

直接 cout <<* it<<endl; 结果报错

  • 那么由于拿到的是pair这个类对象,那么我们可以使用箭头 ->,当迭代器中存储的是一个结构的使用,map迭代器封装了一个指向map的一个节点的指针,我们可以类似结构的指针的方式去访问它的成员函数,因为迭代器中重载了箭头 -> 运算符,小编推荐这种方式访问pair这个结构对象中存储的first(key),second(value)
cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串") };

	map<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	map<string, string>::iterator it1 = m2.begin();
	cout << it1->first <<" : "<<it1->second<< endl;
    
    map<string, string>dict; 
    dict.insert(make_pair("string", "字符串"));
    dict.insert(make_pair("sort", "排序"));
    dict.insert(make_pair("insert", "插入"));
    map<string, string>::iterator it = dict.begin();
    while (it != dict.end())
    {
	    cout << it->first<<":"<<it->second << endl;
	    ++it;
    }
    cout << endl;
    for (const auto& kv : dict)
    {
	    cout << kv.first << ":" << kv.second << endl;
    }

	return 0;
}
  • 范围for在使用上推荐规范使用即不改变值加const修饰,由于map中存储的是一个结构,拷贝传参消耗大,所以这里使用引用& ,同样的范围for的变量e拿到的是map节点中存储的pair这个结构对象的引用,那么既然是拿到的是一个对象,访问这个对象的成员变量应该使用 . 的方式进行访问pair这个结构对象中存储的first(key),second(value)
cpp 复制代码
for (const auto& e : m)
	{
		cout << e.first << ':' << e.second << endl;
	}
	

empty&size

  • empty判断容器内的数据是否为空
  • size返回容器内的数据节点的大小
cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串") };

	map<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	cout << m2.size() << endl;
	cout << m2.empty() << endl;

	    return 0;
    }

insert

  • single element : insert插入一个pair类型的键值对<key,value>,如果插入成功,那么返回一个pair类型的键值对<iterator,bool>,其中第一个数据first是插入位置的迭代器,第二个位置是 - >是否插入成功,由于map中的key值不能重复且只能有一个,如果key值已经有了,那么返回的是原key对应位置的迭代器以及false 表示插入失败 ,如果key值没有,那么返回的是新插入key对应位置的迭代器以及true 表示插入成功,由于return 不能返回两个值,所以如果想返回多个值,必须放在一个结构中进行返回,这里是放在了键值对pair中进行返回
  • with hint : 是在迭代器位置(这个迭代器位置对于编译器来说只是一个建议位置,具体插入在哪里还是要看原map的插入规则)插入一个pair类型的键值对<key,value>
  • range : 是插入一段 iterator 区间
cpp 复制代码
map<string, string >dict;
pair<string, string>kv1("insert", "插入");//C++98 :单参数才能隐式类型转换,多参数不行
//1 
dict.insert(kv1);
//2 
dict.insert(pair<string, string>("sort", "排序"));
//3 
dict.insert(make_pair("string", "字符串"));
 //C++11: 支持多参数隐式类型转换
//4 
dict.insert({ "string","字符串" });//相当于 //2 

erase

  • void erase : 删除一个迭代器位置
  • size_t type erase : 删除某个key值,返回值是删除的这个key值的个数 ,这是为了后面的multimap做准备,multimap可以允许有多个相同的key值,而map和mulitmap的关联性很大,所以为了接口的统一性将这里的删除值为key的erase的操作的返回值设置为了返回删除key值的个数,在map中当有这个key值的时候删除对应的节点成功返回1,当没有这个key值的时候删除对应节点失败返回0
  • void erase : 删除一段迭代器区间

clear

  • 清空map的元素
cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串") };

	map<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	m2.clear();

	return 0;
}

find

  • find传入key,如果key存在那么返回key对应位置的iterator,如果key值不存在,那么返回end()
cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串") };

	map<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	 cout << m2.find("insert")->second<< endl;

	return 0;
}

count

  • count是返回key值对应的节点个数,由于个数不能为负数,所以返回值是size_t类型的,奇怪,multimap允许多个key值存在,所以当myltimap调用count的时候可能key值对应的节点个数会有多个,为了map和multimap接口的统一性,我们给map也提供一个count接口,count接口在map中可以用于判空
cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串") };

	map<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	cout<<m2.count("insert")<<endl;

	return 0;
}

operator []

  • operator[]的原理是使用<key,T()>构造出一个pair的键值对,这个T()是调用的value对应类型的默认构造函数去生成对应类型的匿名对象,然后调用insert将键值对插入到map中
  • 如果map中已经有了key,那么insert会返回key位置所在的iterator,那么上面构造的键值对不会起作用,即不会影响原map中的key和对应的value
  • 如果map中没有key,那么insert会向map插入该键值对,并且返回新插入位置的iterator
  • 接下来operator会根据insert返回的迭代器拿到key对应的value,那么operator[]会将value以引用的形式返回,因为value被放在pair结构对象中,pair结构对象存储在map节点中,这个节点在operator[]函数外已存在
  • operator []的行为就是,在[]内输入key,返回得到key对应的value的引用,我们可以对这个value进行访问或修改
cpp 复制代码
 !!! pair是类,不是对象,pair<iterator ,bool> ret : 对象

  value& operator [] (const K& key)  能通过key返回value
  {
  pair<iterator ,bool> ret=insert(make_pair(key,value()));
  return pair.first->second;
  }

  pair.first 是 pair<string,int> 和 pair<iterator,bool> 是两个东西 : key的值与传过来的key相等于pair的指针(地址)

 1.  做到了这个值在 , 就返回这个值的个数(value),
 2.  如果这个值不在,那我就新创立以它为初始化一个新的值,并且value的值是零,返回value

用operator 统计次数

cpp 复制代码
map<string, int> countMap;
string arr[] = {"小明","小童","小夏","小雅","小欣","小池","小雅" };
for (auto e : arr)
{
	countMap[e]++; 
}
for(const auto& k: countMap)
{
	cout << k.first << " : " << k.second << endl;
}

value++ 好说
但countMap原本是空的,怎么有key和value了
pair<iterator,bool> insert(const value_type& val)

insert :

1.key已经在树里面,返回pair<树里面key所在节点的iterator,false>这个pair的value是false
2.key不在树里面,返回pair<新插入key所在节点的iterator,true>

operator []

  • 做到了这个值在 , 就返回这个值的 second(value)
  • 如果这个值不在,那我就新创立以它为初始化一个新的值,并且value的值是零,返回value

lower_bound

  • lower_bound是下界(>=)的意思,即寻找比给定key值等于大于的值的位置的迭代器,有等于优先是等于,其次是大于,如果找不到那么返回end()

upper_bound

  • upper_bound(>)是上界的意思,即寻找比给定key值大的值的位置的迭代器,如果找不到那么返回end()

ower_bound和upper_bound通常是搭配使用,用于寻找指定区间的迭代器(前闭区间 ,后开区间),通常要使用erase或insert插入一段区间,对区间进行操作,那么就会用到这两个函数

在set中小遍演示过,这里就不再多讲->请点击这里了解

equal_range

  • equal_range的意思是相等的范围,即去寻找和key值相等序列的开头和结尾位置的迭代器,同理 ,这里同样是为了和multimap保持接口的一致性,即multimap中可以有多个相同的key值,即使用equal_range去找key值会找到的是一段序列
  • 返回的pair中第一个iterator 是 和key值相等序列的开头位置的iterator , 第二个是结尾位置的迭代器
  • 如果给equal_range的key值,在set中没有,那么会返回比key值大的位置的迭代器作为开头位置的迭代器和结尾位置的迭代器进行返回,举例set数据序列为1,5,7,那么传入查找3作为key值进行查找,3没有,那么就会找比key值3大的值的迭代器即5位置的迭代器作为开头和结尾位置的迭代器,那么就会返回[5,5),那么由于成员函数对序列区间进行操作是前闭区间后开区间,那么[5,5)这个序列就相当于不存在的区间,如果比key值(key值不存在set的数据序列中)大的位置的值都没有那么会返回end()作为开头和结尾的位置的迭代器即[end(),end()),即如果给equal_range的key值不存在,那么equal_range会返回一个不存在的区间

multimap

multimap与map唯一不同的是:map中的key是唯一的,multimap中的key是可以重复的

multimap不提供operator[],因为有多个key值相同的键值对<key,value>,使用[]传入key需要返回key对应的value,那么对于这个value,value是有多个的,无法确定要返回哪一个value

multimap的insert

cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
	pair<string, string> arr[] = { make_pair("sort", "排序"),
	make_pair("insert", "插入") ,
	make_pair("string", "字符串"),
    make_pair("sort", "排序"),
    make_pair("string", "字符串"),
    make_pair("sort", "排序")};

	multimap<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));

	for(auto e: m2)
    {
    cout<<e.first<<" : "<<e.second;
    }

	    return 0;
    }

multimap的erase

  • multimap删除key值,如果key值相同的有多个,那么会将多个相同的key值对应存储键值对<key,value>的多个multimap节点全部删除
cpp 复制代码
#include <iostream>
#include <map>
using namespace std;

int main()
{
    pair<string, string> arr[] = { 
    make_pair("sort", "排序"),
    make_pair("insert", "插入") ,
    make_pair("string", "字符串"),
    make_pair("sort", "排序"),
    make_pair("string", "字符串"),
    make_pair("sort", "排序") };

    multimap<string, string> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
    size_t ret=m2.erase("sort");
    cout << ret;
    return 0;
}

总结

本文详细介绍了C++中map和multimap容器的使用。map是关联容器,存储键值对<key,value>,key用于排序和唯一标识,value存储关联内容。文章讲解了map的构造、插入(insert)、删除(erase)、查找(find)等操作,特别说明了operator[]的实现原理及其应用场景。通过示例代码演示了map的迭代器使用、范围for遍历等技巧。同时对比了multimap与map的区别,指出multimap允许key重复且不提供operator[]。最后介绍了lower_bound、upper_bound和equal_range等边界查找方法的使用场景和注意事项

相关推荐
yw00yw27 分钟前
常见的设计模式
开发语言·javascript·设计模式
我不是星海1 小时前
RabbitMQ基础入门实战
java·开发语言
重启的码农1 小时前
Windows虚拟显示器MttVDD源码分析 (6) 高级色彩与HDR管理
c++·windows·操作系统
jingfeng5142 小时前
C++多态
开发语言·c++
kyle~2 小时前
C/C++---浮点数与整形的转换,为什么使用sqrt函数时,要给参数加上一个极小的小数(如1e-6)
c语言·开发语言·c++
jokr_2 小时前
C++ STL 专家容器:关联式、哈希与适配器
java·c++·哈希算法
暖苏3 小时前
python-多线程(笔记)(持续更新)
大数据·开发语言·python
倔强的石头3 小时前
java程序员如何搭建C++windows开发环境搭建(二)
c++·后端
汤永红3 小时前
week5-[字符数组]查找
c++·算法·信睡奥赛