【STL】map&multimap

头文件:

#include<map>
  • map的特性是,所有元素都会根据元素的键值自动排序。
  • map所有的元素都是pair,同时拥有实值和键值,pair的第一元素被视为键值,第二元素被视为实值,map不允许两个元素有相同的键值。
  • 可以根据key值快速找到value值
  • 我们不可以通过map的迭代器改变map的键值, 因为map的键值关系到map元素的排列规则,任意改变map键值将会严重破坏map组织。如果想要修改元素的实值,那么是可以的。
  • map和list拥有相同的某些性质,当对它的容器元素进行新增操作或者删除操作时,操作之前的所有迭代器,在操作完成之后依然有效,当然被删除的那个元素的迭代器必然是个例外。
  • map 容器配备的是双向迭代器(bidirectional iterator)。这意味着,map 容器迭代器只能进行 ++p、p++、–p、p–、*p 操作,并且迭代器之间只能使用 == 或者 != 运算符进行比较。
  • map支持下标访问符,即在[]中放入key,就可以找到与key对应的value的引用。其实在map中,中括号已经被重载为有不存在即插入的功能(对于自定义类型会调用值的默认构造函数,对于int类型则为0)。于是如果查找的key不存在,则map中会增加很多垃圾数据
  • multimap和map的操作类似,唯一区别multimap键值可重复。
  • map和multimap都是以红黑树为底层实现机制。

map原型

std::map
template <class Key,
				 class T,
				 class Compare = less<Key>,
				 class Alloc = allocator<pair<const Key, T> >
				 > class map;
key:键值对中的key的类型
T:键值中value的类型
Compare:比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)
该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标注库提供的空间配置器

map容器的构造与赋值

函数描述
map<T,T> mmap默认构造函数
map(const map &mp)拷贝构造函数
map& operator=(const map &mp)重载等号操作符

实例:

#include<iostream>
#include<map>
using namespace std;
//map容器的构造与赋值
void printMap(map<int,int> &m) {
	for (auto it = m.begin(); it != m.end(); it++) {
		cout << "key =" << it->first << " value =" << it->second << endl;
	}
	cout << endl;
}
int main() {
	//创建map容器
	map<int, int> m;
	//插入数据里面需要传入的是对组
	m.insert(pair<int, int>(1, 10));//pair<int, int>(1, 10)为匿名二元组
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(4, 40));
	m.insert(pair<int, int>(2, 20));
	//插入元素后会根据key自动进行升序排列
	printMap(m);
	
	//拷贝构造
	map<int, int> m2(m);
	printMap(m2);

	//赋值
	map<int, int> m3;
	m3 = m2;
	printMap(m3);
	return 0;
}

map容器中所有的元素都是成对出现的,插入数据的时候要使用二元组,即对组pair。

map容器的大小与交换

函数描述
size()返回容器中元素的数目
empty()判断容器中是否为空
swap(st)交换两个集合容器

实例:

#include<iostream>
#include<map>
using namespace std;
//map容器的大小与交换
void printMap(map<int,int> &m) {
	for (auto it = m.begin(); it != m.end(); it++) {
		cout << "key值为:" << it->first << " value值为:" << it->second << endl;
	}
	cout << endl;
}
int main() {
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(2, 20));
	if (m.empty()) {
		cout << "m容器为空" << endl;
	}
	else {
		cout << "m容器不为空" << endl;
		cout << "m的大小为:" << m.size() << endl;
	}
	//交换
	map<int, int> m2;
	m2.insert(pair<int,int>(4, 40));
	m2.insert(pair<int,int>(6, 60));
	m2.insert(pair<int,int>(5, 50));
	cout << "map容器交换前" << endl;
	printMap(m);
	printMap(m2);
	cout << "map容器交换后" << endl;
	m.swap(m2);
	printMap(m);
	printMap(m2);
	return 0;
}

map容器的插入与删除

函数描述
insert(elem)在容器中插入元素
pair<iterator,bool> insert(const value_type &x)在map中插入键值对x,iterator代表插入元素的位置,bool代表是否插入成功
iterator insert(iterator position, const value_type &x在position位置插入值为x的键值对,返回该键值对在map中的位置,注意:元素不一定必须插入在position位置,该位置指示一个参考
template void insert(InputIterator first, InputIterator last)在map中插入first到last区间中的元素
clear()清除所有元素
erase(pos)删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end)删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(key)删除容器中值为key的元素

map插入数据的三种方法:

  1. 通过 insert 函数插入 pair
  2. 通过insert 函数插入 value_type(其实value_type就是pair的typedef)
  3. 用数组方式插入数据

区别:以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的。第一种和第二种在效果上是完成一样的,用 insert 函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当 map 中已经存在某个关键字时,insert 操作是不能成功插入数据,但是用数组方式则可以,它可以覆盖以前该关键字对应的值。

实例:

#include<iostream>
#include <map>
using namespace std;
//map容器的插入与删除操作
void printMap(map<int,int> &m) {
	for (auto it = m.begin(); it != m.end(); it++) {
		cout << "key值为:" << it->first << " value值为:" << it->second << endl;
	}
	cout << endl;
}
int main() {
	map<int, int> m;
	//插入 第一种
	m.insert(pair<int, int>(1, 10));
	//插入 第二种
	m.insert(make_pair(2, 20));
	//插入 第三种 不建议使用
	m.insert(map<int, int>::value_type(3, 30));
	//插入 第四种 最简单
	//[]不建议插入 通过[]可以利用key访问到value
	//使用[]插入元素的时候,如果key不存在将会自动创建键值对
	m[4] = 40;
	printMap(m);
	//删除
	m.erase(m.begin());
	printMap(m);
	//删除 直接传入key
	m.erase(3);
	printMap(m);
	//全部删除
	m.clear();//相当于m.erase(m.begin(),m.end())
	printMap(m);
	return 0;
}

map容器的查找与统计

函数描述
find(key)查找key是否存在,返回该键的元素的迭代器;若不存在返回map.end()
mapped_type& at (const key_type& k)返回键值k对应value的引用,如果k不在map中,那么out_of_range exception异常抛出(C++11新特性)
mapped_type& operator[] (const key_type& k)返回key对应的value,可以通过返回查看当前元素是否在map中
count(key)统计key的元素的个数

注意:multimap不支持[]操作符

实例1:

#include<iostream>
#include<map>
using namespace std;
int main() {
	//查找
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(2, 20));
	//查找键为3的键值对
	map<int,int>::iterator pos=m.find(3);
	if (pos != m.end()) {
		cout << "查到了元素 key=" << pos->first << " value=" << pos->second << endl;
	}
	else {
		cout << "未找到元素" << endl;
	}
	//统计
	//由于map容器中key不能重复出现 因此count统计的结果只有0或1
	int num=m.count(3);//返回结果为整型
	cout << "num=" << num << endl;
	return 0;
}

注意:find返回的是迭代器,而count返回的是整型;而map 中的key是唯一的,因此该函数的返回值要么为0,要么为1,因此可以用该函数来检测一个key是否在map中

实例2:

#include<iostream>
#include<map>
using namespace std;

int main()
{
    map<string,string> m;
    // 向map中插入元素的方式
    // 将键值对<"peach", "桃子">插入到Map,用pair直接来构造键值对
    m.insert(pair<string, string>("peach", "桃子"));
    //将键值对<"watermel", "西瓜">插入到map中,用make_pair函数来构造键值对
    m.insert(make_pair("watermel", "西瓜"));
    // 先拿到该容器中的键值对类型,然后插入
    m.insert(map<string,string>::value_type("peach", "桃子"));
    // key不存在时抛异常
    //m.at("banan") = "香蕉";
    // 这个的原理是,先用key和T()构造一个键值对,然后调用insert函数将该键值对插入到map中
    // 如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器,
    // 如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器,
    // operator[]函数最后将insert的返回值中的value返回
    m["banan"] = "香蕉";
   // 输出Map的大小
    cout << m.size() << endl;
    // 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
    for(auto &e : m)
    {
        cout << e.first << "--->" << e.second << endl;
    }
    cout << endl;
    // map中的键值对key一定是唯一的,如果key存在将插入失败
    auto ret = m.insert(make_pair("peach", "桃色"));
    if(ret.second)
    {
        cout << "<peach, 桃色>不在map中,已经插入" << endl;
    }
    else
    {
        cout << "键值为peach已经在map:" << ret.first->first << ":" << ret.first->second << endl;
    }
    // 删除key为apple的元素
    m.erase("apple");
    for(auto&e : m)
    {
        cout << e.first << "--->" << e.second << endl;
    }
    if(1 == m.count("apple"))
    {
        cout << "apple还在" << endl;
    }
    else
    {
        cout << "apple被吃了" << endl;
    }
    return 0;
} 

map比较

比较两个关键字在map中位置的先后

key_compare key_comp() const;

实例:

#include<iostream>
#include<map>
using namespace std;
int main(){
	map<char,int> mymap;
    map<char,int>::key_compare mycomp = mymap.key_comp();
    mymap['a']=100;
    mymap['b']=200;
    bool b=mycomp('a', 'b');  // a排在b前面,因此返回结果为true
    cout<<b<<endl;
	return 0;
}

map自定义排序规则

sort排序不能针对关联容器(set,map),因为sort会破环关联容器的底层数据结构,详细解释如下。

map中的元素是pair类型对象,每个pair类型由关键字—值(key-value)组成:关键字起到索引的作用,值则表示与索引相关联的数据。字典是一个很好的map例子,可以将单词作为关键字,将单词释义作为值。

map使用的底层数据结构为一颗红黑树(红黑树是一颗高度平衡二叉排序树),因为map的各种操作接口,RB-Tree也都提供了,所以几乎所有的map操作行为,都只是转调用了RB-Tree的操作行为而已。TB-Tree中的key是按弱序排序的,因此map中的key也是按弱序排列的,所以任意更改map的key会严重破坏map组织的,也就是key不能修改(key也就不能使用sort排序。)

使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。默认情况下,map 容器选用std::less< T >排序规则(其中 T 表示键的数据类型),其会根据键的大小对所有键值对做升序排序。当然,根据实际情况的需要,我们可以手动指定 map 容器的排序规则,既可以选用 STL 标准库中提供的其它排序规则(比如std::greater< T >),也可以自定义排序规则。

template < class Key,                                     // 指定键(key)的类型
           class T,                                       // 指定值(value)的类型
           class Compare = less<Key>,                     // 指定排序规则
           class Alloc = allocator<pair<const Key,T> >    // 指定分配器对象的类型
           > class map;

实例:

#include<iostream>
#include<map>
using namespace std;

class myCompare
{
public:
    bool operator()(int v1, int v2)
    {
        return v1 > v2;
    }
};

int main()
{
    map<int, int, myCompare> m;
    m.insert(pair<int, int>(1,10));
    m.insert(make_pair(2,20));
    m.insert(map<int,int>::value_type(3,30));
    m[4] = 40;
    for(map<int, int, myCompare>::iterator it = m.begin(); it != m.end(); it++)
    {
        cout << "key :" << it->first << "value :" << it->second << endl;
    }
    return 0;
}

map迭代器

共有八个获取迭代器的函数:begin, end, rbegin, rend 以及对应的 cbegin, cend, crbegin, crend。

二者的区别在于:后者一定返回 const_iterator,而前者则根据map的类型返回iterator 或者 const_iterator。const情况下,不允许对值进行修改。如下面代码所示:

map<int,int>::iterator it;

map<int,int> mmap;
const map<int,int> const_mmap;

it = mmap.begin(); //iterator
mmap.cbegin(); //const_iterator

const_mmap.begin(); //const_iterator
const_mmap.cbegin(); //const_iterator

返回的迭代器可以进行加减操作,此外,如果map为空,则 begin = end

multimap

multimap 容器具有和 map 相同的特性,即 multimap 容器也用于存储 pair<const K, T> 类型的键值对(其中 K 表示键的类型,T 表示值的类型),其中各个键值对的键的值不能做修改;并且,该容器也会自行根据键的大小对存储的所有键值对做排序操作。和 map 容器的区别在于,multimap 容器中可以同时存储多(≥2)个键相同的键值对。

和 map 容器相比,multimap 未提供 at() 成员方法,也没有重载 [] 运算符。这意味着,map 容器中通过指定键获取指定指定键值对的方式,将不再适用于 multimap 容器。其实这很好理解,因为 multimap 容器中指定的键可能对应多个键值对,而不再是 1 个。

map可以
map[xx]++;
map[xx]=xxx..;
 
multimap不可以这样

实例1:

#include<iostream>
#include<map>
using namespace std;
void Show(map<int,int> &m){
	for(map<int, int>::iterator it = m.begin(); it != m.end(); it++)
    {
        cout << "key :" << it->first << " value :" << it->second << endl;
    }
    cout<<endl;
}
void Show_M(multimap<int,int> &m){
	for(multimap<int, int>::iterator it = m.begin(); it != m.end(); it++)
    {
        cout << "key :" << it->first << " value :" << it->second << endl;
    }
    cout<<endl;
}
int main()
{
	map<int, int>mp = { {2,10},{1,50},{3,8,} ,{3,15} };//按前后顺序 只能插入到3,8
	mp.insert(pair<int, int>(3, 10)); //插不进去
	Show(mp);
 
	multimap<int, int>mp1 = { {2,10},{1,50},{3,8},{3,11} };//用初始化列表也可以写键值相同的
	mp1.insert(pair<int, int>(3, 10));//可以插进去
	Show_M(mp1);
	return 0;
}

实例2:

#include<iostream>
#include<map>
using namespace std;

void test1()
{
    multimap<string, string> m;
    m.insert(make_pair("李逵", "黑旋风"));
    m.insert(make_pair("林冲", "豹子头"));
    m.insert(make_pair("鲁达", "花和尚"));
    m.insert(make_pair("李逵", "铁牛"));
    
    cout << m.size() << endl;
    
    for(auto& e : m)
    {
        cout << "<" << e.first << "," << e.second << ">" << endl;
    }
    cout << m.count("李逵") << endl;
    cout<<endl;
}

void test2()
{
    multimap<int, int> m;
    for(int i = 0; i < 10; ++i)
    {
        m.insert(pair<int,int>(i, i));
    }
    for(auto& e : m)
    {
        cout << e.first << "--->" << e.second << endl;
    }
    cout << endl;

    auto it = m.lower_bound(5);
    cout << it->first << "--->" << it->second << endl;
    cout << endl;
    it = m.upper_bound(5);
    cout << it->first << "--->" << it->second << endl;
}

int main(){
	test1();
	test2();
	return 0;
}

参考博文:

https://blog.csdn.net/soinlove36/article/details/119796794

https://blog.csdn.net/qq_51447436/article/details/126711431

https://blog.csdn.net/weixin_43718250/article/details/100751494

https://blog.csdn.net/lemonxiaoxiao/article/details/121204095

https://blog.csdn.net/weixin_51609435/article/details/126392880

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值