第9章 词典
借助数据结构来表示和组织的数字信息,可将所有数据视作一个整体统筹处理,进而提高信息访问的规范性及其处理的效率。例如,借助关键码直接查找和访问数据元素的形式,已为越来越多的数据结构所采用,这也成为现代数据结构的一个重要特征。词典(dictionary)结构,即是其中最典型的例子。
逻辑上的词典,是由一组数据构成的集合,其中各元素都是由关键码和数据合成的词条(entry)。映射(map)结构与词典结构一样,也是词条的集合,但映射要求不同词条关键码互异,词典允许多个词条拥有相同关键码。除了静态查找,两者都支持动态更新,统称符号表(symbol table)。实际上,“是否允许雷同关键码”应从语义层面,而非ADT接口的层面予以界定,故本章不过分强调二者的差异,统称词典。
尽管此处词典和映射中的数据元素,仍表示和实现为词条形式,但这一做法并非必须。实际上,以散列表为代表的符号表结构,将转而依据数据项的数值,直接做逻辑查找和物理定位。即对于此类结构,在作为基本数据单位的词条内部,关键码(key)与数值(value)的地位等同,二者不必加以区分,此类结构所支持的这种新的数据访问方式称为循值访问(call - by - value) 。(之前学的是循秩访问)
既已抛开大小次序的概念,采用循值访问方式的计算过程,自然不再属于CBA式算法的范畴,此前关于CBA式算法下界的结论亦不再适用,一条通往高效算法的崭新大道,由此在我们面前豁然展开。
当然,为支持循值访问的方式,在符号表内部,仍然必须强制地在数据对象的数值与其物理地址之间建立某种关联。而所谓散列,正是在兼顾空间与时间效率的前提下,讨论和研究赖以设计并实现这种关联的一般性原则、技巧与方法。
9.1 词典ADT
9.1.1 操作接口
9.1.2 操作实例
注意第二次put()操作,其拟插入词条的关键码“Yide”,在该词典中已经存在,插入效果等同于用新词条替换旧词条。
9.1.3 接口定义
0001 template <typename K, typename V> struct Dictionary { //词典Dictionary模板类
0002 virtual int size() const = 0; //当前词条总数
0003 virtual bool put ( K, V ) = 0; //插入词条(禁止雷同词条时可能失败)
0004 virtual V* get ( K k ) = 0; //读取词条
0005 virtual bool remove ( K k ) = 0; //删除词条
0006 };
其中所有操作接口均以虚函数形式给出,留在派生类中予以具体实现。
9.1.4 实现方法
不难发现,基于此前介绍的任何一种平衡二叉树,都可便捷地实现词典结构。比如Java语言的java.util.TreeMap类即是基于红黑树实现的词典结构。然而这类实现方式都在不经意中假设“关键码可以比较大小”,故其所实现的并非严格意义上的词典结构。
以下以散列表为例介绍词典结构的实现方法。尽管它在底层引入了某种“序”,但这类“序”只是内部的一种约定;从外部接口看,依然只有“相等”的概念。
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情”