【问题标题】:Hashtable with linked list not work in c?带有链表的哈希表在c中不起作用?
【发布时间】:2021-12-25 09:30:01
【问题描述】:

我在 C 中为带有链表(为了避免冲突)的哈希表分配内存时遇到了问题。

我认为问题在于项目的分配。 我做了两个 scruct,一个用于单个项目,一个用于表。 第一个有两个指向下一个和上一个项目的指针。 请帮我。 我一直使用此代码直到 3 天。 代码:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define CAPACITY 50000 

unsigned long hash(char *str) { 
    unsigned long int stringsum = 0; 
    for(; *str != '\0'; str++) { 
        stringsum += *str; 
    } 
    return stringsum % CAPACITY; 
    
} 


typedef struct item  { 
    char *value; 
    char *key; 
    struct item *next; 
    struct item *prev; 
} ht_item; 

typedef struct hashtable { 
    ht_item **items; 
    int dim; 
    int count; 
} HashTable; 

HashTable* create_table(int size); HashTable* create_item(HashTable *table, char *value, char *key); 
void print_table(HashTable* table, int dim); 


int main(void) { 
    HashTable *table = create_table(CAPACITY); 
    table = create_item(table, "Giuseppe", "Nome"); 
    print_table(table, CAPACITY); 
    return 0; 
    
} 


HashTable* create_item(HashTable *table, char *value, char *key) { 
    unsigned long index = hash(key);
    printf("%u", index); 
    ht_item *_iterator; ht_item *prev;
    for(_iterator = table->items[index], prev = NULL; _iterator != NULL; prev = _iterator, _iterator = _iterator->next);
     _iterator = (ht_item*)malloc(sizeof(ht_item));
     _iterator->key = (char*)malloc(200);
     _iterator->value = (char*)malloc(200); 
     strcpy(_iterator->key, key);
     strcpy(_iterator->value, value);
     _iterator->next = NULL;
     _iterator->prev = prev; 
    return table; 
} 


HashTable* create_table(int size)
{ 
    HashTable *table = (HashTable*)malloc(sizeof(HashTable));
    table->dim = size; 
    table->items = (ht_item**)calloc(size, sizeof(ht_item*)); 
    for(int i = 0; i < size; i++){ 
        table->items[i] = NULL; 
    } 
    
    return table; 
} 


void print_table(HashTable* table, int dim) { 
    for(int i = 0; i < CAPACITY; i++)
     { 
        if(table->items[i] != NULL)
         { ht_item *_iterator = (ht_item*)malloc(sizeof(ht_item));
             for(_iterator = table->items[i]; _iterator != NULL;
             _iterator = _iterator->next)
             { 
                printf("Key: %s\tValue: %s\n", _iterator->key, _iterator->value); 
                } free(_iterator); 
            } 
        } 
}

【问题讨论】:

  • 嗯.. 看看你的create_item 函数并考虑这一点:你使用该 for 循环来查找冲突链中的最后一个条目,存储指向 said-same_iterator 的指针。现在,在那个循环之后你要做的第一件事是什么?你认为_iterator = (ht_item*)malloc(sizeof(ht_item)); 会对你刚刚努力(而且完全不必要,顺便说一句)找到的_iterator 变量值做什么?在调试器中运行您的代码并逐步执行该函数,观察完成后表格的外观。实际表未修改,您的所有努力都被浪费了(并且内存泄漏)。
  • @WhozCraig OP 还在prev 中保留了_iterator 的副本。
  • 没有分配-&gt;next。 @OP 在处理哈希表之前首先处理链表的正确实现。例如,如果它是一个双向链表,则没有理由遍历链表中的所有节点来找到最后一个节点。它也不必是双向链表,您也可以在头端插入新节点。
  • 我使用迭代器来查找最后一个元素。然后我为迭代器指向的块(我要插入的元素)分配内存。
  • 我使用 prev 将元素保留在 _iterator 之前,使其指向 ht_item 对象的 prev 指针

标签: c memory linked-list hashtable


【解决方案1】:

对您的代码进行了一些更改。请仔细阅读包含// CHANGE HERE 评论的块。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define CAPACITY 50000

// CHANGE HERE - additional parameter, value to be used for modulo
unsigned long hash(char *str, unsigned int mod_value) { 
    unsigned long int stringsum = 0; 
    for(; *str != '\0'; str++) { 
        stringsum += *str; 
    }
    // CHANGE HERE - use mod_value instead of CAPACITY
    return stringsum % mod_value; 
    
} 


typedef struct item  { 
    char *value; 
    char *key; 
    struct item *next; 
    struct item *prev; 
} ht_item; 

typedef struct hashtable { 
    ht_item **items; 
    int dim; 
    int count; 
} HashTable; 

HashTable* create_table(int size); HashTable* create_item(HashTable *table, char *value, char *key); 
void print_table(HashTable* table, int dim); 


int main(void) { 
    HashTable *table = create_table(CAPACITY); 
    table = create_item(table, "Giuseppe", "Nome"); 
    print_table(table); 
    return 0; 
} 


HashTable* create_item(HashTable *table, char *value, char *key) {
    // CHANGE HERE - function arguments validation
    if (table == NULL)
    {
        return table;
    }
    if (value == NULL || key == NULL)
    {
        printf("Key or value is null\n");
        return table;
    }

    // CHANGE HERE - pass table->dim to hash
    unsigned long index = hash(key, table->dim);
    printf("Index: %lu\n", index);
    
    // CHANGE HERE - simplified the code a bit
    ht_item* new_node = malloc(sizeof(ht_item));
    new_node->key = malloc(200 * sizeof(char));
    strncpy(new_node->key, key, 200);
    new_node->value = malloc(200 * sizeof(char));
    strncpy(new_node->value, value, 200);
    
    // CHANGE HERE - if first node in index
    if (table->items[index] == NULL)
    {
        table->items[index] = new_node;
        return table;
    }
    
    ht_item *cur, *prev = NULL;
    for(cur = table->items[index]; cur != NULL; prev = cur, cur = cur->next);
    
    prev->next = new_node;  // CHANGE HERE - it seems this line was missing
    new_node->prev = prev;
    new_node->next = NULL;
    
    return table; 
} 


HashTable* create_table(int size)
{ 
    HashTable *table = (HashTable*)malloc(sizeof(HashTable));
    table->dim = size; 
    table->items = (ht_item**)calloc(size, sizeof(ht_item*)); 
    for(int i = 0; i < size; i++){ 
        table->items[i] = NULL; 
    } 
    
    return table; 
} 


void print_table(HashTable* table) {
    // CHANGE HERE - function arguments validation
    if (table == NULL)
    {
        printf("Table is null\n");
        return;
    }
    // CHANGE HERE - change CAPACITY to dim
    for(int i = 0; i < table->dim; i++)
    {
        //printf("i = %d [%d]\n", i, table->items[i] == NULL);
        if(table->items[i] != NULL)
        {
            // CHANGE HERE - removed unnecessary malloc
            ht_item *_iterator = NULL;
            for(_iterator = table->items[i]; _iterator != NULL; _iterator = _iterator->next)
            { 
                printf("Key: %s\tValue: %s\n", _iterator->key, _iterator->value);
            }
        } 
    } 
}

【讨论】:

  • 请解释每个CHANGE HERE。为什么要改变?错误是什么?另请解释为什么名为print_table 的函数通过释放存储在其中的所有键和值以及一些但不是所有节点来破坏表。
  • @n.1.8e9-where's-my-sharem.,编辑了这些 cmets 并包含了详细信息。关于print_table,我认为这是作者释放内存的意图。
  • 不,作者的意图被分配一个完全不必要的struct item 掩盖了,然后很快就忘记了它。 OP 显然假设 free 最后是 malloc 的自然配对(它不是)。
  • 抱歉错过了
  • 如果我们考虑 OP 的意图,在 print_table 函数中使用 dim 参数是一个很差的改进。 table 结构已经有一个在创建时分配的 dim 成员。这应该用于print_table 函数和直接在哈希函数返回值上运行的create_item 函数(请参阅我的答案和其他 cmets)。应该删除 print_table 函数的 dim 参数。
【解决方案2】:

create_item 函数可以而且应该被简化。 我已将一些 cmets 内联。

HashTable* create_item(HashTable *table, char *value, char *key) { 
    // use modulo operator here, not in the hash function
    unsigned long index = hash(key) % table->dim;

    // nicer way of allocating
    ht_item *insert = malloc(sizeof *insert);

    // use strdup to avoid wasted memory and buffer overflows
    insert->key = strdup(key);
    insert->value = strdup(value);

    // head insert rather than tail
    insert->next = table->items[index];
    table->items[index] = insert;        

    return table; 
}

我放弃了prev 成员的使用。如果您在某处需要它,那么添加它是一项练习。我认为对于简单的哈希表来说没有必要。

【讨论】:

  • 在原始问题上查看我的 cmets 以获得一些额外的改进。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-17
  • 2017-05-17
  • 2019-09-22
  • 1970-01-01
  • 2015-05-25
相关资源
最近更新 更多