Linux hash表

哈希表的意义就是快速定位查找,使用key值快速定位出数据位置。

流程图

查找时根据key值算出nodes的下标地址:(h->can_ch + h->can_id) % bucktes;,这样就能快速定位,时间复杂度O(1)。

代码实现

头文件:hash.h

#ifndef __NEV__HEAD__
#define __NEV__HEAD__

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

/*此方法采用的是连地址法*/

/*为了不将hash结构暴露在外面,所以就必须typedef*/
typedef struct hash hash_t;

//这是一个hash函数指针,对用户来说应该需要使用一个合适的hash函数
//第一个参数unsigned int指的是桶的大小 第二个参数是key值
typedef unsigned int(*hash_func_t)(unsigned int,void *);

/*返回的是hash表指针,创建hash表*/
hash_t *hash_alloc(unsigned int,hash_func_t);

void *hash_lookup_entry(hash_t *,void *,unsigned int );
void hash_add_entry(hash_t *, void *, unsigned int ,void *, unsigned int );
void hash_free_entry(hash_t *,void *,int );
unsigned int hash_func(unsigned int bucktes, void *key);


struct nev_hash_key {
	int can_ch;          /* can通道号 */
	unsigned int can_id; /* canID */
};
struct nev_hash_s {
	int can_ch;          /* can通道号 */
	unsigned int can_id; /* canID */
	int fd;        		 /* fd */
};

#define MAX_BUCKETS 10
#define CAN_CH		1
#define CAN_FD		8848

#endif

hash文件:hash.c

#include "hash.h"

typedef struct hash_node {
    void *key;//查找依据
    void *data;//数据块,一般是结构体
    struct hash_node *prev;
    struct hash_node *next;
}hash_node_t;

struct hash {
    unsigned int buckets;//桶的个数(大小)
    hash_func_t hash_func;//hash函数指针
    hash_node_t **nodes;//hash表中存放的链表地址
};

//由于获得桶,和得到node节点的接口并不需要外部看见,所以定义成两个内部函数
/*根据key,得到桶*/
static hash_node_t **hash_get_buckets(hash_t *hash,void *key);

/*根据key得到node节点*/
static hash_node_t * hash_get_node_by_key(hash_t *hash,void *key,unsigned int key_size);

//创建hash表
hash_t *hash_alloc(unsigned int buckets, hash_func_t hash_func)
{
    hash_t *hash = (hash_t *)malloc(sizeof(hash_t));
    hash->buckets = buckets;
    hash->hash_func = hash_func;
    unsigned int size = buckets * sizeof(hash_node_t *);
    hash->nodes = (hash_node_t **)malloc(size);
    memset(hash->nodes,0x00, size);
    return hash;
}

//判断
void* hash_lookup_entry(hash_t *hash, void *key, unsigned int key_size)
{
    hash_node_t  *node = hash_get_node_by_key(hash,key, key_size);
    if (NULL == node) return NULL;
    return node->data;
}

//添加hash节点
void hash_add_entry(hash_t *hash, void *key, unsigned int key_size,void *data, unsigned int data_size)
{
    //首先需要查找看此数据项是否存在,如果存在则表明重复了,需要返回
    if (hash_lookup_entry(hash, key, key_size)) {
        printf("duplicate hash key\n");
        return;
    }
    //创建节点,申请内存
    hash_node_t *node = (hash_node_t*)malloc(sizeof(hash_node_t));
    node->next = NULL;
    node->prev = NULL;
    node->key = malloc(key_size);
    memcpy(node->key,key,key_size);
    node->data = malloc(data_size);
    memcpy(node->data, data, data_size);
    //采用头插法,将节点插入到hash表中
    //1.首先得取到桶
    hash_node_t **bucket = hash_get_buckets(hash,key);
    //如果没有节点
    if (*bucket == NULL) {
        *bucket = node;
    }
    else {
        node->next = *bucket;
        (*bucket)->prev = node;
        *bucket = node;
    }
}

//释放节点
void hash_free_entry(hash_t *hash, void *key, int key_size)
{
    //释放节点首先得找到节点,
    hash_node_t *node = hash_get_node_by_key(hash,key,key_size);
    if (node == NULL) return;
    if (node->prev) {
        node->prev->next = node->next;
    }
    else{
        //如果是第一个节点,就必须获得桶节点
        hash_node_t **bucket = hash_get_buckets(hash,key);
        *bucket = node->next;
    }
    if (node->next) {
        node->next->prev = node->prev;
    }
    free(node->key);
    free(node->data);
    free(node);
}

//根据key得到buckets号
static hash_node_t **hash_get_buckets(hash_t *hash, void *key)
{
    unsigned int buckets = hash->hash_func(hash->buckets,key);
    if (buckets >= hash->buckets){
        printf("bad buckets loockup %d %d\n",hash->buckets, buckets);
    }
    return &(hash->nodes[buckets]);
}

//通过key值,得到node节点
static hash_node_t * hash_get_node_by_key(hash_t *hash, void *key, unsigned int key_size) 
{
    hash_node_t **buckets = hash_get_buckets(hash,key);
    hash_node_t *node = *buckets;
    if (node == NULL) return NULL;
    //没有找到,有那两种可能,一种是节点就不存在,另一种是没有找到
    while (node != NULL && memcmp(node->key, key, key_size) != 0) {
        node = node->next;
    }
    return node;
}

//返回下标地址
unsigned int hash_func(unsigned int bucktes, void *key)
{
	struct nev_hash_key *h = (struct nev_hash_key *)key;
	return (h->can_ch + h->can_id) % bucktes;
}

main文件:main.c

#include <stdio.h>
#include "hash.h"

int main()
{
	int i = 0;

	//创建hash表
	hash_t *hash = hash_alloc(MAX_BUCKETS, hash_func);
	if(NULL == hash) {
		printf("error hash alloc failed! return.");
		return;
	}

	//添加节点
	for(i=0; i<MAX_BUCKETS; i++) {
		struct nev_hash_s *c = NULL;
		struct nev_hash_key key;

		c = calloc(1, sizeof(struct nev_hash_s));
		if(NULL == c) {
			printf("error calloc failed! return.");
			return;
		}		
		c->can_ch = CAN_CH;
		c->can_id = i;
		c->fd = i;

		key.can_ch = CAN_CH;
		key.can_id = i;
		
		if(hash != NULL)
			hash_add_entry(hash, &key, sizeof(key), c, sizeof(struct nev_hash_s));
		else
			printf("eason hash == NULL,ERROR");
		free(c);			
	}

	//索引节点
	if (hash != NULL) {
		printf("find fd=");
		for(i=0; i<MAX_BUCKETS; i++) {
			struct nev_hash_key key;
			struct nev_hash_s *cc = NULL;
			
			key.can_ch = CAN_CH;
			key.can_id = i;
			
			cc = (struct nev_hash_s *)hash_lookup_entry(hash, &key, sizeof(key));
			if (cc != NULL) {
				printf("%d/%d ",i,cc->fd);
			}
			else {
				printf("no hash %d\n",i);
			}
		}
		printf("\n");
	}
	return;
}

编译:gcc main.c hash.c -o test

测试打印

[yubo.wang@localhost hash-test]$ ./test 
find fd=0/0 1/1 2/2 3/3 4/4 5/5 6/6 7/7 8/8 9/9 

扩展

散列函数构造方法:

直接定址法:f(key)=axkey+b (a,b为常数)

数字分析法:抽取数字串的后面4位,应用在电话号码

平方取中法:key平方后取中间的3位数

折叠法:数字串分成几组然后相加得出数字

除留余数法:key%p,

处理散列冲突的方法:

开放地址发:一旦发生冲突就寻找下一个空的散列地址,只要散列表足够大就能装下

链地址法:把冲突的地址形成一个链表挂在同一个地址上面

公共溢出区法:把冲突的地址添加在一个新表中维护,查找时先查找基本表再查找溢出表

销毁hash表

//销毁hash表
void hash_free(hash_t *hash)
{
	int i;
	hash_node_t *temp = NULL;
	hash_node_t *node = NULL;
	hash_node_t **buckets = NULL;

	if(NULL == hash) {
        printf("free hash is NULL,return!\n");
		return;
	}
	//遍历node节点并释放
	for(i=0; i<hash->buckets; i++) {
		buckets = &(hash->nodes[i]);
		if (*buckets == NULL) continue;
		node = *buckets;

		while (node != NULL) {
			temp = node;
			node = node->next;
		
		    free(temp->key);
		    free(temp->data);
		    free(temp);
		}
		*buckets = NULL;
	}
	//释放nodes节点二级指针
	free(hash->nodes);
	hash->nodes = NULL;
}

使用方法:

	//释放hash
	hash_free(hash);
	free(hash);
	hash = NULL;

猜你喜欢

转载自blog.csdn.net/TSZ0000/article/details/92983881
今日推荐