C++ 数据结构算法 学习笔记(23) - 哈希表

C++ 数据结构算法 学习笔记(23) - 哈希表

哈希表的原理精讲

哈希表 - 散列表,它是基于快速存取的角度设计的,也是一种典型的“**空间换时间”**的做法

键(key): 组员的编号 如, 1 、 5 、 19 。 。 。

值(value): 组员的其它信息(包含 性别、年龄和战斗力等)

索引: 数组的下标(0,1,2,3,4) ,用以快速定位和检索数据

哈希桶: 保存索引的数组(链表或数组),数组成员为每一个索引值相同的多个元素

哈希函数: 将组员编号映射到索引上,采用求余法 ,如: 组员编号 1

在这里插入图片描述

哈希表的算法实现

哈希链表数据结构的定义

#define Hash_Size	16;

typedef struct _LinkNode
{
	struct _LinkNode* next;
	int key;
	const void* data;    //void is for can save any type of the data for the key
}LinkNode;

typedef LinkNode* Element;
typedef LinkNode* LinkTable;

typedef struct _HashTable
{
	int Table_Size;
	LinkTable* Thelists;  //Double-level pointer for LinkNode
}Hash_Table;

哈希函数

int hash_function(Hash_Table* table, int key)
{
	if (!table)
	{
		cout << "In the hash function its have error because the table is invalid" << endl;
		return -1;
	}
	return key % (table->Table_Size);
}

哈希链表初始化

Hash_Table* Init_hash(int table_size)
{
	if (table_size <= 0)
	{
		table_size = Hash_Size;
	}

	Hash_Table* tmp = NULL;
	tmp = (Hash_Table*)malloc(sizeof(Hash_Table));

	if (tmp == NULL)
	{
		cout << "The initiallized for the Hash Table failed due to unknown reason" << endl;
		return NULL;
	}

	tmp->Table_Size = table_size;
	tmp->Thelists = (LinkTable*)malloc(table_size * sizeof(LinkNode*));

	if (tmp->Thelists == NULL)
	{
		cout << "The initaillized for the Hash Table failed due to unknown reason" << endl;
		free(tmp);
		return NULL;
	}

	for (int i = 0; i < table_size; i++)
	{
		tmp->Thelists[i] = (LinkNode*)malloc(sizeof(LinkNode));

		if (tmp->Thelists[i] == NULL)
		{
			cout << "The initiallized for the Hash Table is failed due to unknown reason" << endl;
			free(tmp->Thelists);
			free(tmp);
			return NULL;
		}
		else
		{
			memset(tmp->Thelists[i], 0, sizeof(LinkNode)); //Set the new created struct to NULL so that it's clear.
		}

	}
	return tmp;
}

哈希链表插入元素

bool Insert_hash(Hash_Table* table, int key, const void* data)
{
	if (!table)
	{
		cout << "The hash table is invalid" << endl;
		return false;
	}
	Element tmp = NULL;
	Element element = NULL;
	tmp = Find(table, key);
	Element cur = NULL;
	Element last = NULL;
	if (tmp == NULL)
	{
		int value = hash_function(table, key);
		element = (Element)malloc(sizeof(LinkNode));
		if (element == NULL)
		{
			cout << "Failed to allocate memory to element due to unknown reason" << endl;
			return false;
		}
		
		element->data = data;
		element->key = key;
		last = table->Thelists[value];
		cur = last->next;


		if (cur==NULL || cur->key > element->key)
		{
			element->next = cur;
			table->Thelists[value]->next = element;
			return true;
		}
		else
		{
			while (cur != NULL && element->key > cur->key)
			{
				last = cur;
				cur = cur->next;		
			}

			element->next = last->next;
			last->next = element;
			return true;
		}

	}
	else
	{
		cout << "The key is already inside the hash table, unable to insert" << endl;
		return false;
	}
}

哈希链表查找元素

Element Find(Hash_Table* table, int key)
{
	if (!table)
	{
		cout << "The find function return because the table is invalid" << endl;
		return NULL;
	}

	LinkNode* L = NULL;
	Element e = NULL;
	int element = hash_function(table, key);
	L = table->Thelists[element];
	e = L->next;
	while (e != NULL && e->key != key) e = e->next;
				
	return e;
}

哈希链表删除元素

bool Delete_hash(Hash_Table* table, int key)
{
	if (!table)
	{
		cout << "The delete hash is failed due to the table is invalid" << endl;
		return false;
	}
	Element tmp = NULL;
	Element last = NULL;
	int value = hash_function(table, key);
	last = table->Thelists[value];
	tmp = last->next;

	if (tmp==NULL)
	{
		cout << "The key box is empty, nothing can delete from the hash key box" << endl;
		return false;
	}

	while (tmp != NULL && tmp->key != key)
	{
		last = tmp;
		tmp = tmp->next;
	}

	if (tmp)
	{
		last->next = tmp->next;
		free (tmp);
		tmp = NULL;
		return true;
	}
	else
	{
		cout << "Not able to find the key inside the hash table" << endl;
		return false;
	}
}

完整代码实现

hash_table.h

#pragma once
#define Hash_Size	16;

typedef struct _LinkNode
{
	struct _LinkNode* next;
	int key;
	const void* data;    //void is for can save any type of the data for the key
}LinkNode;

typedef LinkNode* Element;
typedef LinkNode* LinkTable;

typedef struct _HashTable
{
	int Table_Size;
	LinkTable* Thelists;  //Double-level pointer for LinkNode
}Hash_Table;


hash_table.c

#include <iostream>
#include <string>
#include "hash_table.h"

using namespace std;

Hash_Table* Init_hash(int table_size)
{
	if (table_size <= 0)
	{
		table_size = Hash_Size;
	}

	Hash_Table* tmp = NULL;
	tmp = (Hash_Table*)malloc(sizeof(Hash_Table));

	if (tmp == NULL)
	{
		cout << "The initiallized for the Hash Table failed due to unknown reason" << endl;
		return NULL;
	}

	tmp->Table_Size = table_size;
	tmp->Thelists = (LinkTable*)malloc(table_size * sizeof(LinkNode*));

	if (tmp->Thelists == NULL)
	{
		cout << "The initaillized for the Hash Table failed due to unknown reason" << endl;
		free(tmp);
		return NULL;
	}

	for (int i = 0; i < table_size; i++)
	{
		tmp->Thelists[i] = (LinkNode*)malloc(sizeof(LinkNode));

		if (tmp->Thelists[i] == NULL)
		{
			cout << "The initiallized for the Hash Table is failed due to unknown reason" << endl;
			free(tmp->Thelists);
			free(tmp);
			return NULL;
		}
		else
		{
			memset(tmp->Thelists[i], 0, sizeof(LinkNode)); //Set the new created struct to NULL so that it's clear.
		}

	}
	return tmp;
}
int hash_function(Hash_Table* table, int key)
{
	if (!table)
	{
		cout << "In the hash function its have error because the table is invalid" << endl;
		return -1;
	}
	return key % (table->Table_Size);
}

Element Find(Hash_Table* table, int key)
{
	if (!table)
	{
		cout << "The find function return because the table is invalid" << endl;
		return NULL;
	}

	LinkNode* L = NULL;
	Element e = NULL;
	int element = hash_function(table, key);
	L = table->Thelists[element];
	e = L->next;
	while (e != NULL && e->key != key) e = e->next;
				
	return e;
}

bool Insert_hash(Hash_Table* table, int key, const void* data)
{
	if (!table)
	{
		cout << "The hash table is invalid" << endl;
		return false;
	}
	Element tmp = NULL;
	Element element = NULL;
	tmp = Find(table, key);
	Element cur = NULL;
	Element last = NULL;
	if (tmp == NULL)
	{
		int value = hash_function(table, key);
		element = (Element)malloc(sizeof(LinkNode));
		if (element == NULL)
		{
			cout << "Failed to allocate memory to element due to unknown reason" << endl;
			return false;
		}
		
		element->data = data;
		element->key = key;
		last = table->Thelists[value];
		cur = last->next;


		if (cur==NULL || cur->key > element->key)
		{
			element->next = cur;
			table->Thelists[value]->next = element;
			return true;
		}
		else
		{
			while (cur != NULL && element->key > cur->key)
			{
				last = cur;
				cur = cur->next;		
			}

			element->next = last->next;
			last->next = element;
			return true;
		}

	}
	else
	{
		cout << "The key is already inside the hash table, unable to insert" << endl;
		return false;
	}
}

bool Delete_hash(Hash_Table* table, int key)
{
	if (!table)
	{
		cout << "The delete hash is failed due to the table is invalid" << endl;
		return false;
	}
	Element tmp = NULL;
	Element last = NULL;
	int value = hash_function(table, key);
	last = table->Thelists[value];
	tmp = last->next;

	if (tmp==NULL)
	{
		cout << "The key box is empty, nothing can delete from the hash key box" << endl;
		return false;
	}

	while (tmp != NULL && tmp->key != key)
	{
		last = tmp;
		tmp = tmp->next;
	}

	if (tmp)
	{
		last->next = tmp->next;
		free (tmp);
		tmp = NULL;
		return true;
	}
	else
	{
		cout << "Not able to find the key inside the hash table" << endl;
		return false;
	}
}

const void* Retrieve(Element element)
{
	if (element == NULL)
	{
		cout << "Not able to find the key inside the hash table" << endl;
		return NULL;
	}
	else
	{
		return element->data;
	}
}


bool Destroy_hash(Hash_Table* table)
{
	if (table == NULL)
	{
		cout << "The destroy function is invalid because the table is invalid" << endl;
		return false;
	}
	Element next = NULL;
	Element cur = NULL;
	int value = 0;
	LinkNode* L = NULL;

	for (int i = 0; i < table->Table_Size; i++)
	{
		L = table->Thelists[i];
		cur = L->next;
		while (cur != NULL)
		{
			next = cur->next;
			free(cur);
			cur = next;
		}
		free(L);
	}
	free(table->Thelists);
	free(table);
	return true;
}

void get_element(Hash_Table* table, int key_value)
{
	if (!table)
	{
		cout << "The printout element function invalid because the hash table is invalid" << endl;
		return;
	}
	LinkTable node = NULL;
	node = table->Thelists[key_value];
	node = node->next;
	while (node != NULL)
	{
		cout << (const char*)(node->data) << ", ";
		node = node->next;
	}
}

int main()
{
	const char* elems[] = {"Flower","Fang","Cang" };
	int i = 0;
	Hash_Table* HashTable;
	HashTable = Init_hash(4);
	Insert_hash(HashTable, 1, elems[0]);
	Insert_hash(HashTable, 2, elems[1]);
	Insert_hash(HashTable, 3, elems[2]);
	Delete_hash(HashTable, 1);
	Insert_hash(HashTable, 11, elems[0]);
	Insert_hash(HashTable, 7, elems[1]);
	for (i = 0; i < 8; i++) {
		Element e = Find(HashTable, i);
		if (e) {
			printf("%s\n", (const char*)Retrieve(e));
		}
		else {
			printf("Not found [key:%d]\n", i);
		}
	}
	cout << endl;
	cout << "The element inside for the key = 3 is: ";
	get_element(HashTable, 3);
	system("pause");
	return 0;
}

注意事项(自己重写的时候发现)

  1. 有关哈希表的值(组的其他讯息) 他们的类型在整个程序里用的是 const void* data . 这是因为用这个类型的话可以兼容所有类型,比如 int char``double. 只是后续要打印/要应用该数据的话需要再做强转换 比如: (const char *) element->data

  2. 在哈希表的结构体里 Thelists是相当于二级级指针的. 这是因为这个二级指针需要指向不同索引的链表的头节点指针.

  3. 为什么代码里的while (e != NULL && e->key != key) e = e->next; 需要 && e->key != key? 这是因为如果这个key已经被储存在整个哈希表里了的话, 返回报错/警告 关于这个key已经加过了. (key和索引是不一样的概念)

  4. 函数const void* Retrieve(Element element) 为什么不需要带入 Hash_Table变量? 这个的话也可以改,但是在main函数里面由于Find 函数返回了一个 Element变量,所以这已经确定了此变量的数据已经肯定是在这个Hash_Table里了,所以没必要.

  5. 自己写代码的时候发现95行的element = (Element)malloc(sizeof(LinkNode));写错成element = (Element)malloc(sizeof(LinkNode*));了, 但是编译没报错. 这个的话需要注意, 虽然分配内存不够,但是完全没报错导致程序运行的时候在free的时候报错了. 需要留意!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值