如何使用 glib 处理哈希表中的冲突

Posted

技术标签:

【中文标题】如何使用 glib 处理哈希表中的冲突【英文标题】:How to use handle collisions in hashtables using glib 【发布时间】:2021-12-18 06:07:24 【问题描述】:

我正在尝试使用哈希表存储一些数据,我决定使用 glib。

所以我可以使用 g_str_hash 来生成密钥,但是有相等的字符串。基本上,数据来自一个 csv 文件,例如,同一个 id 有多行。而且我想使用 id 作为键,并且仍然有单独的行。

所以我试图从 g_str_hash 实现一个类似的算法,但是当密钥已经附加了一些东西时,它会转到下一个可用空间。但是由于类型和如何做的一些问题,我遇到了困难。

  guint hash(char * key, GHashTable *hasht ) 
    unsigned int hash = 5381;
    int c;

    while ((c = *(key++))) 
      hash = ((hash << 5) + hash) + c;
    

    //this is where i get lost on how to check if there is alreay something stored using the hash I generated before

    while (g_hash_table_contains(hasht, hash))
    
      hash++;
    
    
    return hash;
  

Soo 我真的很感谢一些关于如何做到这一点的帮助!非常感谢!

【问题讨论】:

哈希表是一个数组加上一个链表(一般使用forward-chaining进行列表插入)。您的阵列为您的桌子保存桶。每个桶的内容是一个指向节点的指针(或NULL)。它也是链表的head 节点,该链表从该存储桶开始,在发生冲突时添加到该存储桶中。您可能会发现 Coding up a Hash Table 和 Hash tables - eternally confuzzled 很有用。 while (g_hash_table_contains(hasht, hash)) 正在检查是否发生了冲突。 hast++ 试图通过将1 添加到哈希中来避免冲突(如果您还想将信息取回,那将是灾难的根源)。根据我使用glib 的经验,它尽最大努力向您隐藏详细信息,并为您提供从哈希表中添加、查找和删除的工具。见GLib-HashTable(我讨厌新的黑色 glib/gtk 文档方案——它很难看) @DavidC.Rankin 非常感谢!但这只有在我从头开始创建哈希表时才有效,对吗?如果我使用 glib,是否实现了链表? @DavidC.Rankin 哦,谢谢你让我知道,因为我确实需要找回信息!所以你会建议我从头开始创建我的哈希表吗? 使用 glib 哈希表很好。它为您处理冲突和列表创建。当您添加到哈希表时,如果发生冲突,您正在存储的节点会自动添加到从该节点开始的链表中。当您从散列表中检索信息时,您的值会被散列,它指定存储桶以开始在列表中搜索项目,并处理比较以匹配链接列表中的项目。只需使用 glib 提供的哈希表函数(感觉有点盲目——但这就是重点)。否则,你必须重新发明***。 【参考方案1】:

如果您只是想学习,那么为此实现一个自定义哈希表将是一种很好的体验。但是,如果您只是想获得一些工作代码,那么您就过于复杂了。只需使用GHashTableGPtrArrays。类似的东西(未经测试,主要来自内存,我已经好几年没有真正使用过 C 中的 GLib):

/* create hash table */
GHashTable* ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_ptr_array_unref);

for (/* each record in CSV */) 
  GPtrArray* arr = g_hash_table_lookup(ht, record->id);
  if (arr == NULL) 
    /* We don't have an entry for that ID yet */
    arr = g_ptr_array_new_full(1, g_free);
    g_hash_table_insert(ht, g_strdup(record->id), arr);
  
  g_ptr_array_add(arr, g_strdup(record->value));

除非您处于对性能非常关键的循环中,否则应该没问题。

【讨论】:

【参考方案2】:

哈希表通过使用一个数组来工作,其中数组的每个元素都是一个“桶”。哈希表中特定键或元素的存储桶(数组索引)是根据该元素的哈希值计算的。每个桶都包含一个具有相同哈希值(哈希冲突)的元素的链表,如果桶为空,则为 NULL。

因此,您的哈希函数必须始终为给定的键/元素返回相同的值。否则,您将永远无法找到/查找给定的密钥。请注意,在插入哈希表时以及尝试查找给定键时都会调用您的哈希函数。使用此哈希函数,您可以插入一个键“key1”,然后当您稍后尝试查找“key1”时,您将增加哈希值,因为它已经存在于哈希表中,并且不会被找到。您不能根据键是否已存在于哈希表中来增加哈希值。

哈希表还需要一种方法来确定两个键是否相等。例如,这用于在查找中查找您要查找的特定元素。简而言之,查找涉及使用散列函数查找给定键的存储桶,然后使用“key_equal”函数在该存储桶中的所有其他键中查找特定的给定键。插入时会发生类似的事情,这取决于哈希表是否允许多个相同的键,以及当哈希表中已经存在相等的键时它会做什么(例如,失败或替换现有的键。)

我以前没有使用过 GLib,但是查看文档,GLib.HashTable 正在实现键/值对的哈希映射。键是散列的,每个键都有一个关联的值。它不允许重复键。插入重复键会导致旧键被替换。

因此,在这样的背景下,这种数据结构是否合适取决于您的要求以及您以后需要对数据执行的操作。听起来您想将所有行与它们的 id 相关联,单独存储所有行,并且稍后您需要查找具有给定 id 的部分或所有行。在这种情况下,对于 GLib.HashTable,我建议使用 id 作为键,并使用行的集合(可能是链表)作为 HashTable 值。并更正您的哈希函数以删除这部分:

while (g_hash_table_contains(hasht, hash))

  hash++;

【讨论】:

以上是关于如何使用 glib 处理哈希表中的冲突的主要内容,如果未能解决你的问题,请参考以下文章

iOS中的哈希表

哈希表原理及如何避免键值冲突法?

如何在哈希表中均匀分布不同的键?

20162304 2017-2018-1 《程序设计与数据结构》第十一周学习总结

哈希表中的冲突概率

发生冲突时哈希表如何读取正确的值?