线段Hash算法(原创)

线段Hash算法通过Hash将Key映射为int,按位比较定位内存块。搜索、插入和删除算法简单高效,冲突处理采用溢出区。最多16次搜索,优于平衡二叉树,提供稳定的O(1)时间复杂度。

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

 

线段Hash算法(原创)


1-1

 

1-1中的每个节点成为bucket,而每个bucket4个元素。我们可以使用以下结构体定义:

struct bucket {

    int elems[4];

};

 

因为每个元素可能指向下一个节点或者一个数据块,所以我们必须有个标志指定元素指向的是下一个节点还是数据块。那么,我们把bucket的定义修改成:

struct bucket {

    unsigned char flags[4];

    int elems[4];

};

如果flags1表示指向的是数据块,如果是-1表示指向的是下一个节点,0就表示指向NULL

 

因为2bit可以确定4个位置,所以第一层比较Hash值的最后两个bit,可以使用以下方式访问:

hvl = hash(key);

pos = hvl & 3;

 

那么第二层就是比较34bit,所以可以使用以下方式取得:

hvl = hash(key);

pos = (hvl >> 2) & 3;

第三、四、五层以此类推。

 

我们可以总结成一个公式:

int getPosition(int hvl, int level)

{

    return (hvl >> (level – 1) * 2) & 3;

}

 

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)

{

    int pos = getPosition(hvl, level);

    if (node->flags[pos] == 1) {

        return node->elems[pos];

} else if (node->flags[pos] == 0) {

return NULL;

} else {

node = node->elems[pos];

level++;

}

}

}

 

2.    插入算法:

插入算法与搜索算法类似,首先使用搜索算法定位到其中一个层的一个元素,然后比较此元素的flags值是否为NULL,如果是NULL就直接新建一个数据块,让此元素指向此数据块。如果flags值是1,表示此位置已经有数据,那么我们就新建一个层,把旧数据和新数据插入到这个新层中,如果继续碰撞,就继续新建层,直到不碰撞为止。插入完成后,我们需要修改flags的值。

 

3.    删除算法:

找到要删除的记录,然后直接把flags设置为0即可。如果整个bucketflags都是0,那么我们可以把这个bucket删除。然后设置上一级的flags值为0。并递归此操作,直到到达根节点为止。

 

解决冲突:

原理上看,int可以保存4394967295个元素,所以基本不会发生两个Hash值一样的Key。不过随着数据增多,Hash值有可能会一样,那么我们怎么处理呢?

我们可以设置一个溢出区,如果到最后一层也有数据,那么我们就可以把新数据保存到溢出区中。搜索的时候,如果到最后一层也不是我们要找的数据,那么就在溢出区中找。如果还是找不到,就说明此数据不存在。溢出区可以使用链表或者数组来保存。

 

效率:

因为每层使用2bit来定位,所以32bitint最多只需要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;
}

用途:可以用于内存索引和文件索引。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值