线段Hash算法原理是:通过Hash算法把Key转换成一个int类型的数字,然后按照每次比较2个bit来定位到一个内存块。结构图如图1-1:

图1-1
图1-1中的每个节点成为bucket,而每个bucket有4个元素。我们可以使用以下结构体定义:
struct bucket {
};
因为每个元素可能指向下一个节点或者一个数据块,所以我们必须有个标志指定元素指向的是下一个节点还是数据块。那么,我们把bucket的定义修改成:
struct bucket {
};
如果flags是1表示指向的是数据块,如果是-1表示指向的是下一个节点,0就表示指向NULL。
因为2个bit可以确定4个位置,所以第一层比较Hash值的最后两个bit,可以使用以下方式访问:
hvl = hash(key);
pos = hvl & 3;
那么第二层就是比较3、4位bit,所以可以使用以下方式取得:
hvl = hash(key);
pos = (hvl >> 2) & 3;
第三、四、五…层以此类推。
我们可以总结成一个公式:
int getPosition(int hvl, int level)
{
}
1.
有了以上的数据结构,那么搜索算法是很简单的。首先根据Key来计算出Hash值,然后把根节点作为第一层向下搜索,直到找到一个元素的flags值为1(找到)或者0(找不到)的元素,搜索结束。算法如下:
void *SegmentHash_Find(SegmetHash *ht, char *key) {
int hvl = hash(key);
int level = 1;
HashNode *node = ht->root;
while (true)
{
} else if (node->flags[pos] == 0) {
return NULL;
} else {
node = node->elems[pos];
level++;
}
}
}
2.
插入算法与搜索算法类似,首先使用搜索算法定位到其中一个层的一个元素,然后比较此元素的flags值是否为NULL,如果是NULL就直接新建一个数据块,让此元素指向此数据块。如果flags值是1,表示此位置已经有数据,那么我们就新建一个层,把旧数据和新数据插入到这个新层中,如果继续碰撞,就继续新建层,直到不碰撞为止。插入完成后,我们需要修改flags的值。
3.
找到要删除的记录,然后直接把flags设置为0即可。如果整个bucket的flags都是0,那么我们可以把这个bucket删除。然后设置上一级的flags值为0。并递归此操作,直到到达根节点为止。
解决冲突:
原理上看,int可以保存4394967295个元素,所以基本不会发生两个Hash值一样的Key。不过随着数据增多,Hash值有可能会一样,那么我们怎么处理呢?
我们可以设置一个溢出区,如果到最后一层也有数据,那么我们就可以把新数据保存到溢出区中。搜索的时候,如果到最后一层也不是我们要找的数据,那么就在溢出区中找。如果还是找不到,就说明此数据不存在。溢出区可以使用链表或者数组来保存。
效率:
因为每层使用2个bit来定位,所以32bit的int最多只需要16次的搜索。所以效率比平衡二叉树要快(最多搜索32次)。另外普通的HashTable算法原理时间复杂度是O(1),不过由于不能保证冲突链的长度,所以线段Hash算法更稳定。
实现代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char u1;
typedef unsigned int u4;
typedef struct bucket
{
void *parent;
u1 flags[4];
void *elems[4];
} bucket;
typedef struct node
{
u4 hv;
void *key;
void *val;
} node;
typedef struct HashTable
{
u4 nodeSize;
bucket *root;
} HashTable;
#define GET_POSITION(h, level) ((h >> (level - 1) * 2) & 3)
#define HASH_NODE 2
#define HASH_ELEM 1
#define HASH_NULL 0
#define THE_MAX_LEVEL 16
static u4 hash(char *arKey, int nKeyLength)
{
register u4 hash = 5381;
/* variant with the hash unrolled eight times */
for (; nKeyLength >= 8; nKeyLength -= 8) {
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
}
switch (nKeyLength) {
case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 1: hash = ((hash << 5) + hash) + *arKey++; break;
case 0: break;
default: break;
}
return hash;
}
static bucket *bucket_alloc(HashTable *ht)
{
bucket *bck;
bck = malloc(sizeof(bucket));
if (bck)
{
memset(bck, 0, sizeof(bucket));
ht->nodeSize++;
}
return bck;
}
static node *node_alloc(u4 hv, void *key, void *val)
{
node *nd;
nd = malloc(sizeof(node));
if (nd)
{
nd->hv = hv;
nd->key = strdup((char *)key);
if (!nd->key)
{
free(nd);
return NULL;
}
nd->val = val;
}
return nd;
}
HashTable *hash_init()
{
HashTable *ht;
ht = malloc(sizeof(hash));
if (ht)
{
ht->nodeSize = 0;
ht->root = bucket_alloc(ht);
if (!ht->root)
{
free(ht);
return NULL;
}
}
return ht;
}
void *hash_find(HashTable *ht, char *key)
{
u4 hv = hash(key, strlen(key));
u4 level = 1;
bucket *bck;
bck = ht->root;
while (bck && level <= THE_MAX_LEVEL)
{
int pos = GET_POSITION(hv, level);//get the position
if (bck->flags[pos] == HASH_NULL)
{
return NULL;
}
else if (bck->flags[pos] == HASH_NODE)
{
bck = (bucket *)bck->elems[pos];
}
else
{
node *nd = (node *)bck->elems[pos];
if (!strcmp(nd->key, key))
{
return nd->val;
}
else
{
return NULL;
}
}
level++;
}
return NULL;
}
int hash_insert(HashTable *ht, char *key, void *val)
{
bucket **bck;
int level = 1;
u4 hv = hash(key, strlen(key));
node *new_node = node_alloc(hv, (void *)key, val);
if (!new_node)
return -1;
bck = &(ht->root);
while (level <= THE_MAX_LEVEL)
{
int pos = GET_POSITION(hv, level);
if ((*bck)->flags[pos] == HASH_NULL)
{
(*bck)->elems[pos] = new_node;
(*bck)->flags[pos] = HASH_ELEM;
return 0;
}
else if ((*bck)->flags[pos] == HASH_NODE)
{
bck = (bucket **)&((*bck)->elems[pos]);
level++;
}
else
{
node *old_node = (*bck)->elems[pos];//save old node
bucket *parent = *bck;
(*bck)->elems[pos] = bucket_alloc(ht);
if ((*bck)->elems[pos])
{
(*bck)->flags[pos] = HASH_NODE;//set the flag with HASH_NODE
bck = (bucket **)&((*bck)->elems[pos]);
level++;//the next level
pos = GET_POSITION(old_node->hv, level);
(*bck)->elems[pos] = (void *)old_node;
(*bck)->flags[pos] = HASH_ELEM;
(*bck)->parent = (void *)parent;
}
else
{
break;
}
}
}
return -1;
}
int main()
{
HashTable *ht;
char key[128];
int i;
void *val;
ht = hash_init();
if (!ht)
{
printf("Unable init HashTable\n");
return -1;
}
for (i = 0; i < 1000; i++)
{
sprintf(key, "key%d", i);
if (hash_insert(ht, key, strdup(key)) == -1) {
printf("Unable insert key\n");
return -1;
}
}
for (i = 0; i < 1000; i++)
{
sprintf(key, "key%d", i);
if ((val = hash_find(ht, key))) {
//printf("Found %s\n", val);
} else {
printf("Not found %s\n", key);
}
}
printf("HashTable node size: %d\n", ht->nodeSize);
return 0;
}
用途:可以用于内存索引和文件索引。
线段Hash算法通过Hash将Key映射为int,按位比较定位内存块。搜索、插入和删除算法简单高效,冲突处理采用溢出区。最多16次搜索,优于平衡二叉树,提供稳定的O(1)时间复杂度。
13万+

被折叠的 条评论
为什么被折叠?



