multimap 和priority_queue详解
上一期是关于STL和并查集结合的例题,也附了STL中部分容器的使用摘要,由于是从网上东拼西凑的,感觉有的关键点还是没解释清楚,现在从其中摘出两个容器,用例题对它们的用法进行进一步解释。
以下是例题的介绍
题目简述:有一个人每天往返于一段道路中,走着走着就觉得无聊了,于是自己给自己找乐子发明了一个扔石头的游戏:在一次单程行走过程中,假设整段路程中有 n 块石头,每块石头有两个重要信息,一个是所处的位置,一个是石头可以扔出的距离。还有一点值得注意,石头的块头越大扔的距离就越近。此人从第一块石头开始,遇到的第奇数个石头就搬起来往前扔,遇到的第偶数个石头不作处理。如果在某个位置上有两个石头,那么此人会先看到个头较大的那块石头(即扔的距离较近的那块石头)。现在要求算出此人走出多远就没石头可扔了。
输入数据:第一行给出测试数据的组数 t ( 1< = t <= 10 ),接下来给出 t 组测试数据,每组数据第一行 给出测试数据的组数 n ( 0 < n <=10 000) ,接下来的 n 行,每行给出一对测试数据 p 和 d ,( p( 0 <= p <= 10 000) 表示石头的位置,d ( 0 <= d <= 1 000) 表示石头可扔出的距离 )
输出数据:给出每组测试数据的结果,每行一个数据。
测试数据如下:
现在对整个题目进行分析,此人从第一块石头出发,随着时间推移接下来遇到的石头相对于起始位置越来越远,因此石头的位置是一个递增的信息,由此可以断定,存储石头信息的结构应该具备顺序结构,但石头的位置并不连续,如果用 vector 来存储,势必会造成空间的大量浪费。此人遇到偶数个石头丢弃,遇到奇数个石头要朝着他行走的方向往前扔,也就意味着扔到前面的石头他还会再遇到。这就需要在处理的时候,删掉偶数块的石头,并将奇数块石头重新添加到序列其他的位置,这就要求存储石头信息的结构需要有优良的插入、删除性质,序列型容器 vector 在这一点上更显的能力不足,这个时候,考虑其他的序列容器 队列queue 、链表 list 。链表虽然在随机插入删除方面很有优势,但若要求有序,链表操作是相当麻烦的。这个时候就剩 queue 还没被抛弃,题目要求遇到一个石头处理一个,即将此石头出列进行其余操作,这正是队列具备的特征,处理石头的时候需要将奇数的石头根据位置信息插入到相应位置。在有序的序列中,将元素插入到相应位置,priority_queue 就是应此要求诞生的。
选定了存储结构,现在来讨论一下具体的实现:使用计数器对此人遇到过的石头进行计数,分成奇数和偶数情况来处理,偶数情况将该石头信息出队不作处理,奇数情况将石头出队,并把位置信息更改为当前位置加可扔距离再次入队。每次对当前访问的石头位置进行存储,如此处理直至队列为空,输出最后一个处理石头的位置信息即可。
优先队列需要对队列的优先标准进行定义:当位置不相同时,位置靠前的优先性高,位置相同时,可扔距离较近的优先性较高。
在这个问题中需要考虑可扔距离为零的特殊情况。
下面对优先队列使用中需要注意的问题进行阐述
优先队列包含在 queue 头文件中
优先队列的声明有三种形式,
第一种:
priority_queue< element_type > que;
此时声明了一个存储 element_type 类型元素的优先队列 ,名称为 que,这句声明中包含了如下信息:
该优先队列元素的类型为 element_type,
该优先队列使用的容器为默认容器 vector ,
该优先队列使用的优先级关系为优先级由高到低,一般的数字大的数据优先级高,此时队首的元素为优先级高的元素。确定优先级的比较函数是less();请注意,优先队列中的内置函数只能对基本数据类型进行排序,如果要对特殊类型进行排序,需要自定义比较函数。
第二种:
priority_queue< element_type, name< element_type > > ;
此时声明了一个存储 element_type 类型的元素的优先队列 ,名称为 que,这句声明中包含了一下信息:
该优先队列元素的类型为 element_type ,
该优先队列使用的容器为 name 容器,( 此处的 name 为用户需要使用的容器的名称,并非真实存在的某容器,假设 此处需要使用 queue容器,那么声明为
priority_queue< element_type, queue< element_type > >
)
该队列使用的优先级关系为默认优先级(由高到低),默认比较函数为 less()。
第三种:
priority_queue< element_type , vector< element_type > , less<element_type > > ;
这种定义用于用户使用非 less()函数的其他优先级。
需要注意此时无论容器是不是默认容器 vector 都需要进行传参,这由优先队列相关函数内部实现决定。
例如:
现在元素类型为结构体类型,比较函数需要改变,则 处理如下:
struct node
{
element_type x;
element_type y;
};
bool compare( node a, node b )
{
return a . x > a . y ;(此处的优先级关系可任意更改)
}
priority_queue<node , vector<node> , compare<node> > ;
另例如下:
struct node
{
friend bool operator < ( node a , node b )
{
return a . y > b . y ; (同样,此处的优先级顺序也是可以根据需要任意更改的)
}
element_type x;
element_type y;
};
priority_queue< node>
或者下面这种:
priority_queue< node , vector<node> , less<node> > ; // 此时优先级实际上已经更改。
补充说明:
在优先队列中,内置优先性比较函数有两个:
less(),由队首开始优先性逐渐减小
greater(),由队首开始优先性逐渐增大
但不声明的情况下,默认使用 less(),需要使用 greater ()的时候,要使用上面介绍的第三种声明方式。
2. 优先队列其余函数大多与普通队列 queue 的使用方法类似。
以上是使用优先队列的方法,下面我将用 multimap 方法对此题进行讲解,在此题中,multimap 方法似乎有些麻烦,但是也不失为一种解题思路,重在介绍 multimap 的相关函数使用方法。
在序列式容器中有自动排序功能的是 priority_queue ,而关联式容器也有这个特点,而且红黑树实现效率非常高。
关联式容器有 set / multiset 和 map / multimap 这两对:
set 的结构相当于集合,除了满足集合的唯一性、确定性,还满足有序性(默认由小到大排列)。multiset 与 set 的相关用法大多相同,唯一的区别在于,multiset 允许集合中有相同元素的出现,由此会引起部分函数的差异。
map/multimap 的结构相当于 映射对的集合,同样,两者大同小异,区别在于multimap中允许有相同键值的元素出现。
由于在某一确定时刻每个石头的位置和可扔距离是确定的,且一一对应。因此我想到可以用 map 容器,又因为扔石头过程中,同一位置有可能出现多于一块石头,因此可以确定使用 multimap 容器。
思路和上面的思路大同小异,先将初始数据存入 map ,然后设置计数变量和存储当前位置的变量,map 不空就不断进行计数变量的奇偶判断和map 删除、插入元素的处理。同样应该考虑可扔距离为零的情况。
在这个过程中有一些需要注意的地方:
一、头文件
multimap 包含在 map 头文件中,使用时要在程序打开头文件编写时加入#include<map>语句。
二、声明
分为两种:
第一种:
multimap<key_type , value_type> mp;
包含以下信息
声明了一个 键值为 key_type 类型 、关联值为 value_type 类型的 multimap 型变量 mp;
multimap内 的键值排序按照默认排序顺序由小到大;
第二种:
multimap<key_type , value_type ,compare< key_type> >;
包含以下信息
声明了一个键值为 key_type 类型 ,关联值为 value_type 类型的 multimap 型变量 mp;
multimap 内键值的排序规则由比较函数 compare给出,请注意这里 compare()函数中的参数只有 键值的类型,而没有关联值的类型,原因是,multimap 内部的排序只根据键值大小进行排序,而对关联值的顺序没有相应的处理,由此可见, multimap 中同一键值元素的相对位置并不是由关联值的大小决定,而是由添加顺序决定。
三、普通map可以用 map[key]来对键值为 key 的元素的关联值进行操作,但 multimap 不包含此性质,因此向multimap中添加元素只能使用 insert()函数,同样也不能用mp[ key ]来访问某一元素。用法如下:
mp . insert( pair< key_type ,value_type>( key ,value ) )
mp . insert( make_pair ( key , value ) ) ;
其中,pair是一种模版类型,含有两个成员分别为 first 和 second。 两个成员的类型可以根据需要任意确定,可以是基本数据类型,也可以是用户自定义类型。pair 可以作为新的类型 进行函数传参和使用。声明如下:
pair< element_type , element_type > p = ( element_1 , element_2);
pair< element_type , element_type > p = make_pair ( element_1 , element_2 )
需要使用pair 的两个成员时如下:
p . first
p . second
巧用 pair,当某函数需要返回两个类型不同的值时,可以定义 pair 来返回。
四、如何确定键值相同而关联值不同的元素的个数,以及如何对它们进行访问:
确定个数使用函数
mp . count ( key ) ;
逐个进行访问
mp . equal_range ( key ) ;
注意,此函数的返回值是两个迭代器,其中,第一个迭代器返回的是 该键值第一个元素的迭代器,第二个迭代器返回的的是 该键值最后一个元素访问结束即将跳转的下一个元素的迭代器 。因此需要使用模版 pair ,如下:
typedef multimap< key_type , value_type >:: iterator iter;
pair< iter , iter > p ;
p = mp . equal_range ( key ) ;
while( p . first != p . second )
{
cout << p . first << endl ;
p . first ++ ;
}
在此应该注意思考一个问题:当multimap中只有一个元素的时候, p . second 此时的值应该与当前 mp . end ( ) 返回值相同,这时候,如果循环写成这样:
while( p . first != p . second )
{
cout << p . first << endl ;
mp . insert ( make_pair( elem_1 , elem_ 2 ) ) ;
p . first ++ ;
}
在插入新的元素后, mp . end ( ) 值变了,但 p . second 值还是原来的末尾,既不是现在的尾,也不是新加入的元素的位置,p .first 会一直自增寻找结束条件,但 p . second 目前已经失效了,会陷入死循环,并且这种错误一般不容易被发现 。因此在编写程序的过程中,应该尽量避免这种情况的发生。改进方法需要根据具体的要求来实现,在这里就不举例了。
五、特殊查找
mp . lower_bound( key ) 查找第一个键值不小于 key 的元素,返回其迭代器
mp . upper_bound( key ) 查找第一个键值比 key 大的元素,返回其迭代器
由上面注意事项中所提到的,同一键值的不同元素在multimap中的顺序并不是按照 value 值的大小决定,而是由输入顺序决定,因此,在本题中,需要将相同键值的元素摘出来存在vector中,对其进行排序再使用。本题中,multimap 比 priority_queue 复杂的地方就在此了。
本题的题解到此结束,主要还是利用这个例题来详细的解释一下 优先队列和 multimap 中 不同于基本容器的地方,其它未涉及到的知识大都与基本容器的使用方法相同。今天例题的代码详见http://paste.ubuntu.com/24358192/。
multimap 和priority_queue详解的更多相关文章
- 【STL】优先队列priority_queue详解+OpenJudge-4980拯救行动
一.关于优先队列 队列(queue)这种东西广大OIer应该都不陌生,或者说,队列都不会你还学个卵啊(╯‵□′)╯︵┻━┻咳咳,通俗讲,队列是一种只允许从前端(队头)删除元素.从后端(队尾)插入元素的 ...
- priority_queue详解
priority_queue是一个安排好的顺序存储的队列,队首是优先级最高的元素. Template<class T , class Container = vector<T> , ...
- C++ STL 优先队列 priority_queue 详解(转)
转自https://blog.csdn.net/c20182030/article/details/70757660,感谢大佬. 优先队列 引入 优先队列是一种特殊的队列,在学习堆排序的时候就有所了解 ...
- 优先队列priority_queue详解
转载链接
- 详解C++ STL priority_queue 容器
详解C++ STL priority_queue 容器 本篇随笔简单介绍一下\(C++STL\)中\(priority_queue\)容器的使用方法和常见的使用技巧. priority_queue容器 ...
- STL priority_queue 常见用法详解
<算法笔记>学习笔记 priority_queue 常见用法详解 //priority_queue又称优先队列,其底层时用堆来实现的. //在优先队列中,队首元素一定是当前队列中优先级最高 ...
- C++ STL详解
C++ STL详解 转载自:http://www.cnblogs.com/shiyangxt/archive/2008/09/11/1289493.html 一.STL简介 STL(Standard ...
- STL之heap与优先级队列Priority Queue详解
一.heap heap并不属于STL容器组件,它分为 max heap 和min heap,在缺省情况下,max-heap是优先队列(priority queue)的底层实现机制.而这个实现机制中的m ...
- 7-set用法详解
C++中set用法详解 转载 http://blog.csdn.net/yas12345678/article/details/52601454 C++ / set 更详细见:http://www.c ...
随机推荐
- Android 开发者,如何提升自己的职场竞争力?
前言 该文章是笔者参加 Android 巴士线下交流会成都站 的手写讲稿虚拟场景,所以大家将就看一下. 开始 大家好,我是刘世麟,首先感谢安卓巴士为我们创造了这次奇妙的相遇.现场的氛围也让我十分激动. ...
- Java8 lamda表达式快速上手
1.对比着经典foreach 简单的循环 o相当于foreach中的临时变量,要遍历的list放在句首 list.foreach(o->{你要进行的操作}); package com.compa ...
- 一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移
不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx.superviso ...
- opencv VideoCapture使用示例
在centos7下验证VideoCapture功能. 1 opencv处理视频时要使用ffmpeg,这里使用添加源的方式安装,分为3步 1.1 先安装EPEL Release,使用其他的repo源,所 ...
- Jmeter之接口测试
最近才入职新公司,好几天没有写博客了,经过一个朋友提醒,刚刚好觉得用Jmeter来做接口测试真的是再好不过了.下面就详细讲解下这两天我利用Jmeter做的接口测试. [安装Jmeter] 详细见博文: ...
- jquery的2.0.3版本源码系列(3):96行-283行,给JQ对象,添加一些方法和属性
jquery是面向对象的程序,面向对象就离不开方法和属性. 方法的简化 jQuery.fn=jQuery.prototype={ jquery: 版本 constructor: 修正指向问题 init ...
- class对象详解
我们知道,对于java语言,我们一般先写一个类对象,表示对某一类对象概述,其中包括属性,方法等.我们在对类对象编译时,会产生一个.class对象,jvm在加载类对象时,是加载.class 对象文件,我 ...
- Spring详解(四)------注解配置IOC、DI
Annotation(注解)是JDK1.5及以后版本引入的.它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查.注解是以‘@注解名’在代码中存在的. 前面讲解 IOC 和 DI 都是通过 ...
- java多线程知识点概述
这里只起一个概述的作用,极其简单的列一下知识点,需要在脑海中过一下,如果哪些方面不熟悉的话,建议利用网络资源去学习. 1.线程.进程概念 概念 线程状态及其转换 2.死锁.预防.解决 3.jdk线程实 ...
- 通过日期在js中求出判断间隔天数,周期等实现分享
在我们在项目的时候,可能出现这样的一种情况,有一个开始时间和一个结束时间,而这两个时间用$('#StartTime').val(); 取出来的时候又是datetime 类型,我们需要求这个时间中的间隔 ...