【问题标题】:My hash table is slower than binary search我的哈希表比二分查找慢
【发布时间】:2016-03-11 10:45:46
【问题描述】:

我已经实现了二进制搜索、线性搜索和哈希表来比较每种时间复杂度。问题是,当我测量时间以查找素数时,我的哈希表比二进制搜索慢得多。以下是我的代码:

// Make the hash table 20 times the number of prime numbers
HashTable::HashTable(std::vector<int> primes)
{
    int tablesize = primes.size() * 20;
    table = new std::list<int>[tablesize];
    size = tablesize;
    for (auto &prime : primes)
        this->insert(prime);
}

// Hash function
int HashTable::hash(int key)
{
    return key % size;
}

// Finds element
int HashTable::find(int key)
{
    // Get index from hash
    int index = hash(key);

    // Find element
    std::list<int>::iterator foundelement = std::find(table[index].begin(), table[index].end(), key);


    // If element has been found return index
    // If not, return -1
    if (foundelement != table[index].end())
        return index;
    else
        return -1;
}



// Adds element to hashtable
void HashTable::insert(int element)
{
    // Get index from hash and insert the element
    int index = hash(element);
    table[index].push_back(element);
}

哈希表.h

#ifndef HASHTABLE_H
#define HASHTABLE_H

#include <list>
#include <iostream>
#include <vector>

class HashTable 
{
private:
    // Each position in Hashtable has an array of lists to store elements in case of collision
    std::list<int>* table;

    // Size of hashtable
    int size;

    // Hashfunction that returns the array location for the given key
    int hash(int key);

public:

    HashTable(int tablesize);
    HashTable(std::vector<int> primes);

    // Adds element to hashtable
    void insert(int element);

    // Deletes an element by key 
    void remove(int key);

    // Returns an element from hashtable for a given key
    int find(int key);

    // Displays the hashtable
    void printTable();

    // Display histogram to illustrate elements distribution
    void printHistogram();

    // Returns the number of lists in hash table
    int getSize();

    // Returns the total number of elements in hash table
    int getNumberOfItems();

    // De-allocates all memory used for the Hash Table.
    ~HashTable();
};

#endif

我已经尝试超过表格大小以消除冲突,但我没有发现任何差异。

【问题讨论】:

  • 这是一个非常好的图表。看起来像人们期望的那样:哈希搜索具有恒定的时间复杂度,而二进制具有对数的时间复杂度。只是哈希表的常数相当大。向量与缓存配合得很好。
  • 如果您将table 的类型更改为std::vector&lt;int&gt; *,您的计时会发生什么变化?
  • 如果您将key % size 更改为key % 12345,我的意思是,硬编码计数?会更快吗?我认为,除法可能有点太慢了。 (顺便说一句,这通常是一种不好的哈希函数,除非除数是素数)。另外,您编译代码时是否开启或关闭优化?
  • int tablesize = primes.size() * 20; - 这是很多浪费的空间(以及,因此,时间)
  • 我相信@msandiford 的建议是使用向量数组而不是列表数组。您对数组中的元素所做的只是搜索和追加; vector 的表现应该优于 list

标签: c++ hashtable binary-search-tree


【解决方案1】:

复杂性二进制搜索是 O(log n) ,而你的查找是线性的,所以 O(n),当你有很多碰撞时,在某一点上是最糟糕的。

【讨论】:

    【解决方案2】:

    哈希表实现的一些次优之处:

    • primes.size() * 20 过多 - 您将获得比必要更多的缓存未命中;尝试 1 到 ~2 之间的值范围以找到最佳点

    • primes.size() * 20 总是偶数,而您使用 key % size 散列的所有素数都是奇数,因此您永远不会散列到一半的桶中,浪费空间并降低缓存性能

    • 您使用链表处理冲突:这意味着您始终跟踪至少一个远离表的连续内存的指针,这很慢,并且对于冲突您在内存中与列表中的每个节点跳转;使用std::vector&lt;int&gt; 存储冲突值将限制在哈希表外的 1 个内存区域的跳跃,或者您可以使用封闭哈希/开放寻址和位移列表来通常在附近的哈希表存储桶中找到元素:我的基准测试发现对于类似的int 值,大约快一个数量级。

    【讨论】:

      【解决方案3】:

      如果您的数据是完全随机的,则可能很难为模运算找到一个好的常数。如果您的数据遵循某种模式,您可能需要尝试遍历一堆候选常量,看看哪个常量在您的数据上表现最好。

      this 帖子中,我展示了如何构建如此大规模的测试。最后,我的哈希表在 1.5 次比较中产生了平均查找,最坏情况为 14 次。该表包含 16000 个条目,大约 2^14。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-24
        • 1970-01-01
        • 1970-01-01
        • 2016-04-03
        • 1970-01-01
        • 2018-06-14
        • 2010-11-26
        • 2013-04-03
        相关资源
        最近更新 更多