具有完美哈希函数的哈希表是否比数组更好?

时间:2019-04-17 14:06:22

标签: c++ arrays data-structures hashtable

我正在使用自定义数据结构来解释选举数据的项目。目前,我正在决定哪种数据结构最适合存储有关候选人在不同地区单位获得的最终票数的信息。

由于这是一项家庭作业,因此禁止使用该语言构建的数据结构和来自外部库的数据结构。另外,搜索的复杂度必须小于O(n)。

我打算使用的哈希函数看起来像这样

密钥类型将为unsigned int类型,密钥本身将是候选人在选票上的编号。

template<typename K, typename T>
inline int CandidateResultsHashTable<K, T>::hashFunction(const K & key) const
    {
        return key % (amount_of_candidates + 1);
    }

已知候选人的数量,尽管它可以在选举回合之间改变。散列表中存储的所有数据将从一个文件中读取,该文件包含所有候选数据。因此,不应有不属于候选人的任何数字。

我想知道,根据访问时间和内存使用情况,哪种实现会更好。

1 个答案:

答案 0 :(得分:2)

我已将我的评论汇总为一个答案。

这是实现称为map(其他语言的字典)的数据结构的不同方法的摘要。


键值对列表

解决问题的最简单方法是创建键/值对的数组/列表,您只需一一检查,直到找到正确的键即可。 但是,它的效率很差。 O(n)仅适用于小型数据集。速度无关紧要,在数据量非常少的情况下,由于更复杂的数据结构(例如,计算哈希函数)的开销,这种方法甚至可能更快。

如果对键进行排序并使用仅O(log(n))的二进制搜索,则可以大大优化此方法。


哈希表

哈希表的实现非常棘手。您需要足够好的哈希函数。 良好的哈希函数意味着它具有较少的冲突-当两个不同的键具有相同的哈希时。无论如何,您都需要程序来解决这种情况,但是冲突次数过多会降低使用哈希表的好处。

您的实现非常简单。

key % (amount_of_candidates + 1)

在不知道如何分配键的情况下很难判断它是否足够好。

如果键只是连续的数字,那就很好了。 (您甚至不需要+ 1。)实际上,在这种情况下,哈希表有一种特殊情况,您无需检查冲突,因为您可以知道不会有冲突。 此时,您可以不再假装使用哈希表并仅创建一个数组;)每个候选者的位置仅为key - smallest_key。实际上,这将是一个非常有效的解决方案:O(1)。

如果密钥是随机分配的,则不能简化太多。在这种情况下,您的解决方案通常很好。但是,对于哈希表,(amount_of_candidates + 1)的大小太小。它应该比数据量(load factor)大30%。这样会将碰撞次数减少到合理的水平。


二叉树

另一种解决方案是使用直接映射到密钥的二进制表示形式的二进制树。 (0-左分支,1右分支) 这是一种与数组中的二进制搜索非常相似的方法,但是它允许轻松地添加新元素,而无需调整数组的大小并将新元素排序到其中。 该解决方案的缺点是内存需求更高。

您还可以尝试其他类型的二叉树。您只需要记住保持平衡即可保持效率。我对平衡真的不太了解,所以在这个话题上我不会写更多。


结论

在您的情况下,我推断键只是连续的整数,因此我建议使用直接将索引层设置为键值的纯数组的解决方案。 这是一个非常简单,同时又非常有效的解决方案。


编辑

好的,让我们从标题中实际回答问题。

您展示的完美哈希函数的实现与数组没有什么不同。这是对同一事物进行编码的另一种方法,并且根据某些因素,结果汇编可能是相同的。

对于其他散列函数,其中键分布在K的整个范围内,由于需要大量内存,因此直接数组将是不切实际的/无法使用。如果您可以成功分配此数量的内存,则数组会稍微快一些,因为它不需要计算哈希,但是肯定不值得。