浅谈C++map容器

本文详细介绍了C++中的map容器,基于红黑树实现,提供插入、查找和删除操作。讲解了map的基本用法,如插入、访问元素、迭代器应用,并通过多个实战例子展示了map在解决问题中的应用。同时,还介绍了map的特点和拓展内容——multimap,其中key可以重复。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

-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 旋转图示
  • 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 平衡二叉搜索树

平衡树二叉搜索树合并构成的新数据结构。

                                                                              --来自度娘

平衡树的每个节点由keyweight组成。

平衡树参考文章:日报#2 日报#119
这里不再重复赘述。

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 AB=C注意:不同位置的 A , B A,B A,B 算不同的一对。

s o l u t i o n : solution: solution:

思路:枚举每个 A A A,算出 A − C A-C AC 在数列中的出现次数,次数使用 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 而已。

正题:mapmultimap 的区别

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]: 来源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值