【问题标题】:Hash of Hash in CC中哈希的哈希
【发布时间】:2019-03-26 09:17:18
【问题描述】:

我是 C 语言的新手,因为 6 年后又回到了 C 语言! 想要像这样实现一个代码来存储这些数据的树:

string1 = "foo.bar.foo.\*" string2 = "foo.baz.\*" string3 = "foo.\*.bar"

     foo
      |
  |   |   |
 bar baz  *
  |   |   |
 foo  *  bar
  |
  *

尝试使用 HashTable 来做到这一点:

struct entry_s {
    char *key;
    struct entry_s *value;
    struct entry_s *next;
};

但我认为它不起作用,最好的方法是什么,即使 Hash Map 是可以在 C 中使用的最佳数据结构?

【问题讨论】:

  • 我认为你需要重新启动什么 " 我希望能够在当前哈希的值中设置哈希,例如:" 实际上 意味着。目前尚不清楚您实际上在问什么。双散列只是意味着散列初始键将您带到另一个散列表(与冲突列表相反),其中 另一个 散列函数将您带到目标(实际的冲突列表)。完全不清楚这就是您要完成的任务。
  • 顺便说一句,您的哈希表结构首先看起来很奇怪。为什么会有一个指向表(**table)的双指针?
  • @Serge 正如我所说,我在 Github 中找到了这段代码,这是链接 https://gist.github.com/tonious/1377667 ,不幸的是我没有在 C 中进步,所以我不知道为什么会发生这种情况:(
  • @WhozCraig 这是我想到的 st = new Map();sub_trie.set(word, st); 的等效 js 代码,其中 sub_trie 是哈希值,单词是字符串。

标签: c hash


【解决方案1】:

您似乎想要实现一个具有两个级别的映射:第一级将字符串映射到第二级映射,第二级将另一个键映射到一个值。例如,在 Javascript 语法中:

data = {
    "London": {
        "Paris": 450
    },
    "Paris": {
       "Madrid": 600,
       "Algiers": 700
    }
}

有几种方法可以实现这一点。

Javascript 变量带有它们的类型,因此 yozu 可以对两个级别使用相同的 Map 实现。在 C 中,您可以实现两个具有不同值类型的哈希表,例如:

struct OItem {                  // Outer map item
    const char *key;                // string key
    struct IMap *value;             // inner map value
    struct OItem *next;         
};

struct OMap {                   // Outer map
    struct OItem *head[oSize];      // hash table
};

struct IItem {                  // Inner map item
    const char *key;                // string key
    int value;                      // integer value
    struct IItem *next;
};

struct IMap {                   // Inner map
    struct IItem *head[iSize];      // hash table
};

这将为您提供上面的两级结构。 (这些哈希表具有固定的大小,因此当二级映射稀疏时,您最终可能会浪费大量空间。也许在这里只使用单个列表或平衡树可能会更好。如果您使用二级映射只是模拟一个总是散列相同或相似数据的对象,请考虑在此处使用结构。)

例如,您可以使用此结构和lookup("London", "Paris")。如果您不需要访问内部映射,您还可以使用两个键将两个级别打包到一个大哈希表中:

struct Item {
    const char *key1;
    const char *key2;
    int value;
    struct Item *next;
};

struct Map {
    struct Item *head[hSize];
};

计算哈希时,使用两个键,例如:

static unsigned int hash(const char *s1, const char *s2)
{
    unsigned long hash = 5381u;

    while (*s1) hash = hash * 33 ^ *s1++;
    hash = hash *33;
    while (*s2) hash = hash * 33 ^ *s2++;

    return hash;
}

查找项目时,确保两个键匹配:

int map_find(const struct Map *map,
    const char *k1, const char *k2)
{
    unsigned int h = hash(k1, k2) % hSize;
    struct Item *item = map->head[h];

    while (item) {
        if (strcmp(item->key1, k1) == 0
         && strcmp(item->key2, k2) == 0) {
            return item->value;
        }

        item = item->next;
    }

    return 0;
}

这种方法可能限制性更强,但优点是您没有很多可能超大的哈希表,而只有一个数据结构。

最后,无论你做什么,都不要使用你在 GitHub 上找到的哈希表实现。作者承认这更像是一个编码练习。不处理使用后释放内存,哈希函数较差。


在您在实际用例中进行编辑后,很明显您需要trie。您可以按照您的建议实施 trie。键和值可以是您实现中的任何内容,因此它们也可以是字符串和 trie 节点。您可以调整现有实现以使用指向 trie 节点结构的指针作为值。 (幸运的是,所有比较的东西都保持不变。)

我看到的一个问题是,使用固定的哈希表大小,最终会浪费大量空间。如果你的树是稀疏的,最好只使用链表或平衡二叉树作为映射。在任何情况下,您都必须找到合适的库或自行开发。

【讨论】:

  • 有点接近我的想法,但仍然有点远!我想要 n-level 哈希映射,其中 n 是特定字符串中的多个单词,类似于 { London : {Map1}}, Map1 = {Paris : {Map2}} , Map2 = {Madrid : {Map3}}, ...
  • 那你为什么不把它放在问题中呢?无论如何,我仍然没有看到你想要实现什么,也许这是一个xy problem:你在问哈希表,因为这就是你在 Javascript 中解决问题的方法,但是由于 C 的内置数据很少类型,这可能不是在 C 中实现它的最佳方式。Javascript 通常不会直接转换为 C,反之亦然。
  • 我明白了,请您对可能的数据结构提出您的想法,该结构可以使层次结构像树一样,并且检索密钥的速度很快。
  • 请先告诉我你想达到什么目标。恐怕我不是很清楚。
  • 我已添加到我的答案中。一个示例实现是here。 (它还不可用,它缺乏错误检查和查找。)
【解决方案2】:

您的问题没有真正的意义,我认为这是因为您并不真正了解哈希表是如何工作的,所以这里有一些(粗略且未经测试的)代码向您展示它们是如何工作的:

typedef struct entry_s {
    char *key;
    char *value;
    struct entry_s *next;
} entry_t;

#define MAX_HASH 1234;

entry_t *myHashTable[MAX_HASH];


void insert(char *key, char *value);
    entry_t *entry;

    hash = calculateHash(key) % MAX_HASH;

    // Create entry

    entry = malloc(sizeof(entry_t));
    if(entry == NULL) exit(EXIT_FAILURE);
    entry->key = key;
    entry->value = value;

    // Add entry to the singly linked list for this hash

    entry->next = myHashTable[hash];
    myHashTable[hash] = entry;
}


entry *find(char *key) {
    entry_t *entry;

    hash = calculateHash(key) % MAX_HASH;
    entry = myHashTable[hash];
    while(entry != NULL) {
        if(strcmp(key, entry->key) == 0) {
            return entry;
        }
        entry = entry->next;
    }
    return NULL;
}


void delete(char *key) {
    entry_t *previous = NULL;
    entry_t *entry;

    hash = calculateHash(key) % MAX_HASH;
    entry = myHashTable[hash];
    while(entry != NULL) {
        if(strcmp(key, entry->key) == 0) {

            // Remove entry from the singly linked list for this hash

            if(previous == NULL) {
                myHashTable[hash] = entry->next;
            } else {
                previous->next = entry->next;
            }

            // Free the memory and return

            free(entry);
            return;
        }
        previous = entry;
        entry = entry->next;
    }
}

注意:很好地理解单链表将帮助你弄清楚这个例子在做什么。

【讨论】:

  • 你在这里添加char *作为哈希值,如果键是静态的但值应该是指向另一个哈希的指针呢?也许这对你有意义。
猜你喜欢
  • 2011-05-27
  • 2019-12-20
  • 2012-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-10
  • 1970-01-01
相关资源
最近更新 更多