哈希表的代码实现以及性质

一.哈希表的介绍

      哈希表本质上是一种存储键值对的容器,但是和avl树或者是红黑树比较而言,哈希表的查找要更加直接和快速,我个人感觉有点像打靶一样,直接打入某个位置,如果那个位置已经有元素了,就去附近的位置,直到找到空白的地方,但是这个打靶也是存在着一定的规则,一般情况下都是采用哈希值取模的结果进行打靶。

直接来说它通过哈希函数将键映射到表中的一个位置,从而实现快速的数据插入、查找和删除操作。哈希表的核心思想是利用哈希函数将键的值转换为存储位置的索引,从而实现高效的访问。

在C++中unorder_map的底层就是采用了哈希表

二.哈希表的实现逻辑

    首先它是通过管理数组的形式来进行实现,所以肯定要含有vector,不过这个vector里面的类型是什么呢?第一,每一个元素它肯定是存键值对的,所以肯定含有pair<K,V>,然后因为对于每一个元素而言,我们需要知道它的状态,比如EXIST,EMPTY,DELETE,所以说就需要采用结构体和联合的结合来构成一个元素

enum STATE
{
    EXIST,
    DELETE,
    EMPTY
};
template<class K, class V>
class HashData
{
public:
    pair<K, V> kv;
    STATE state = EMPTY;
};

然后我们管理好vector以后,肯定还要知道这个哈希表中有多少元素,这个时候就需要有一个int n来记录元素的数量了,这就是哈希表的成员变量、

class HashTable
{
private:
    vector<HashData<K, V>> data;
    int n;

然后就是实现构造函数,我一般实现构造函数就是给data开辟空间和将n进行初始化,将n初始化为0,然后将data开辟10空间 

HashTable()
{
    data.resize(10);
    n = 0;
}

然后写插入函数,对于哈希表的插入而言,第一步肯定是判断哈希表里面是否含有这个元素,如果含有,而插入失败,然后就是将这个kay转化成哈希值了,这里转化成哈希值其实并不是很简单,因为要涉及到很多的数据类型,所以说要让外界传递一个哈希函数,但是对于一些int,或者String的类型来说,可以直接生成对应的哈希指,我们也要构造两个默认的哈希函数,第一个哈希函数就比较简单,直接是将key强转为int类型,然后返回,而对于String类型来说,就是需要采用一定的算法来获得哈希指了,不过这里重要的是要利用将类强制定义为String的形式,就可以使用String了

template<class K>
struct defafunc
{
    int operator()(const K&val)
    {
        return (int)val;
    }
};
template<>
struct defafunc< std::string>
{
    int operator()( const std::string& str)
    {
        int hash = 0;
        for (auto e : str)
        {
            hash *=  131;
            hash += e;
        }
        return hash;
    }
};

这个比较重要,得到哈希值以后,就将哈希值模上数组长度,得到位置,然后再判断该位置是否还存在其他元素,如果有,就将哈希值加加,但是为了防止越界,每次加完以后进行模上数组长度,直到找到空位,放入pair,然后将位置的状态置为存在

HashFunc func;
int hash = func(val.first);

hash = hash % data.size();
cout << hash << endl;
while (data[hash].state == EXIST)
{
    hash++;
    hash = hash % data.size();
}
data[hash].kv = val;
data[hash].state = EXIST;
n++;
return true;

当然,实现上面的前提是看数组是否需要扩容,一般情况下当存在的元素处于size大于0.7时就属于过多,此时就需要扩容,扩容的思路就是先拷贝实例化一个当前大小二倍的哈希表,然后将现在存在的元素一个个插入新的表里面,然后将两个哈希表的容器交换,这样也就实现了扩容

     if (n * 10 / data.size() > 7)
    {
        int newsize = data.size() * 2;
        HashTable<K, V> copy;
        copy.data.resize(newsize);
        for (auto e : data)
        {
            if(e.state == EXIST)
            copy.insert(e.kv);
        }
        copy.data.swap(data);
    }

然后是查找,查找一般是给一个key,然后返回这个元素的指针,如果不存在这个元素时,就返回nullptr,查找一般是利用刚刚的规则,先利用哈希函数获得哈希值,然后将哈希值进行判断,得到对应的位置,最后是看看直到为EMPTY时能不能找到对应的key,找不到则返回nullptr,找到则返回对应的元素

HashData<K, V>* find(const K& key)
{
    HashFunc func;
    int hash = func(key);
    if (isEmpty())
    {
        return nullptr;
    }
    hash = hash % data.size();
    
    while (data[hash].state != EMPTY)
    {
        if (data[hash].kv.first == key && data[hash].state == EXIST)
            return &data[hash];
        hash++;
        hash = hash % data.size();
    }
    return nullptr;
}

删除最简单,先从find里面找到位置,然后将位置状态置为DELETE就行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值