哈希表
散列表,它是基于快速存取的角度设计的,也是一种典型的 “空间换时间” 的做法;
键(key) : 组员的编号, 如: 1,2,3…;
值(value): 组员的其他信息 (包含: 性别, 年龄, 战斗力等);
索引: 数组的下标 (0,1,2…), 用以快速定位和检索数据;
哈希桶: 保存索引的数组 (链表或数组), 数组成员为每一个索引值相同的多个元素
哈希函数: 将组员编号映射到索引上, 采用求余法(方法可以有很多种);
算法代码实现:(使用的是链表实现)
#include
#include
using namespace std;
#define DEFAULT_SIZE 32
//哈希表元素定义
typedef struct _listNode {
struct _listNode* next;//指像下一个位置的指针
int key;//索引编号
void* data;//保存的数据
}listNode;
typedef listNode* list;
typedef listNode* elem;
//哈希表结构定义
typedef struct _HashTable {
int TableSize;//哈希桶的总个数
list* Thelists;//保存在链表的位置
}HashTable;
//哈希函数: 根据key 计算索引,定位Hash桶的位置
int Hash(int key, int TableSize) {
return (key % TableSize);
}
//初始化哈希表
HashTable* initHash(int TableSize) {
if (TableSize <= 0) {
TableSize = DEFAULT_SIZE;
}
HashTable* htable = NULL;
htable = new HashTable;
if (htable == NULL) {
cout << "htable new error!" << endl;
return NULL;
}
htable->TableSize = TableSize;
//为hash桶分配内存空间,其为一个指针数组
htable->Thelists = new list[htable->TableSize];//这里需要分配一个指针数组
if (htable->Thelists == NULL) {
cout << "list new error!" << endl;
delete htable;
return NULL;
}
//为hash桶对应的指针数组初始化链表节点
for (int i = 0; i < TableSize; i++) {
htable->Thelists[i] = (list)(new listNode);//需要把分配的节点进行类型转换
if ((list)htable->Thelists[i]==NULL) {
cout << "listNode new error!" << endl;
delete htable->Thelists;
delete htable;
return NULL;
}
else {
//初始化hash桶里的所有元素为0
memset(htable->Thelists[i], 0, sizeof(listNode));
}
}
return htable;
}
//从哈希表中根据键值查找元素
elem Find(HashTable* table, int key) {
list L = NULL;
elem e = NULL;
int i = 0;
i = Hash(key, table->TableSize);
L = table->Thelists[i];
e = L->next;
while (e != NULL && e->key != key) {
e = e->next;
}
return e;
}
//哈希表链表插入元素,元素为键值对
void insertHash(HashTable* table, int key, void* value) {
elem e = NULL;
elem tmp = NULL;
list L = NULL;
e = Find(table, key);
if (e == NULL) {
tmp = new listNode;
if (tmp == NULL) {
cout << "tmp new error!" << endl;
return;
}
//使用前插法
L = table->Thelists[Hash(key,table->TableSize)];
tmp->data = value;
tmp->key = key;
tmp->next = L->next;
L->next = tmp;
}
else {
cout << "key already exist!" << endl;
}
}
//哈希表链表删除元素
void deleteHash(HashTable* table, int key) {
elem e = NULL;
elem last = NULL;
list L = NULL;
int i = Hash(key, table->TableSize);
L = table->Thelists[i];
last = L;
e = L->next;
while (e != NULL && e->key != key) {
e = e->next;
last = e;
}
if (e != NULL) {
//如果键值对存在
last->next = e->next;
delete e;
}
}
//提取哈希表中的数据
void *retrieveHash(elem e) {
return e ? e->data : NULL;
}
//销毁哈希表
void destoryHash(HashTable* hash) {
list L = NULL;
elem cur = NULL;
elem next = NULL;
for (int i = 0; i < hash->TableSize; i++) {
L = hash->Thelists[i];
cur = L->next;
while (cur != NULL) {
next = cur->next;
delete cur;
cur = next;
}
delete L;
}
delete hash->Thelists;
delete hash;
}
//测试代码
int main(void) {
const char* elems[] = { "小王","老王","老宋"};
HashTable* hash;
hash = initHash(16);
insertHash(hash, 1, (char*)elems[0]);
insertHash(hash, 2, (char*)elems[1]);
insertHash(hash, 3, (char*)elems[2]);
deleteHash(hash, 1);
for (int i = 1; i < 4; i++) {
elem e = Find(hash, i);
if (e != NULL) {
cout << "NO."<<i<<" is:"<<(const char*)retrieveHash(e) << endl;
}
else {
cout << "not seek out:"<<elems[i-1] << endl;
}
}
system("pause");
return 0;
}
小练习:
DNA 检测字串匹配
测试色盲的基因组包含 8 位基因,编号 1 至 8。每一位基因都可以用一个字符来表示,这个字符 是’A’、‘B’、‘C’、'D’四个字符;
解答思路:
#pragma once
#ifndef DNA_HASH_H
#define DNA_HASH_H
#include
#include
using namespace std;
#define DEFAULT_SIZE 32
#define H_int32 int
//定义哈希表的元素结构
typedef struct _listNode {
struct _listNode* next;
void* key;
void* data;
}listNode;
typedef listNode* list;
typedef listNode* Elem;
typedef struct _HashTable {
int TableSize;
list* Thelists;
}Hash;
//哈希函数
H_int32 Hash_Func(void* key, H_int32 TableSize);
//初始化哈希表
Hash* initHash(H_int32 TableSize);
//哈希表插入元素和键值对
void insertHash(Hash* hash, void* key, void* value);
//哈希表删除元素,元素为键值对
void deleteHash(Hash* hash, void* key);
//哈希表查找
Elem FindHash(Hash* hash, void* key);
//哈希表销毁
void destotyHash(Hash* hash);
//哈希表元素中提取数据
void* retrieveHash(Elem elem);
#endif // !DNA_HASH_H
#include "DNA_hash.h"
#define BUCKET_SIZE 1024
#define compare(a,b) strcmp((const char*)a,(const char*)b)
#define hash_func SDDMhash
unsigned int SDDMhash(void* key)
{
unsigned int hash = 0;
char* str = (char*)key;
while (*str) {
hash = (*str++) + (hash << 6) + (hash << 16) - hash;
}
return (hash & 0x7FFFFFFF);
}
H_int32 Hash_Func(void* key, H_int32 TableSize)
{
return hash_func(key)%TableSize;
}
Hash* initHash(H_int32 TableSize)
{
Hash* hash = NULL;
hash = new Hash;
if (TableSize <= 0) {
TableSize = DEFAULT_SIZE;
}
if (hash == NULL) {
cout << "初始化内存分配失败!" << endl;
return NULL;
}
hash->TableSize = TableSize;
//为hash桶分配内存空间,其为一个指针数组
//hash->Thelists = (list*)malloc(sizeof(list) * TableSize);
hash->Thelists = new list[hash->TableSize];
if (hash->Thelists == NULL) {
cout << "数组指针内存分配失败!" << endl;
delete hash;
return NULL;
}
//为hash 桶对应的指针数组初始化链表节点
for (int i = 0; i < TableSize; i++) {
hash->Thelists[i] = (list) (new listNode);
if ((list)hash->Thelists[i]==NULL) {
cout << "指针数组内存分配失败!" << endl;
delete hash;
return NULL;
}
else{
memset(hash->Thelists[i], 0, sizeof(list));
}
}
return hash;
}
void insertHash(Hash* hash, void* key, void* value)
{
Elem elem = NULL;
Elem tmp = NULL;
list L = NULL;
elem = FindHash(hash, key);
if (elem == NULL) {
tmp = new listNode;
if (tmp == NULL) {
cout << "插入元素内存分配失败!" << endl;
return;
}
H_int32 code = Hash_Func(key, hash->TableSize);
L = hash->Thelists[code];//前插法
tmp->data = value;
tmp->key = key;
tmp->next = L->next;
L->next = tmp;
}
else {
cout << "已存在的索引!" << endl;
}
}
void deleteHash(Hash* hash, void* key)
{
Elem elem = NULL;
Elem last = NULL;
list L = NULL;
int i = Hash_Func(key, hash->TableSize);
L = hash->Thelists[i];
last = L;
elem = L->next;
while (elem != NULL && elem->key != key) {
last = elem;
elem = elem->next;
}
if (elem) {
last->next = elem->next;
delete elem;
}
}
Elem FindHash(Hash* hash, void* key)
{
list L = NULL;
Elem elem = NULL;
int i = 0;
i = Hash_Func(key, hash->TableSize);
L = hash->Thelists[i];
elem = L->next;
while (elem != NULL && compare(elem->key, key) != 0) {
elem = elem->next;
}
return elem;
}
void destotyHash(Hash* hash)
{
list L = NULL;
Elem cur = NULL;
Elem next = NULL;
for (int i = 0; i < hash->TableSize; i++) {
L = hash->Thelists[i];
cur = L->next;
while (cur != NULL) {
next = cur->next;
delete cur;
cur = next;
}
delete L;
}
delete hash->Thelists;
delete hash;
}
void* retrieveHash(Elem elem)
{
return elem?elem->data:NULL;
}
int main(void) {
const char* elems[] = { "ADBB","BDDC","CDBC","BDBB" };
const char* tester = "ABDABACD";
char cur[5] = { '\0' };
Hash* hash = NULL;
hash = initHash(BUCKET_SIZE);
insertHash(hash, (int*)elems[0], (char*)elems[0]);
insertHash(hash, (int*)elems[1], (char*)elems[1]);
insertHash(hash, (int*)elems[2], (char*)elems[2]);
insertHash(hash, (int*)elems[3], (char*)elems[3]);
deleteHash(hash, (int*)elems[0]);
strncpy_s(cur, tester + 1, 4);
Elem elem = FindHash(hash, (char*)cur);
if (elem) {
cout <<"匹配到基因:"<< (char*)retrieveHash(elem) << endl;
}
else {
cout << "没有找到匹配的基因!" << endl;
}
system("pause");
return 0;
}
#include
#include
#include //pair<>函数的头文件
using namespace std;
//为了兼容更多的数据使用泛型编程
template<class T>
class Hash_tab
{
public:
Hash_tab(int length);//构造函数
Hash_tab(const Hash_tab* h_t);//拷贝构造函数
~Hash_tab();//析构函数
T insert(int key, T val);//数据插入
T remove_key(int key);//根据索引删除数据
T remove_val(T val);//根据数据删除数据
T find_key(int key);//根据索引查找数据
int find_val(T val);//根据数据查找索引
void print();//打印哈希表
private:
//哈希函数,计算哈希桶的位置
int hash_fun(int key) { return key % len; }
int get_lenght() { return len; }
private:
using ht = list<pair<int, T>>;//组合两个数据的链表节点
int len;//哈希表的长度;
ht* elem = NULL;
};
template<class T>
Hash_tab<T>::Hash_tab(int length):len(length)
{
if (this->len <= 0) {
this->len = 1;
}
//分配一个组合了两个数据的指针数组
this->elem = new ht[len];
}
template<class T>
Hash_tab<T>::Hash_tab(const Hash_tab* h_t)
{
this->elem = new ht[len];
for (int i = 0; i < len; i++) {
h_t->elem[i] = this->elem[i];
}
}
template<class T>
Hash_tab<T>::~Hash_tab()
{
if (elem != nullptr) {
delete[]elem;
elem = NULL;
}
}
template<class T>
T Hash_tab<T>::insert(int key, T val)
{
int place = this->hash_fun(key);
for (auto& s : this->elem[place]) {//找到place在elem数组中的位置
if (s.first == key) {//如果key这个索引已经存在有三种情况
//1.返回原有的数据
return s.second;
//2.替换掉原有的数据
//s.first = key;
//s.second = val;
//return val;
//3.在后面继续插入
//elem[place].push_back({key,val});
//return val;
}
}
elem[place].push_back({ key,val });
return val;
}
template<class T>
T Hash_tab<T>::remove_key(int key)
{
int place = this->hash_fun(key);
for (auto& s : elem[place]) {
if (s.first == key) {
return s.second;
elem[place].remove(s);
}
}
return 0;
}
template<class T>
T Hash_tab<T>::remove_val(T val)
{
for (int i = 0; i < len; i++) {
for (auto& s : elem[i]) {
if (s.second == val) {
return s.second;
elem[i].remove(s);
}
}
}
return NULL;
}
template<class T>
T Hash_tab<T>::find_key(int key)
{
int base = this->hash_fun(key);
for (auto& s : elem[base]) {
if (s.first == key) {
return s.second;
}
}
return NULL;
}
template<class T>
int Hash_tab<T>::find_val(T val)
{
for (int i = 0; i < len; i++) {
for (auto& s : elem[i]) {
if (s.second == val) {
return s.first;
}
}
}
return 0;
}
template<class T>
void Hash_tab<T>::print()
{
for (int i = 0; i < len; i++) {
cout << "(" << i << ")->";
for (auto& s : elem[i]) {
cout << "[" << s.first << "," << s.second << "]->";
}
cout << "NULL" << endl;
}
}
int main() {
Hash_tab<string> s(0);
s.insert(0, "张飞");
s.insert(4, "刘备");
s.insert(11,"鲁班");
cout << s.find_key(0) << endl;
cout << s.find_val("刘备") << endl;
s.print();
system("pause");
}