哈希表C++简单实现

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值建立一个冲突表,即将冲突的几个记录以表的形式存储在其中
哈希表C++简单实现_第1张图片
2、开放地址法
如果遇到冲突的时候怎么办呢?就找hash表剩下空余的空间,找到空余的空间然后插入。就像你去商店买东西,发现东西卖光了,怎么办呢?找下一家有东西卖的商家买呗。
哈希表C++简单实现_第2张图片

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 
#include 
#include 

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] ]

你可能感兴趣的:(数据结构和算法,数据结构和算法)