2017-09-20 90 views
0

目前我在C中使用字符串作爲鍵和值的哈希表實現。如果我想存儲整數而不是字符串作爲值,那麼執行此操作的最佳方法是什麼?我正在考慮將整數存儲在字符串中,並在需要時將其轉換爲整數,但對於算術來說效率似乎很低。類似於在c實現的散列表中存儲整數?

insert("money", "13"); 
int i = atoi(get("key1")); 
int sum = i + 10; 
insert("money", itoa(sum)); 

有沒有更好的方法來做到這一點?

編輯:哈希表的實現

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

typedef struct tableentry /* hashtab entry */ 
{ 
    struct tableentry *next; 
    char *key; 
    char *val; 
} tableentry_t; 

typedef struct hashtable 
{ 
    size_t size; 
    struct tableentry **tab; 
} hashtable_t; 

/* creates hashtable */ 
/* NOTE: dynamically allocated, remember to ht_free() */ 
hashtable_t *ht_create(size_t size) 
{ 
    hashtable_t *ht = NULL; 
    if ((ht = malloc(sizeof(hashtable_t))) == NULL) 
     return NULL; 
    /* allocate ht's table */ 
    if ((ht->tab = malloc(sizeof(tableentry_t) * size)) == NULL) 
     return NULL; 
    /* null-initialize table */ 
    int i; 
    for (i = 0; i < size; i++) 
     ht->tab[i] = NULL; 
    ht->size = size; 
    return ht; 
} 

/* creates hash for a hashtab */ 
static unsigned hash(hashtable_t *ht, char *s) 
{ 
    unsigned hashval; 
    for (hashval = 0; *s != '\0'; s++) 
     hashval = *s + 31 * hashval; 
    return hashval; 
} 

/* loops through linked list freeing */ 
static void te_free(tableentry_t *te) 
{ 
    tableentry_t *next; 
    while (te != NULL) 
    { 
     next = te->next; 
     free(te->key); 
     free(te->val); 
     free(te); 
     te = next; 
    } 
} 

/* creates a key-val pair */ 
static tableentry_t *new(char *k, char *v) 
{ 
    tableentry_t *te = NULL; 

    if ((te = calloc(1, sizeof(*te))) == NULL 
     || (te->key = strdup(k)) == NULL 
     || (te->val = strdup(v)) == NULL) 
    { 
     te_free(te); 
     return NULL; 
    } 
    te->next = NULL; 
    return te; 
} 

static tableentry_t *lookup(hashtable_t *ht, char *k) 
{ 
    tableentry_t *te; 
    /* step through linked list */ 
    for (te = ht->tab[hash(ht, k) % ht->size]; te != NULL; te = te->next) 
     if (strcmp(te->key, k) == 0) 
      return te; /* found */ 
    return NULL; /* not found */ 
} 

/* inserts the key-val pair */ 
hashtable_t *ht_insert(hashtable_t *ht, char *k, char *v) 
{ 
    tableentry_t *te; 
    /* unique entry */ 
    if ((te = lookup(ht, k)) == NULL) 
    { 
     te = new(k, v); 
     unsigned hashval = hash(ht, k) % ht->size; 
     /* insert at beginning of linked list */ 
     te->next = ht->tab[hashval]; 
     ht->tab[hashval] = te; 
    } 
    /* replace val of previous entry */ 
    else 
    { 
     free(te->val); 
     if ((te->val = strdup(v)) == NULL) 
      return NULL; 
    } 
    return ht; 
} 

/* retrieve value from key */ 
char *ht_get(hashtable_t *ht, char *k) 
{ 
    tableentry_t *te; 
    if ((te = lookup(ht, k)) == NULL) 
     return NULL; 
    return te->val; 
} 

/* frees hashtable created from ht_create() */ 
void ht_free(hashtable_t *ht) 
{ 
    int i; 
    for (i = 0; i < ht->size; i++) 
     if (ht->tab[i] != NULL) 
      te_free(ht->tab[i]); 
    free(ht); 
} 

/* resizes hashtable, returns new hashtable and frees old*/ 
hashtable_t *ht_resize(hashtable_t *oht, size_t size) 
{ 
    hashtable_t *nht; /* new hashtable */ 
    nht = ht_create(size); 
    /* rehash */ 
    int i; 
    tableentry_t *te; 
    /* loop through hashtable */ 
    for (i = 0; i < oht->size; i++) 
     /* loop through linked list */ 
     for (te = oht->tab[i]; te != NULL; te = te->next) 
      if (ht_insert(nht, te->key, te->val) == NULL) 
       return NULL; 
    ht_free(oht); 
    return nht; 
} 
+0

只是使用C++的一個選項? :| – Alexander

+7

我很困惑,爲什麼你不能只將鍵類型改爲整數,並對整數鍵進行散列。 – Jonesinator

+0

對不起,我不清楚。我想散列一個字符串(使用char指針作爲鍵)並存儲一個整數(整數是值)。雖然我的哈希表實現使用字符串。 –

回答

1

的訪問和操作與哈希表實現相關功能的假設值具有空值終止字符串的形式,他們的意義是由它們的內容(完全進行而不是,例如,由指針本身的值)。除此之外,這從new()ht_insert()函數通過strdup()複製所提供的值的事實中可以明顯看出。因此,如果打算使用這些函數(而不僅僅是底層數據結構),那麼存儲整數的唯一選擇是以某種方式將整數編碼爲字符串,並存儲這些字符串。這就是你已經想出來的。

請注意,順便說一句,如果您希望能夠將字符串和整數存儲在同一個哈希表中,則會出現一些問題。這些表條目不提供任何方式來記錄數據類型元數據,所以爲了避免字符串和數字表示之間的衝突,您需要將數據類型編碼到您存儲的值中 - 不僅是整數,而且是字符串。例如,您可能將值編碼爲第一個字符傳遞數據類型的字符串。因此,或許"S12345"代表字符串"12345",而"I12345"代表整數12345。但是如果你假設所有的值都是統一類型的,那麼你就不需要這樣的技巧。

如果您打算至少編寫一部分替代散列表函數用於在現有數據結構中存儲整數,那麼您將擁有更多選項。例如,您可能會使用指針和整數可以來回轉換(使用實現定義的結果)的事實。但我認爲你拒絕了這種方法,因爲使用替代函數與修改實現實際上是一回事。

+0

好的,也許值得修改實現文件。什麼是最有效的方法呢? –

+0

它取決於@DavidTran,你願意做什麼修改,以及你想支持哪些功能。如果您希望通過通用API支持不同類型的數據值以及特定於類型的處理,則您的條目需要附加一個記錄該值類型的成員。在這種情況下,我也可以將值類型指定爲聯合,也許類似'union {char * as_string; int as_int;/* ... * /} val;'。然後,根據條目中攜帶的類型元數據,選擇聯盟的哪個成員訪問。 –

+0

我想知道是使用那個(聯合)還是將結果中的值存儲爲'void *',將一個結構成員添加到'hashtable_t'來跟蹤它的類型,並相應地使用其他函數。這會工作嗎? –