【问题标题】:Necessary Structs to implement a HashTable (that uses a Linked List to avoid collisions) in C?在 C 中实现 HashTable(使用链表来避免冲突)的必要结构?
【发布时间】:2018-03-13 22:10:00
【问题描述】:

大家好,过去几天我一直在阅读 C 语言中的指针、结构和数据结构。现在我正在尝试按照本教程在 C 中实现哈希表:https://www.tutorialspoint.com/data_structures_algorithms/hash_table_program_in_c.htm

但是,tutorialspoint 假设只有 1 个哈希表,并且它正在使其成为全球性的。此外,tutorialspoint 不考虑碰撞。

我想创建一个不将自己限制为单个哈希表的结构,并且我计划将链接(以处理冲突)与链表结合起来。我有以下内容:

typedef struct node {
    int key;
    int data;
    struct node* next;
} node;

typedef struct linkedList{
    node* head;
    node* tail;
    size_t size;
} linkedList;

typedef struct hashTable{
    //perhaps an array of linked list? This member is what I need help with
    //And other potential members I am overlooking
    size_t size
} hashTable;

我需要解释的一些事情:

  1. 节点结构用作一对键/数据,它有一个指向具有相同哈希码的下一对的指针。所有具有相同哈希码的节点都将在同一个链表中。

  2. linkedList 结构表示一个链表,最终将成为哈希表中的一行。所有链表都将成为哈希表中的一行。

  3. hashTable 结构包含所有的linkedList。

如何在我的 hashTable 结构中创建一个linkedList 结构的数组?在我的 3 个结构中是否还有其他我忽略的成员?

感谢任何帮助!

附:我打算使用我在 Linked List 程序中编写的方法。这是它的早期版本的链接:https://codereview.stackexchange.com/questions/176904/my-linked-list-implementation-in-c

【问题讨论】:

  • 你的结构的成员是完全任意的,需要反映你想要使用的元素。我建议您可以考虑将 sizedata 和 key 组合到一个结构中,除非它们不相关。 (看来它们来自您显示的代码)。通常,密钥用于从加密对象中捕获某些内容。散列后的东西无法恢复,为什么需要密钥?
  • @rykker 结构对(我从 item 重命名,以清除问题)结构意味着包含其数据和密钥的对。 hashTable 结构是包含对的实际哈希表。所以我并不是说这些是相关的。
  • @rykker 我编辑了 OP。也许 HashMap 是我想要完成的一个更好的名字?该程序旨在将值存储在表中,然后允许通过键进行东访问。用户应该能够恢复在我的表中插入的内容。
  • "所有具有相同键的节点都将在同一个链表中。" -- 哈希表中的键是唯一的。您的意思是具有相同哈希码的所有节点。
  • @M Oehm 是的!我是那个意思!谢谢!

标签: c linked-list hashtable


【解决方案1】:
typedef struct linkedList{
    node* head;
    node* tail;
    size_t size;
} linkedList;

我认为您在这里并不需要size_t size。对于实际的哈希表结构,您可以这样做:

typedef struct has_map {
    linkedList** table; // stores the actual collision chains.
    size_t table_capacity; // the current capacity of table.
    size_t size; // number of mappings in this hash map.
} hash_map;

另外,我建议您将table 的长度保持为2 的幂:2、4、8、16,...这样您可以通过位操作hash_code & (hash_map->table_capacity - 1) 更改hash_code % hash_map->table_capacity

【讨论】:

    【解决方案2】:

    您可以使用节点和列表结构。哈希表将只是一个带有列表数组的结构。哈希表的大小可以是动态的,但让我们使用一个恒定的大小:

    enum {
        hashSize = 32
    };
    
    typedef struct hashTable {
        linkedList list[hashSize];
        size_t size;                     // How many elelents overall?
    } hashTable;
    

    你的哈希函数必须返回一个小于hashSize的无符号哈希值。

    但是你并不真的需要链表结构。这种结构可以方便地在前端和末端插入,并且它还保留了其元素的数量。链表的元素是无序的,所以你总是可以在前面插入,而且你并不需要大小,所以你可以通过指向它们头部的指针来定义链表,最初都是NULL

    typedef struct hashTable {
        node *head[hashSize];
        size_t size;
    } hashTable;
    

    (但如果你已经有链表的代码,那么链表数组可能是要走的路。)


    关于您链接的教程点代码的注释:它确实可以解决冲突,但它使用了另一种方法:每个哈希码只有一个插槽。如果一个哈希码的槽已经被使用,它将使用下一个空闲的哈希码。

    这种方法意味着您不能简单地删除元素。想象一下,您插入一个哈希值为 5 的项目,但该插槽已被另一个具有不同键但哈希码相同的项目占用。假设 6 和 7 也被占用,因此您将项目插入插槽 8。如果您搜索该项目,搜索首先查看插槽 5,但那里的项目不匹配。增加 has 代码,最终在 slot 8 中找到该项目。如果我们首先找到一个空 slot,则搜索将被取消,但没有成功。移除插槽 5 中的物品会使插槽 8 中的物品无法访问。因此,必须在该插槽中放置一个哑元。您的链表方法不需要这样的虚拟元素。

    (tutorialspoint 方法也意味着如果哈希表已满,您将搜索所有元素,这并不比搜索未排序的数组更好。链表哈希表永远不会满,但是当表中的项目远大于哈希表大小,性能会下降。)

    【讨论】:

    • 感谢@MOehm 的回复和对教程点的解释!我在链表上花了很多时间,所以我将使用这种方法!另外,如何访问 hashTable 结构上的数组?假设我创建了一个 hashTable 结构对象并将其命名为 temp。我会通过这样做访问数组: temp->list[i] 吗?
    • 是的,这就是您访问数组的方式。
    猜你喜欢
    • 1970-01-01
    • 2018-10-03
    • 1970-01-01
    • 1970-01-01
    • 2014-10-28
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    相关资源
    最近更新 更多