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;
}
注意事项(自己重写的时候发现)
-
有关哈希表的值(组的其他讯息) 他们的类型在整个程序里用的是
const void* data
. 这是因为用这个类型的话可以兼容所有类型,比如int
char``double
. 只是后续要打印/要应用该数据的话需要再做强转换 比如:(const char *) element->data
-
在哈希表的结构体里
Thelists
是相当于二级级指针的. 这是因为这个二级指针需要指向不同索引的链表的头节点指针. -
为什么代码里的
while (e != NULL && e->key != key) e = e->next;
需要&& e->key != key
? 这是因为如果这个key已经被储存在整个哈希表里了的话, 返回报错/警告 关于这个key已经加过了. (key和索引是不一样的概念) -
函数
const void* Retrieve(Element element)
为什么不需要带入Hash_Table
变量? 这个的话也可以改,但是在main
函数里面由于Find
函数返回了一个Element
变量,所以这已经确定了此变量的数据已经肯定是在这个Hash_Table
里了,所以没必要. -
自己写代码的时候发现95行的
element = (Element)malloc(sizeof(LinkNode));
写错成element = (Element)malloc(sizeof(LinkNode*));
了, 但是编译没报错. 这个的话需要注意, 虽然分配内存不够,但是完全没报错导致程序运行的时候在free
的时候报错了. 需要留意!