版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、MAP原理及其在MFC中的实现2009-12-23 14:02一、 Map的基本知识映射(Map),又称为字典(Dictionary),是由关键字(Key)及其对应的元素值(Value)所组成的元素单元(Element)的表单式集合。通常,对于Map而言,使用给定的Key,可以迅速地从单元集合中检索到相应的元素。因此,在需要对大量数据进行查找操作而查找的性能又占据重要地位的场合,Map无疑是一种较理想的容器。譬如,在MFC中,使用Map来实现HandleMaps(句柄映射),以及其他的一些内部数据结构。同时,MFC也提供了公共Map类。使用公共Map类,MFC程序员可以轻易地高效地根据自身的需
2、求实现程序中自定义的映射。通常,当一个Map对象被删除时,或者,当其中的元素被移除时,关键字和元素值也将被完全删除。从数据结构的角度分析,有关Map的典型操作有:1、向Map中插入具有给定关键字的元素单元。2、在Map中查找具有给定关键字的元素单元。3、在Map中删除具有给定关键字的元素单元。4、枚举(遍历)Map中的所有元素单元。MFC中的各种Map实现,都提供了实现上述操作的成员函数。为了方便讨论,我们以CMap为代表,进行讲解。一旦你已经向Map中插入了一个关键字-元素值组合对(Key-Value pair)单元,就可以利用关键字访问Map,从而有效地检索、添加或者删除元素单元,也可以遍
3、历Map中的所有单元。对MFC中的CMap等,除了关键字访问方法之外,还有另一种不同的类型-POSITION,也可以作为访问元素单元的辅助方式,可以使用一个POSITION来"记住"一个元素单元或者对Map进行枚举操作。你可能认为这种使用POSITION实现的遍历等同于使用关键字来进行的Map遍历,事实上并非如此,确切的说,两种检索的等价性是不确定的。MFC中的提供了基于模板的CMap类。利用CMap模板类,可以处理特定的数据类型,例如用户自定义的类或结构体等。同时,MFC也提供了基于指定数据类型的非模板类,其中包括: 类名关键字类型元素值类型CMapWordToPtrWO
4、RDSVoid pointersCMapPtrToWordVoidpointers WORDSCMapPtrToPtrVoid pointersVoid pointersCMapWordToObWORDSObjectsCMapStringToObStringsObjectsCMapStringToPtrStringsVoid pointersCMapStringToStringStringsString二、 Map的工作原理使用Map的最大优势是它的快速查找的优秀性能,而取得最优性能的关键在于尽量使得在检索周期内所需进行的元素检查(比对)次数达到最少。顺序查找的性能是最差的,因为如果使用顺序查
5、找算法在包含n个元素单元的Map中查找某个元素,可能(最坏情况下)需要进行n次独立的比较运算。二元查找(折中查找)的性能表现要稍好一些,可是,一个不容忽视的问题是-二元查找要求待查序列为有序排列,这无疑会降低Map自身的操作灵活性。在我们的理解中,所谓的最佳算法应当是不论元素单元数目的多少,也不论元素是以什么顺序进行排列,查找过程都无需任何额外的比对操作,而能够仅仅通过简单的计算方法,就可以直接指向最终的相应元素的快速、高效算法。这听起来有些玄乎,但事实上,这种算法的确是有可能实现的(而且,我相信,Map可以做得到)。在MFC的CMap及其相关的Map类中,只要对Map进行正确设置,Looku
6、p函数通常能够一次到位的查找到任意元素,而很少需要进行两次或者三次以上的查找比对。那么,这种高效的查找是如何实现的呢?不失一般性,以MFC中的CMap模板类为例。在Map被创建之后(通常是恰恰在第一个元素被插入之前的瞬间),系统会为一个指向CAssoc结构体的指针数组的哈希表分配内存。MFC使用CAssoc结构体描述元素值和关键字的组合对。CAssoc结构体描述如下:struct CAssocCAssoc* pNext;UINT nHashValue;CString key;CString value; 无论何时,只要有一个元素值-关键字单元被加入到Map中,就会随之创建一个新的CAssoc结
7、构体,并根据单元中的关键字的实际值来计算出相应的哈希值。同时,拷贝一个指向CAssoc结构体的指针并将其插入到哈希表中索引值为i的位置中。其中,i的计算公式如下:i=nHashValue%nHushTableSize式中,nHashValue是由关键字Key的实际值计算出来的哈希值;nHashTableSize是哈希表中元素的数目(默认情况下,哈希表的大小为17)。如果在哈希表中的索引值为i的位置已经容纳了一个CAssoc指针,那么MFC将建立一个单独的CAssoc结构体的链表(List),链表中的第一个CAssoc结构体的位置被存储到哈希表中,而将第二个CAssoc结构体的位置存储到前一个C
8、Assoc结构体的pNext域,以此类推。下图展示了哈希表的一种可能实现情况,在该哈希表中,共有10个元素,其中5个元素位置分别唯一的存储,另外5个分别存储在长度为2和3的两个链表中。调用一个Map的Lookup()函数时,MFC根据输入的关键字的实际值计算相应的哈希值,然后使用前面提到的公式将哈希值转换为索引值,并从哈希表中的相应位置检索CAssoc指针。理想情况下,该位置只包含一个CAssoc指针,而非CAssoc指针链表。如果事实情况真如同我们所期望的那样,单一位置对应单一CAssoc指针,那么,元素单元将能够被一次查找到位,并直接读出;如果从哈希表中检索到的是CAssoc链表的指针头位
9、置,则MFC顺序比对链表元素CAssoc结构所包含的关键字,直至查找到正确结果。但是,正如我们先前所讨论的那样,只要正确设置Map,链表中的元素一般就不会超过三个,这就意味着,查找通常可以在三次元素比对操作之内完成。三、 优化查找效率在MFC的Map中,查找性能主要依赖于两个因素:1、哈希表的大小2、尽可能产生唯一哈希值的优异算法哈希表的大小对于Map的查找性能而言,是非常重要的。举个简单的例子,如果有一个Map要容纳1000个元素单元,但是哈希表却只能提供17个存放CAssoc指针的空间,那么,即使是最佳情况,哈希表中的每个CAssoc链表中也将包含58或59个CAssoc结构体,自然,在这
10、种情况下,查找性能将受到严重阻碍。哈希算法亦是影响查找效率的重要因素之一。如果所使用的哈希算法只能产生少量的不同哈希值(从而也只能产生少量的不同的哈希表索引值),查找性能也同样将被降低。优化Map查找性能的最有效途径是尽可能的增大哈希表以降低因索引值相同而产生冲突的可能。微软推荐将哈希表的大小设置为Map中所存储元素数目的110% 120%,以使得Map的应用性能在内存消耗和查找效率之间取得相对平衡。在MFC中,指定哈希表大小,可调用InitHashTable()函数: map.InitHashTable(1200);式中,假设Map中要存储1000个元素,按照微软公司的推荐,将哈希表的大小扩
11、展到实际存储元素数目的120%,即设置Map大小为1200。从统计学上考虑,实用奇数作为哈希表的大小也将有助于减少冲突的发生。因此,初始化一个存储1000个元素的哈希表的InitHashTable()函数可以如下形式使用:map.InitHashTable(1201);同时,在InitHashTable()函数的调用时机上,应该注意的是,该函数应当在map包含有任何元素之前使。如果map中已经包含了一个或者更多的元素,那么,重新改变map的大小,将会引发断言(Assertion)错误。尽管MFC中所使用的哈希算法能够适应于大多数场合,但如果您真的有所需要,或者,只要你愿意,用户也可以使用自己的
12、算法来取代原有的算法。对于一个输入的关键字的值,要计算出它的哈希值,MFC通常调用一个全局模板函数HashKey(),对于大多数数据类型而言,HashKey()函数是以下面的方式实现的:AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key)/一般情况的默认算法。return (UINT)(void*)(DWORD)key) >> 4;但对于字符串而言,其具体的实现方式如下:UINT AFXAPI HashKey(LPCWSTR key) / Unicode编码字符串UINT nHash = 0;while (*key)nHash = (nHash&l
13、t;<5) + nHash + *key+;return nHash;UINT AFXAPI HashKey(LPCSTR key) / ANSI编码字符串UINT nHash = 0;while (*key)nHash = (nHash<<5) + nHash + *key+;return nHash;要实现对应于特定数据类型的用户自定义哈希算法,您可以使用上述的字符串版本的HashKey()函数作为参考,写一个类似的特定类型的HashKey()函数。四、 使用MFC中的CMap类有关MFC中的CMap类的概况,上面的文字段落中已经陆续提及,在此不再赘言。下面,列出CMap
14、类的基本成员函数,并通过一个简短的程序片段来粗略地演示CMap类的使用方法。构造函数:CMap构造一个关键字和元素值映射的集合类。操作:Lookup通过给定的关键字查找相应的元素值。SetAt向Map中插入一个元素单元;若存在匹配键字,则替代之。operator 向Map中插入一个元素 -SetAt的子操作RemoveKey移除由关键字标示的元素单元RemoveAll移除Map中的所有元素单元GetStartPosition返回第一个元素单元的位置GetNextAssoc读取下一个元素单元GetHashTableSize返回哈希表的大小(元素单元的数目)InitHashTable初始化哈希表,
15、并指定它的大小状态:GetCount返回Map中元素的数目IsEmpty检查Map是否为空(无元素单元)应用实例如下:CMap myMap;/初始化哈希表,并指定其大小(取奇数)。MyMap.InitHashTable(257);/向myMap中添加元素单元。for (int i=0;i < 200;i+)myMap.SetAt( i, CPoint(i, i) );/ 删除实际值为偶数的关键字所对应的的元素单元。POSITION pos = myMap.GetStartPosition();int nKey;CPoint pt;while (pos != NULL)myMap.GetN
16、extAssoc( pos, nKey, pt );if (nKey%2) = 0)myMap.RemoveKey( nKey );#ifdef _DEBUGafxDump.SetDepth( 1 );afxDump << "myMap: " << &myMap << "/n"#endif在上面的应用程序片段中,我们可以了解有关CMap类的在通常情况下的使用方法。1、首先我们使用CMap模板类来定义一个实例-myMap对象。2、紧接着要做的是对myMap对象的哈希表的大小进行初始化设置。此时,应该先对myMap可能的容量需求进行估计,然后选择适当大小的奇数-或者,有可能的话,使用素数的效果会更好一些-来作为哈希表的初始值。3、然后,向myMa
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 15476-2026肾及甲状腺多功能测量仪
- 译林版(三起)五年级上册 Grammar Time 语法课时精练(全册8单元)
- 换季健康防护指南
- 职业培训发展规划范本
- 人工智能激光雷达技术详解
- 昆山安全经验分享讲解
- 2026年餐饮加盟合作经营合同三篇
- 2026城市聚落面试题及答案
- 2026道路执法面试题库及答案
- 2026东盟财经面试题及答案
- 股份制加盟合同协议书
- 玻璃体疾患的护理
- 2025年党史知识竞赛真题和标准答案(共60题)
- 登高架设高处作业证理论考试题(附答案)
- 2025年全国中小学教师职称评审考试试卷及答案
- 实验动物神经功能缺损评分标准
- 2025年西学中培训结业考试卷(有答案)
- 2025内蒙古鄂尔多斯伊金霍洛旗布连矿区消防救援大队招聘4人考试模拟试题及答案解析
- 2025至2030中国植物饮料行业市场发展分析及前景预测与投资报告
- 物业工程部月度工作总结汇报
- 贵州黔东南州直属事业单位全州遴选考试真题2024
评论
0/150
提交评论