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

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;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是基于链表实现的分离散列表的创建、查找和删除代码示例: ```c++ #include <iostream> #include <string> using namespace std; class HashNode { public: int key; string value; HashNode* next; HashNode(int key, string value) { this->key = key; this->value = value; this->next = nullptr; } ~HashNode() { delete next; } }; class HashMap { private: HashNode** table; // 散列表 int capacity; public: HashMap(int capacity) { this->capacity = capacity; table = new HashNode*[capacity]; for (int i = 0; i < capacity; i++) { table[i] = nullptr; } } ~HashMap() { for (int i = 0; i < capacity; i++) { if (table[i] != nullptr) { delete table[i]; } } delete[] table; } int hash(int key) { return key % capacity; } void insert(int key, string value) { int index = hash(key); HashNode* node = new HashNode(key, value); if (table[index] == nullptr) { table[index] = node; } else { HashNode* cur = table[index]; while (cur->next != nullptr) { cur = cur->next; } cur->next = node; } } bool search(int key, string& value) { int index = hash(key); HashNode* cur = table[index]; while (cur != nullptr) { if (cur->key == key) { value = cur->value; return true; } cur = cur->next; } return false; } void remove(int key) { int index = hash(key); if (table[index] == nullptr) { return; } if (table[index]->key == key) { HashNode* tmp = table[index]; table[index] = table[index]->next; tmp->next = nullptr; delete tmp; } else { HashNode* cur = table[index]; while (cur->next != nullptr && cur->next->key != key) { cur = cur->next; } if (cur->next != nullptr && cur->next->key == key) { HashNode* tmp = cur->next; cur->next = cur->next->next; tmp->next = nullptr; delete tmp; } } } }; int main() { HashMap map(10); map.insert(1, "value1"); map.insert(11, "value11"); map.insert(21, "value21"); map.insert(31, "value31"); map.insert(41, "value41"); map.insert(2, "value2"); map.insert(3, "value3"); map.insert(4, "value4"); map.insert(5, "value5"); map.insert(6, "value6"); string value; if (map.search(31, value)) { cout << "Found key 31: " << value << endl; } else { cout << "Not found key 31" << endl; } map.remove(31); if (map.search(31, value)) { cout << "Found key 31: " << value << endl; } else { cout << "Not found key 31" << endl; } return 0; } ``` 在上面的代码中,`HashNode` 类表示散列表中的一个节点,包含键、值和指向下一个节点的指针。`HashMap` 类表示分离散列表,使用 `HashNode*` 指针数组来存储数据,数组中每个元素都是一个链表的头指针,表示散列表中的一个桶。`hash` 方用于计算给定键的散列值,然后根据散列值找到对应的桶,将新节点插入到链表的末尾。`search` 方用于查找给定键的值,先计算散列值,然后遍历桶中的链表查找匹配的键。`remove` 方用于删除给定键的节点,先计算散列值,然后遍历桶中的链表查找匹配的键,如果找到则删除该节点。 在 `main` 函数中,首先创建了一个容量为 10 的散列表,然后插入了 10 个节点。着通过 `search` 方查找键为 31 的节点,输出节点的值,然后通过 `remove` 方删除键为 31 的节点。最后再次通过 `search` 方查找键为 31 的节点,输出节点的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值