欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

写给自己看的散列表(1):分离链接法

程序员文章站 2024-03-22 08:53:34
...

1.定义
分离链接法实际上是通过在散列表的某个位置上用单链表存储一串数据(这些数据通过散列函数得到的地址相同)来解决冲突的问题,所以在散列表的结构中需要有一个数组(也可以说是指针)pLnode *pList去存储这些链表的“表头”节点。而在链表数据结构中,节点实际上是一个“链表结构”类型的指针(比如在之前单链表的代码中头结点的定义就是Lnode *head),这个指针指向一片内存,这片内存里存储了节点的数据和指向下一个节点的指针。这就导致散列表结构中的pList是一个“二级指针”,在后续新建散列表和插入元素的函数中一定要注意指针是否用得正确。

typedef struct node
{
	eletype data;
	struct node *next;
}Lnode;

typedef Lnode * pLnode;

typedef struct table
{
	int size;
	pLnode *pList;
}Htable;

2.新建散列表
step 1:为散列表结构分配内存。
step 2:计算合适的散列表大小,通常的原则是取素数,也就是这里用的NextPrime函数(这个函数是从DSAA in C的源码中copy的),至于为什么取素数,参考知乎Hash时取模一定要模质数吗?
step 3:为散列表结构中的二级指针pList(也就是存储所有链表表头的数组)中的每个元素(即每个表头,是一级指针)分配所指向的内存。

Htable *CreateHashTable(int tsize)
{
	Htable *H;

	if (tsize < MinSize) {
		printf("Table size must >= %d\n", MinSize);
		return NULL;
	}

	H = malloc(sizeof(Htable));
	if (H == NULL) {
		printf("Create table failed\n");
		return NULL;
	}
	
	H->size = NextPrime(tsize);
	H->pList = malloc(sizeof(pLnode) * H->size);
	
	//I prefer to use pointer rather than using array
	//actually *H->pList is the head of a single-linked list, there are "size" single-linked lists
	pLnode *pLhead; 
	pLhead = H->pList; 
	for (int i = 0; i < H->size; i++) {		
		*pLhead = malloc(sizeof(Lnode));
		if (*pLhead == NULL) {
			printf("Out of space\n");
			return NULL;
		}
		(*pLhead)->next = NULL;
		pLhead++; //!!!!!go to head of next single-linked list 
	}
	
	//using array
	/*
	for (int i = 0; i < H->size; i++) {
		H->pList[i] = malloc(sizeof(Lnode));
		if (H->pList[i] == NULL) {
			printf("Out of space\n");
			return NULL;
		}
		H->pList[i]->next = NULL;
	}
	*/
	return H;
}

3.散列函数
用于key对应的散列表地址,返回值是地址也就是unsigned int。

uint Hash(Htable *H, const char *key) //const eletype key != const char *key
{
	uint hashval = 0;
	while (*key != '\0')
		hashval = (hashval << 5) + *key++;
	return hashval % H->size;
}

4.查找
返回值是指向单链表中某个节点的指针(Lnode *)。

Lnode *Find(Htable *H, eletype key)
{
	Lnode *Lhead, *current;
	Lhead = H->pList[Hash(H, key)];
	current = Lhead->next; //Lhead->next is NULL, initialized in CreateHashTable()
	while (current != NULL && strcmp(current->data, key) != 0) //eletype is string, use strcmp!
		current = current->next;
	return current;
}

5.插入
注意用的是从头插入而不是从尾插入,即新节点插入在头结点和旧节点之间,成为新的head->next。

void Insert(Htable *H, eletype key)
{
	Lnode *pos, *Lhead, *s;
	pos = Find(H, key);
	if (pos == NULL) {
		s = malloc(sizeof(Lnode));
		if (s == NULL) {
			printf("Out of space\n");
			return;
		}
		Lhead = H->pList[Hash(H, key)];
		s->next = Lhead->next; //insert new node from head, old nodes move backward
		s->data = key; //should use strcpy, however strcpy is unsafe
		Lhead->next = s;
	}
}