c++目录项 哈希表_第八章 哈希表(三)

图形演示:哈希表 这是图形示例8-1,位于CD目录:\demonstrations\ch08\Demo01 - Hash Tables\。 演示编译 这个演示使用了SDLGUI库,我已经编译了,更多的关于这个库的信息,可以参考附录B,“计算机程序的内存设计”。 要编译这个演示程序,要么打开目录中的工作区域的文件,要么使用附录B中描述的设置创建自己的项目。如果创建自己的项目,需要包含目录中的所有文件。 在这本书的其他章节中,我通常把图形演示放在本章前面。然而,我觉得在给你看一个图形演示之前,我需要建立链溢出哈希表。为了准备链表,我只向你展示了原始哈希表类型,以便你理解它们背后的概念。然而,现实生活,我不会使用任何东西,除了链哈希表。 这个示例将向你展示一个链式哈希表内部工作。图8.7展示了示例运行的屏幕截图。

图8.7 这是图形演示8-1的屏幕截图 表8.1显示了这个示例效果的命令列表 

表8.1 图形示例8-1命令

按钮                  效果

插入(Insert)         此按钮尝试将文本框中的数字插入哈希表

查找(Find)           此按钮在哈希表里查找给定的键值

移除(Remove)     此按钮从哈希表移除给定的键值项

随机(Random)     此按钮放入一个随机数到盒子里

文本框                      这里你可以输入数字来插入,搜索或者移除

这是示例展示一个10个单元哈希表,到目前为止,我在整个章节中都使用相同的方式。哈希表被设计用于存储三位整数,每个整数都是自己的键。事实上,键和存储的数据不必相同,这我稍后再谈。为了简单起见,表中存储每个数字都是它自己的键值。 图8.7展示了哈希表我已经插入了10个随机数。注意任何单元包含的数字最多三个(在单元格6).这意味着为了找到给定的键是否包含在特定的哈希表中,你最多作三次比较。 实现一个哈希表 现在最终到了创建哈希表的时候了。本节代码包含在CD \structures\HashTable.h文件中。如我之前所说,有两个数据与哈希表相关:一个是键另一个是进入表的数据。 数据的键必须与它关联的数据是唯一的。例如,如果你生活在美国,每个人都被分发了社会安全码。如果你把人放进一个哈希表,这将会是一个很好的关联。因为社会安全码是唯一的,没有人有相同的社会安全码。 当你在哈希表里放入数据,你输入了与数据相关的数据和键值。哈希表将存储键和数据。当你想在哈希表里搜索数据,你告诉表你要找的键,表就会返回数据,如果数据存在的话。 HashEntry类 因为哈希表需要为插入的每个项存储两条信息,创建包含两部分数据的类是最简单的。因为这两个数据可以是不同的类型,HashEntry类将有两个模板参数:
template< class KeyType, class DataType >    class HashEntry    {           public:              KeyType m_key;              DataType m_data;     };
这两个模板参数为KeyType和DataType。 HashTable 类 HashTable类将和HashEntry类一样有相同的两个模板参数。表8-2展示了HashTable类将支持的函数列表。

表8.2 hashTable函数

函数名               作用

Constructor      创建哈希表提供固定大小哈希函数

Insert                插入KeyType/DataType两个到哈希表

Find                  在表中查找给定的键并返回数据指针

Remove            表中找到给定的键,并移除与此关联的数据

Count               返回进入表中的数量

数据 HashTable类将需要几个成员变量。它需要追踪表的大小,入表项数量,构成实际表的链表数组,和一个指向哈希函数的指针。如果你不熟悉函数指针,请阅读附录A,"C++ 入门",那里有描述。
template< class KeyType, class DataType >class HashTable{    public:      typedef HashEntry Entry;      int m_size;      int m_count;      Array< DLinkedList< Entry > > m_table;      unsigned long int (*m_hash)(KeyType);};
第5行typedef让你生活感到轻松。没有这个typedef,你需要定义 HashEntry 当你想要使用HashEntry类型的时候,这将使得代码冗长且丑陋。typeof关键字浓缩到了只是Entry,节省了我们大量的定义并使得代码更容易阅读。 大小(m_size)和数量(m_count)功能是明显的。 第三个成员变量,m_table,是一个DLinkedLists数组。数组里的每一项每一个链表都包含Entrys。 第四个成员变量是哈希函数的函数指针。哈希函数以一个键作为参数,返回一个无符号的长整数。我用哈希函数作为函数指针有几个原因。 首先,给哈希表一个独立的哈希函数是非常好的。它允许你在表中使用不同数据。一些哈希表实现直接将哈希函数构造在哈希表中,这使得它有极大是限制。这种方法,你可以有两个哈希表来存储键类型和数据类型,但是两个表可以使用不同的哈希函数。 其次,它很容易使得哈希表指向哈希函数,因此,哈希表自动散列键传递到表中。这样,用户不必记住哈希的键;他或者她可以直接把键传入表中。 再次,你并不希望哈希函数更改。如果用户允许改变哈希函数,哈希表将变得毫无价值。例如,假设用户插入键值到表中使用一个哈希函数。这时候,用户改变哈希哈希函数,并设法用相同的键来搜索。如果新的哈希函数哈希的键是一个不同的数字,那么表将不会找的这个数据,即使它在表中! 稍后你将看到哈希函数的指针是如何工作的。 构造函数 HashTable的构造函数将获得两个参数:表的大小和指向哈希函数的指针。
HashTable( int p_size, unsigned long int (*p_hash)(KeyType) ): m_table( p_size ){    // set the size, hash function, and count.    m_size = p_size;    m_hash = p_hash;    m_count = 0;}
在代码第二行,我使用了标准C++构造标记法来调用m_table构造函数,因此,它将初始化正确的大小。如果你不熟悉标记法,请阅读附录A。那里我有解释。 插入函数 如我之前描述的,Insert函数将获得一个键值对插入到表中。
void Insert( KeyType p_key, DataType p_data ){        Entry entry;        entry.m_data = p_data;        entry.m_key = p_key;        int index = m_hash( p_key ) % m_size;        m_table[index].Append( entry );        m_count++;}
首先,Entry结构体被创建,并将键和数据传入。 然后,m_hash函数指针在传入键后被调用。因为函数支持返回无符号长整型,这个结果可能超出了表的范围。因为这样,结果使用modulo(模)函数修正来让它变成有效的表的下标。 最终,entry被附加到index指向的单元的链表中,并且增加数据数量。 注意 注意这个哈希表本质上使用我之前描述的双哈希法。首先,键哈希成一个整型,然后,整型再次使用模运算哈希 查找函数 此函数旨在搜索哈希表,以查看表中是否有某个键。如果有,它将返回一个键所在entry结构体的指针。如果没有,它将返回0。
Entry* Find( KeyType p_key ){    int index = m_hash( p_key ) % m_size;    DListIterator itr = m_table[index].GetIterator();    while( itr.Valid() )    {      if( itr.Item().m_key == p_key )        return &(itr.Item());      itr.Forth();     }     return 0;}
键哈希到一个索引使用你插入键到表中完全相同的方法。这时,一个迭代器将被创建,它将指向被哈希键的单元中的链表。 这时候迭代器遍历整个链表,来检查这些键是否匹配。如果匹配,entry指针将被返回。如果不匹配,将继续循环。如果键不在表中,将被返回0。 移除函数 移除函数本质上和搜索函数一样,只是替换了返回entry的指针为entry从表中删除。 这个函数返回一个布尔值。true表示entry被找到并移除了;false意味着entry不存在。
bool Remove( KeyType p_key ){  int index = m_hash( p_key ) % m_size;  DListIterator itr = m_table[index].GetIterator();  while( itr.Valid() )  {    if( itr.Item().m_key == p_key )    {      m_table[index].Remove( itr );      m_count—;      return true;    }    itr.Forth();  }  return false;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值