-1 目录
- 0 前言
- 1 map 的本质
- 1.1 二叉搜索树
- 1.1.1 搜索树
- 1.1.2 平衡二叉搜索树
- 1.2 红黑树简介
- 1.2.1 红黑树的特性
- 1.2.2 红黑树长什么样?
- 1.2.3 红黑树的旋转
- 1.2.3.1 旋转的定义
- 1.2.3.2 旋转图示
- 1.1 二叉搜索树
- 2 map 的基本用法
- 2.1 map 头文件
- 2.2 建立映射表
- 2.3 插入
- 2.3.1 最基础的插入
- 2.3.2 结合 insert 函数插入
- 2.4 访问
- 2.5 其他用法
- 2.6 迭代器(指针)
- 2.6.1 map 首尾指针
- 2.6.2 迭代器应用
- 3 map 的特点
- 4 map 实战
四道 map 实战题
- 5 拓展——multimap
- 5.1 不重载 [ ]
- 5.2 key可以重复
- 6 参考文献
0 前言
本文仅介绍基础的 map 容器,想要进阶请阅读yijan大佬的文章。
1 map 的本质
map 大多数是靠红黑树(一种比较优秀的平衡树)实现的。
至于平衡树是什么我们并不需要了解
1.1 二叉搜索树
1.1.1 搜索树
想看详细的图示?戳这里!
1.1.2 平衡二叉搜索树
平衡树是二叉搜索树和堆合并构成的新数据结构。
--来自度娘
平衡树的每个节点由key和weight组成。
1.2 红黑树简介
其实应该包括在1.1里的,但内容较多,便另起一节。注:仅为简介。
1.2.1 红黑树的特性 [ 1 ] ^{[1]} [1]:
-
每个节点或者是黑色,或者是红色。
-
根节点是黑色。
-
每个叶子节点(NIL)是黑色。 注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!
-
如果一个节点是红色的,则它的子节点必须是黑色的。
-
从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
-
每个叶子结点都带有两个空的黑色结点(即树尾端NULL指针或NULL节点,被称为黑哨兵)
1.2.2 红黑树长什么样?
如下图,就是一棵红黑树(下图引自wikipedia:http://t.cn/hgvH1l)
1.2.3 红黑树的旋转
1.2.3.1 旋转的定义 [ 2 ] ^{[2]} [2]
左旋转:逆时针旋转红黑树的两个节点,使得父节点被自己的右孩子取代,而自己成为自己的左孩子。
右旋转:顺时针旋转红黑树的两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子。
1.2.3.2 旋转图示
//左旋(对节点2左旋转)
1 1
/ /
2 4
/ \ -------> / \
3 4 2 6
/ \ / \
5 6 3 5
//右旋(对节点2右旋转)
1 1
/ /
4 2
/ \ -------> / \
2 6 3 4
/ \ / \
3 5 5 6
华丽丽的分割线 … \dots …
map 也可以看成一个下标可以为任意类型的数组,就是个映射表。
2 map 的基本用法
2.1 map 头文件
#include <map>
2.2 建立映射表
map <A,B> ds
建立一个叫ds
的,下标类型为A
,元素类型为B
的映射表。
2.3 插入
2.3.1 最基础的插入
ds[t1]=t2
把下标为t1
对应的值改为t2
。
2.3.2 结合 insert 函数插入
ds.insert(pair<A,B>(t1,t2))
在map <A,B> ds
的基础上等同于ds[t1]=t2
。ds.insert(map<A,B>::value_type(t1,t2))
在map <A,B> ds
的基础上等同于ds[t1]=t2
。ds.insert(make_pair(t1,t2))
等同于ds[t1]=t2
。
2.4 访问
ds[A]
:访问下标为A
的元素。
2.5 其他用法
ds.clear()
:清空映射表ds.size()
:返回映射表中的元素个数ds.empty()
:判断映射表是否为空
2.6 迭代器(指针)
一般定义格式:
map <A,B> ::iterator it=地址
:建立一个指针 it
指向某一地址
2.6.1 map 首尾指针
- 首:
map.begin()
- 尾:
map.end()
2.6.2 迭代器应用
练习:遍历映射表
注意:指针不能使用<=,<.>,>=
等符号,要用!=
,指针只能++
或--
!
c o d e : code: code:
#include <iostream>
#include <map>
using namespace std;
map <int,int> ds;
int main(){
map <int,int>::iterator it;
for(it=ds.begin();it!=ds.end();it++){
cout<<(*it).first<<' '<<(*it).second<<endl;
}
return 0;
}
3 map 的特点
-
红黑树可以在 Θ ( log n ) \Theta(\log n) Θ(logn)的时间内实现插入、查找和删除操作,这也是它优秀的地方之一。
-
只要在程序中提到
ds[A]
,map 就认为A
是存在的。
4 map 实战
4.1 小D与笔试
题意
给你 n n n 对字符串,表示后者是前者的答案,再给你 q q q 个题目,要求从题目后面四个选项中找出答案。
s o l u t i o n : solution: solution:
分析题面,不难得出:题面和答案是一一对应的关系,可以通过建立一个下标元素类型都为 string
的映射表来做到。
c o d e : code: code:
#include <bits/stdc++.h>
using namespace std;
map <string,string> ds;
int main(){
int n,q;
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++){
string s1,s2;
cin>>s1>>s2;
ds[s1]=s2;
}
for(int i=0;i<q;i++){
string s,a,b,c,d;
cin>>s>>a>>b>>c>>d;
if(a==ds[s]) puts("A");
if(b==ds[s]) puts("B");
if(c==ds[s]) puts("C");
if(d==ds[s]) puts("D");//puts自带换行
//挨个比较
}
return 0;
}
4.2 A-B数对
题意
给定一个数列 { x } \{x\} {x} 和 C C C,从 { x } \{x\} {x} 中能找到多少对 A ( x i ) , B ( x j ) A(x_i),B(x_j) A(xi),B(xj),使 A − B = C A-B=C A−B=C?注意:不同位置的 A , B A,B A,B 算不同的一对。
s o l u t i o n : solution: solution:
思路:枚举每个 A A A,算出 A − C A-C A−C 在数列中的出现次数,次数使用 map 统计。
c o d e : code: code:
#include <map>
#include <iostream>
using namespace std;
typedef long long LL;//把long long替换成LL节省时间
map <LL,LL> ds;
LL n,c,a[200010],ans;
int main(){
cin>>n>>c;
for(int i=0;i<n;i++){
cin>>a[i];
ds[a[i]]++;//统计次数
}
for(int i=0;i<n;i++) ans+=ds[a[i]-c];
cout<<ans;
return 0;
}
4.3 保龄球
题意
给定一个数列 { x } \{x\} {x} 和数列中的一项 x i x_i xi,求出 i i i 的值。注意: i i i 从 1 1 1 开始。
s o l u t i o n & c o d e : solution\And code: solution&code:
这是作业由大家自己完成
4.4 这是4.1的弱化版,有兴趣的可以看看
5 拓展——multimap
multimap
的用法与 map
大同小异,只是把 map
换成 multimap
而已。
正题:map
与 multimap
的区别
5.1 不重载 [ ]
multimap
是不重载 []
的,访问、插入得靠其他办法。
e . g . \mathrm{e.g.} e.g.
//代码片
multimap <char,int> ds;
multimap <char,int> ::iterator it;
ds.insert(make_pair('a',8));
ds.insert(make_pair('e',7));
ds.insert(make_pair('9',9));
ds.insert(make_pair(';',0));
for(it=ds.begin();it!=ds.end();it++){
printf("%d %d\n",it->first,it->second);//it->first等同于(*it).first
}
输出:
a 8
e 7
9 9
; 0
5.2 key可以重复
例如我们写了
multimap <int,int> ds;
ds.insert(pair <int,int> (6,40));
ds.insert(pair <int,int> (6,50));
multimap <int,int> ::iterator it;
for(it=ds.begin();it!=ds.end();it++){
printf("%d %d\n",it->first,it->second);
}
此时 multimap 中存储信息如下表:
k e y ∥ key\| key∥ | v a u l e vaule vaule |
---|---|
6 6 6 | 40 40 40 |
6 6 6 | 50 50 50 |
输出:
6 40
6 50
可见,multimap
中的 key 是可以重复的。
6 参考文献
[ 1 ] : ^{[1]}: [1]:来源
;
}
此时 multimap 中存储信息如下表:
| $key\|$ | $vaule$ |
| :-----------: | :-----------: |
| $6$ | $40$ |
| $6$ | $50$ |
输出:
```plain
6 40
6 50
可见,multimap
中的 key 是可以重复的。
6 参考文献
[ 1 ] : ^{[1]}: [1]:来源
[ 2 ] : ^{[2]}: [2]: 来源