1.什么是哈希表
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
1.1 哈希函数
也叫散列函数,即:根据key,计算出key对应记录的储存位置 position = f(key)
散列函数满足以下的条件:
1、对输入值运算,得到一个固定长度的摘要(Hash value);
2、不同的输入值可能对应同样的输出值;
比较好的哈希函数是time33算法
unsigned int time33(char *str) {
unsigned int hash = 0;
while (*str) {
hash += (hash << 5) + (*str++);
}
return (hash & 0x7FFFFFFF);
}
1.2 哈希冲突(Hash collision)
也就是两个不同输入产生了相同输出值的情况。首先,哈希冲突是无法避免的,因此,哈希算法的选择直接决定了哈希冲突发生的概率;同时必须要对哈希冲突进行处理,方法主要有以下几种:
1、链地址法
链地址法:对Hash表中每个Hash值建立一个冲突表,即将冲突的几个记录以表的形式存储在其中
2、开放地址法
如果遇到冲突的时候怎么办呢?就找hash表剩下空余的空间,找到空余的空间然后插入。就像你去商店买东西,发现东西卖光了,怎么办呢?找下一家有东西卖的商家买呗。
2.哈希表的具体实现
2.1 链地址法实现的哈希表
(1)构建哈希表
首先定义链结点,以结构体Node展示,其中Node有三个属性,一个是key值,一个value值,还有一个是作为链表的指针,还有作为类的哈希表。
#define HASHSIZE 5
typedef struct Node{
char* key;
char* value;
Node *next;
}Node;
class HashTable{
private:
Node* node[HASHSIZE];
public:
HashTable();
~HashTable();
size_t hash(const char* key);
Node* lookup(const char* key);
bool install(const char* key,const char* value);
const char* get(const char* key);
void display();
private:
char* strdup(const char *str); //给每个节点分配空间
};
(2)定义哈希表的构造方法、析构、节点空间分配算法
HashTable::HashTable(){
for (int i = 0; i < HASHSIZE; ++i)
{
node[i] = NULL;
}
}
HashTable::~HashTable(){
for (int i = 0; i < HASHSIZE; ++i)
{
Node *next = node[i];
while(next)
{
Node *tmp = next->next;
free(next->key);
free(next->value);
free(next);
next = tmp;
}
}
}
char* HashTable::strdup(const char *str){
int len=strlen(str)+1;
char *ns=(char*)malloc(len*sizeof(char));
strcpy(ns,str);
if(ns==nullptr)
return nullptr;
else
return ns;
}
(3)定义哈希表的Hash算法,在这里我使用time33算法
size_t HashTable::hash(const char* key){
size_t hash=0;
while (*key) {
hash += (hash << 5) + (*key++);
}
return hash%HASHSIZE;
}
(4)定义一个查找根据key查找结点的方法
思路:首先是用Hash函数计算头地址,然后根据头地址向下一个个去查找结点,如果结点的key和查找的key值相同,则匹配成功。
Node* HashTable::lookup(const char* key){
Node *np;
size_t index;
index = hash(key);
for(np=node[index];np;np=np->next){
if(!strcmp(key,np->key))
return np;
}
return NULL;
}
(5)定义一个插入结点的方法
思路:首先是查看该key值的结点是否存在,如果存在则更改value值就好,如果不存在,则插入新结点。
bool HashTable::install(const char* key,const char* value){
size_t index;
Node *np;
if(!(np=lookup(key))){
index = hash(key);
np = (Node*)malloc(sizeof(Node));
if(!np) return false;
np->key=strdup(key);
if(np->key == nullptr) return false;
np->next = node[index]; //设置新节点的下一个节点
node[index] = np; //把新节点设置为头结点
}
else
{
free(np->value);
}
np->value=strdup(value);
if(np->key == nullptr) return false;
return true;
}
(6)打印出哈希表,用于Debug
void HashTable::display(){
Node* temp;
for (int i = 0; i < HASHSIZE; ++i)
{
if(!node[i]){
printf("[]\n");
}else{
printf("[");
for (temp=node[i]; temp; temp=temp->next)
{
printf("[%s][%s] ",temp->key,temp->value );
}
printf("]\n");
}
}
}
(7)测试代码是否正确
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void testHash(HashTable *ht)
{
const char* key[]={"a","b","c","d","e","f"};
const char* value[]={"value1","value2","value3","value4","value5","value6"};
for (int i = 0; i < 6; ++i)
{
ht->install(key[i],value[i]);
}
}
int main(int argc, char const *argv[])
{
HashTable ht;
testHash(&ht);
ht.display();
return 0;
}
输出如下:
[[d][value4] ]
[[e][value5] ]
[[f][value6] [a][value1] ]
[[b][value2] ]
[[c][value3] ]