哈希闭散列实现

在上一篇文章中,我们已经对哈希的基础有了一个大概的认识,但是对其实现还没有做具体的解释,在这篇文章中,我们将对这一部分做出一个详细的解释。
有关哈希基础,
请参考上篇https://blog.youkuaiyun.com/aaronlanni/article/details/79701843
一、实现静态的哈希表
这里写图片描述
下面,将给出静态实现:

//静态
#define MAX_SIZE 10

typedef enum State{ EMPTY, DETELE, EXIST };

template<class K>
struct Elem
{
    K _key;
    State _state;
};

template<class K,bool IsLine=true>
class HashTable
{
public:
    HashTable()
    {
        //清空每个位置
        for (int i = 0; i < MAX_SIZE; ++i)
        {
            _hashtable[i]._state = EMPTY;
        }
        _size = 0;
    }
    //插入元素
    bool Insert(const K&key)
    {
        //负载因子
        if (_size * 10 / MAX_SIZE>7)
            return false;
        size_t HashAdder = Func(key);
        size_t stateAdder = HashAdder;
        size_t i = 0;
        //找空位置
        while (_hashtable[HashAdder]._state != EMPTY)
        {
            if (_hashtable[HashAdder]._state == EXIST&&_hashtable[HashAdder]._key == key)
                return false;

            if (IsLine)
                LineCheck(HashAdder);
            else
                SecondCheck(HashAdder, i++);
            if (HashAdder == stateAdder)
                return false;
        }
        //插入元素
        _hashtable[HashAdder]._key = key;
        _hashtable[HashAdder]._state = EXIST;
        ++_size;
        return true;
    }
    //查找元素
    int Find(const K&key)
    {
        //在哈希表中找对应的位置
        size_t HashAdder = Func(key);
        size_t i = 0;
        //找空位置
        while (_hashtable[HashAdder]._state != EMPTY)
        {
            if (_hashtable[HashAdder]._key == key)
                return _hashtable[HashAdder]._key;

            if (IsLine)
                LineCheck(HashAdder);
            else
                SecondCheck(HashAdder, i++);
        }
        return -1;
    }
    //删除元素
    bool DElete(const K&key)
    {
        size_t HashAdder = Func(key);
        int ret = Find(key);
        if (-1 != ret)
        {
            _hashtable[HashAdder]._state = DETELE;
            --_size;
            return true;
        }
        return false;
    }
    size_t Size()
    {
        return _size;
    }
    bool Empty()
    {
        return _size == 0;
    }
private:
    //线性探测
    void LineCheck(size_t& HashAdder)
    {
        ++HashAdder;
        if (HashAdder >= MAX_SIZE)
            HashAdder = 0;
    }
    //二次探测
    void SecondCheck(size_t HashAdder,size_t i)
    {
        HashAdder = HashAdder + ((i << 1) + 1);
        if (HashAdder >= MAX_SIZE)
            HashAdder %= MAX_SIZE;
    }
private:
    //哈希函数
    size_t Func(const K&key)
    {
        return key%MAX_SIZE;
    }
private:
    Elem<K> _hashtable[MAX_SIZE];
    size_t _size;
};

二、实现动态的哈希表
在静态哈希表的基础上,由于利用静态存储,很容易将哈希表存满,因此,利用动态的哈希表,从而可以实现不断的给哈希中存储元素,但是在上面的基础上,我们难免还会想到,在哈希中,我们不仅仅是对整型数值的存储,在必要的时候,我们还需要存储字符串,因此在对于字符串的存储中,我们对哈希地址的计算便是一个大问题,因此将字符串转换为整数,然后对其哈希地址进行计算即可。
(对于有关字符串转整数,可以采取两种措施:1、对其字符串取地址,从而可以取得字符串的整数形式2、利用字符串转整数的函数,同时在此基础上,且利用类与特化从而实现字符串的转换即可)
在实现动态的哈希表之时,利用除留余数法即可。
基本实现如下所示:
comm.hpp

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<string.h>
#include<iostream>
using namespace std;
// 使用素数表对齐做哈希表的容量,降低哈希冲突
const int _PrimeSize = 28;
static const unsigned long _PrimeList[_PrimeSize] =
{
    53ul, 97ul, 193ul, 389ul, 769ul,
    1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
    49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
    1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
    50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
    1610612741ul, 3221225473ul, 4294967291ul
};
//除留余数法
size_t GetNextPrime(size_t num)
{
    for (int i = 0; i < _PrimeSize; ++i)
    {
        if (_PrimeList[i] > num)
            return _PrimeList[i];
    }

    //走到最后,仍然没有找到
    return _PrimeList[_PrimeSize - 1];
}
static size_t BKDRHash(const char * str)
{
    unsigned int seed = 131; // 31 131 1313 13131 131313
    unsigned int hash = 0;
    while (*str)
    {
        hash = hash * seed + (*str++);
    }
    return (hash & 0x7FFFFFFF);
}
//转换函数(整数)
template<class K>
class KeyToIntDef
{
public:
    size_t operator()(const K&key)
    {
        return key;
    }
};
//字符串,需要特化

class StringToInt
{
public:
    size_t operator()(const string&  key)
    {
        return BKDRHash(key.c_str()); //key.c_str()将其
    }
};

hashtable.hpp

//可以存取字符串
#include"Common.hpp"

//如果需要存取字符串,则需要在求取哈希地址的时候,需要转换,利用仿函数

template<class K>
struct Elem
{
    K _key;
    State _state;
};

template<class K, class KeyToInt = KeyToIntDef<int>, bool IsLine = true>
class HashTable
{
public:
    HashTable(size_t capacity = 10)
    {
        capacity = GetNextPrime(capacity);//获得的哈希表的长度
        _hashtable = new Elem<K>[capacity];
        _capacity = capacity;
        for (size_t i = 0; i < _capacity; ++i)
            _hashtable[i]._state = EMPTY;
        _size = 0;
    }
    //插入元素
    bool Insert(const K&key)
    {
        CheckCapacity();
        size_t HashAdder = Func(key);
        size_t stateAdder = HashAdder;
        size_t i = 1;
        //找空位置
        while (_hashtable[HashAdder]._state != EMPTY)
        {
            if (_hashtable[HashAdder]._state == EXIST&&_hashtable[HashAdder]._key == key)
                return false;

            if (IsLine)
                LineCheck(HashAdder);
            else
                SecondCheck(HashAdder, i++);
            if (HashAdder == stateAdder)
                return false;
        }
        //插入元素
        _hashtable[HashAdder]._key = key;
        _hashtable[HashAdder]._state = EXIST;
        ++_size;
        return true;
    }
    //查找元素
    int Find(const K&key)
    {
        //在哈希表中找对应的位置
        size_t HashAdder = Func(key);
        size_t i = 1;
        //找位置
        while (_hashtable[HashAdder]._state != EMPTY)
        {
            if (_hashtable[HashAdder]._state == EXIST)
            {
                if (_hashtable[HashAdder]._key == key)
                    return HashAdder;
            }

            if (IsLine)
                LineCheck(HashAdder);
            else
                SecondCheck(HashAdder, i++);
        }

        return -1;
    }

    //删除元素
    bool DElete(const K&key)
    {
        size_t HashAdder = Func(key);
        int ret = Find(key);
        if (-1 != ret)
        {
            _hashtable[HashAdder]._state = DETELE;
            --_size;
            return true;
        }
        return false;
    }

    size_t Size()
    {
        return _size;
    }
    bool Empty()
    {
        return _size == 0;
    }

    ~HashTable()
    {
        if (_hashtable)
        {
            _capacity = 0;
            _size = 0;
            delete[] _hashtable;
        }
    }
private:
    //交换函数
    void Swap(HashTable<K,KeyToInt,IsLine>& ht)
    {
        swap(_hashtable, ht._hashtable);
        swap(_size, ht._size);
        swap(_capacity, ht._capacity);
    }
    //增容函数
    void CheckCapacity()
    {
        if (_size * 10 / _capacity >= 5)
        {
            size_t newCapacity = GetNextPrime(_capacity);
            HashTable<K,KeyToInt,IsLine> newHt(newCapacity);//创建新的哈希表
            //搬移元素
            for (size_t i = 0; i < _capacity; ++i)
            {
                if (_hashtable[i]._state == EXIST)
                    newHt.Insert(_hashtable[i]._key);
            }
            Swap(newHt);
        }
    }
    //线性探测
    void LineCheck(size_t& HashAdder)
    {
        ++HashAdder;
        if (HashAdder >= _capacity)
            HashAdder = 0;
    }
    //二次探测
    void SecondCheck(size_t HashAdder, size_t i)
    {
        HashAdder = HashAdder + ((i << 1) + 1);
        if (HashAdder >= _capacity)
            HashAdder %= _capacity;
    }
private:
    size_t Func(const K&key)
    {
        return KeyToInt()(key)%_capacity;
    }
private:
    Elem<K> * _hashtable;
    size_t _size;
    size_t _capacity;
};

有关闭散列的有关知识,大概就这么多了,希望大家一起进步!!!

只有不停的奔跑,才能不停留在原地!!!

为保证哈希节点更加散列,可以从以下几个方面入手: ### 设计精妙的哈希函数 哈希函数设计得越精妙,产生哈希冲突的可能性就越低。例如除法散列法(Division Method),通过取模运算将键转换为哈希值,公式为 `hash(key) = key % table_size` 。这种方法简单易实现,但容易产生聚集效应,可根据实际情况进行改进,如使用质数作为除数的除法散列法能在一定程度上减少聚集 [^1]。 ```python def hash_function(key, table_size): return key % table_size ``` ### 采用合适的处理冲突方法 - **散列**:是一种预先分配存储空间的方法,存储空间被划分为若干个固定大小的桶,每个桶中可以存储多个元素。当插入新元素时,根据其哈希值确定其所属的桶,并将元素添加到该桶中。如果发生哈希冲突,则将元素添加到下一个可用的桶中。不过,这种方法可能会导致某些桶被过度使用,而其他桶却是空闲的,空间利用率比较低 [^2]。 - **线性探测**:当哈希函数对一个元素产生一个值时,这个值会指向散列表中的某个单元。如果这个单元已经被另一个元素占用,就会发生冲突。此时会查找散列表中离冲突单元最近的空闲单元,并将新的元素插入到这个空闲单元中。当发生冲突时,算法会按照一定的步长(通常为1)逐个检查邻近的单元,直到找到一个空闲单元或者达到某个终止条件。其优点是实现简单,但一旦发生哈希冲突,所有的冲突连在一起,容易产生数据堆积 [^2]。 ```python class ClosedHashTable: def __init__(self, size): self.size = size self.table = [None] * size def hash_function(self, key): return key % self.size def insert(self, key): index = self.hash_function(key) while self.table[index] is not None: index = (index + 1) % self.size self.table[index] = key ``` - **开散列**:哈希表由多个桶组成,每个桶是一个链表。当不同的关键字通过哈希函数计算得到相同的哈希值(即发生哈希冲突)时,这些关键字对应的元素会被插入到同一个桶所对应的链表中。这种方法可以避免散列可能出现的空间利用率低的问题 [^2]。 ```python class OpenHashTable: def __init__(self, size): self.size = size self.table = [[] for _ in range(size)] def hash_function(self, key): return key % self.size def insert(self, key): index = self.hash_function(key) self.table[index].append(key) ``` ### 合理调整哈希表容量 当哈希表中的元素数量达到一定阈值时,进行扩容操作,以降低哈希冲突的概率,提高哈希节点的散列程度。例如在开散列中,当元素个数刚好等于桶的个数时,可以给哈希表增容 [^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值