dbj2 hash算法的实现

来源:互联网 发布:淘宝棋牌游戏源码 编辑:程序博客网 时间:2024/05/16 11:14

* Copyright 2006 David Crawshaw, released under the new BSD license.
 * Version 2, from http://www.zentus.com/c/hash.html */


#include 
#include 
#include 
#include "hash.h"


/* Table is sized by primes to minimise clustering.
   See: http://planetmath.org/encyclopedia/GoodHashTablePrimes.html */
static const unsigned int sizes[] = {
    53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317,
    196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843,
    50331653, 100663319, 201326611, 402653189, 805306457, 1610612741
};
static const int sizes_count = sizeof(sizes) / sizeof(sizes[0]);  /* sizes数组中的成员个数,这里等于26*/
/*hash表上限因子,当大于这个因子时,需要增长hash表*/
static const float load_factor = 0.65;


struct record {
    unsigned int hash;
    const char *key;
    void *value;
};


struct hash {
    struct record *records;
    unsigned int records_count;
    unsigned int size_index;
};


/*增长hash表,用于扩展hash表容量*/

static int hash_grow(hash *h)
{
    int i;
    struct record *old_recs;
    unsigned int old_recs_length;


    old_recs_length = sizes[h->size_index];
    old_recs = h->records;


     /*本来hash表已经是最大的了,无法再增长了*/
    if (h->size_index == sizes_count - 1) return -1;


    /*取下一个hash表大小,并为新的HASH表申请内存*/
    if ((h->records = calloc(sizes[++h->size_index],sizeof(struct record))) == NULL) {
        h->records = old_recs;
        return -1;
    }
   
    h->records_count = 0;  /* Record个数置零 */


    /*将原来旧的hash表中的数据,添加到新建的hash表中*/
    // rehash table
    for (i=0; i < old_recs_length; i++)
        if (old_recs[i].hash && old_recs[i].key)  /* 如果原Record中保存了有效数据,就添加进新的hash表中 */
            hash_add(h, old_recs[i].key, old_recs[i].value);


    free(old_recs);


    return 0;
}


/* algorithm djb2 
该算法背后有较为复杂的数学证明,从工程角度理解:
1、奇数和素数能比偶数得到更随机的结果,所以可以看到hash函数里使用的是5381和33
2、为什么是5381:djb2算法的作者说,只要是一个比255大而又不是特别大的素数就可以,之所以选5381,就是因为它符合这个条件
3、为什么是33: 作者说很多都可以,采用33是因为n*33可以优化为n<<5+n,可以把一个耗时的乘法操作优化为位移操作,这样会更高效
*/

static unsigned int strhash(const char *str)
{
    int c;
    int hash = 5381;
    while (c = *str++)
        hash = hash * 33 + c; //(hash<<5 + hash)+c
    return hash == 0 ? 1 : hash;
}


/* 申请一个新的HASH表  capacity是HASH表容量*/
hash * hash_new(unsigned int capacity) {
    struct hash *h;
    int i, sind;


    capacity /= load_factor; /* 容量除以上限因子,将容量扩充到上限安全线以上 */

    /* 根据容量获得HASH表尺寸索引 */

    for (i=0; i < sizes_count; i++) 

   {

        if (sizes[i] > capacity) 

        { 

               sind = i; 

                break; 

        }

    }


    /* 为HASH表申请空间 */

    if ((h = malloc(sizeof(struct hash))) == NULL) return NULL;


   /* 为HASH表申请结点空间*/

    if ((h->records = calloc(sizes[sind], sizeof(struct record))) == NULL) {
        free(h);
        return NULL;
    }


    h->records_count = 0;
    h->size_index = sind;


    return h;
}


void hash_destroy(hash *h)
{
    free(h->records);
    free(h);
}

/* 为hash表h增加一个结点成员 */

int hash_add(hash *h, const char *key, void *value)
{
    struct record *recs;
    int rc;
    unsigned int off, ind, size, code;


    if (key == NULL || *key == '\0') return -2;
    /*当hash表中的元素个数达到一定的限制之后,就应该增长hash表,使其能够容纳更多的数据*/
    if (h->records_count > sizes[h->size_index] * load_factor) {
        rc = hash_grow(h);
        if (rc) return rc;
    }


    code = strhash(key);  /* 根据key计算hash key */
    recs = h->records;
    size = sizes[h->size_index]; 
    ind = code % size;     /* 让hash key落在size范围内 */
    off = 0;
    /*查看ind内是否有数据,如果没有就可以加入新的节点*/
    while (recs[ind].key)
        ind = (code + (int)pow(++off,2)) % size;


    recs[ind].hash = code;
    recs[ind].key = key;
    recs[ind].value = value;


    h->records_count++;


    return 0;
}

/* 根据key在hash表h中查找对应的value,返回值为value的指针 */
void * hash_get(hash *h, const char *key)
{
    struct record *recs;
    unsigned int off, ind, size;
    unsigned int code = strhash(key); /* 根据key获得hash key */


    recs = h->records;
    size = sizes[h->size_index];
    ind = code % size;    /* 让hash key落在size范围内 */
    off = 0;


    // search on hash which remains even if a record has been removed,

    // so hash_remove() does not need to move any collision records

   /* 根据hash key查找对应结点 */

    while (recs[ind].hash) {
        if ((code == recs[ind].hash) && recs[ind].key &&
                strcmp(key, recs[ind].key) == 0)
            return recs[ind].value;
        ind = (code + (int)pow(++off,2)) % size;
    }


    return NULL;
}

/* 在hash表h中,深处key值为key的结点,并返回key对应的value指针 */
void * hash_remove(hash *h, const char *key)
{
    unsigned int code = strhash(key);
    struct record *recs;
    void * value;
    unsigned int off, ind, size;


    recs = h->records;
    size = sizes[h->size_index];
    ind = code % size;
    off = 0;


    while (recs[ind].hash) {
        if ((code == recs[ind].hash) && recs[ind].key &&
                strcmp(key, recs[ind].key) == 0) {
            // do not erase hash, so probes for collisions succeed
            value = recs[ind].value;
            recs[ind].key = 0;
            recs[ind].value = 0;
            h->records_count--;
            return value;
        }
        ind = (code + (int)pow(++off, 2)) % size;
    }
 
    return NULL;
}

/* 返回hash表中记录的record数目 */
unsigned int hash_size(hash *h)
{
    return h->records_count;
}

0 0