HashMap

目录

 

1. HashMap要点

2. Hash冲突的解决

2.1 开放定址法

2.2 链地址法

2.3 再哈希法

2.4 建立公共溢出区

2.5 开放散列法和封闭散列法的优缺点


1. HashMap要点

1) hashMap利用指针数组来存储键值对,键由key通过hash操作获得

2) 通过链来存储多个hash值相同的数据

3) 初始时需要确定size,即桶数,将hash值对size进行求余操作即得到存储位置

hashmap.h (实现简单的增、删、插入操作)

#ifndef HASHMAP_H_
#define HASHMAP_H_

template<class Key, class Value>
class  HashNode
{
private:
    /* data */
public:
    Key _key;
    Value _value;
    HashNode *next;

    HashNode(Key key, Value value){
        _key = key;
        _value = value;
        next = NULL;
    }

    ~HashNode(){};

    HashNode& operator = (const HashNode& node){
        _key = node._key;
        _value = node._value;
        next = node.next;
        return *this;
    }

};

template <class Key, class Value, class HashFun, class EqualKey>
class  HashMap
{
public:
    HashMap(int size);
    ~HashMap();
    bool insert(const Key& key, const Value& value);
    bool del(const Key& key);
    Value& find(const Key& key);
    Value& operator[] (const Key& key);

private:
    HashFun hash;
    EqualKey equal;
    HashNode<Key, Value> **table;
    unsigned int _size;
    Value ValueNULL;


};

template <class Key, class Value, class HashFun, class EqualKey>
HashMap<Key, Value, HashFun, EqualKey>::HashMap(int size):_size(size),hash(),equal()
{
    table = new HashNode<Key, Value>*[_size];
    for (size_t i = 0; i < _size; i++)
    {
        table[i] = NULL;
    }
    
}

template <class Key, class Value, class HashFun, class EqualKey>
HashMap<Key, Value, HashFun, EqualKey>::~HashMap()
{
    for (size_t i = 0; i < _size; i++)
    {
        HashNode<Key, Value> *currentNode = table[i];
        while (currentNode)
        {
            HashNode<Key, Value> *temp = currentNode;
            currentNode = currentNode->next;
            delete temp;
        }
        
    }
    delete table;
}

template <class Key, class Value, class HashFun, class EqualKey>
bool HashMap<Key, Value, HashFun, EqualKey>::insert(const Key& key, const Value& value)
{
    int index = hash(key)%_size;
    HashNode<Key, Value>* node = new HashNode<Key, Value>(key, value);
    node->next = table[index];
    table[index] = node;
    
    return true;
}

template <class Key, class Value, class HashFun, class EqualKey>
bool HashMap<Key, Value, HashFun, EqualKey>::del(const Key& key)
{
    int index = hash(key)%_size;
    HashNode<Key, Value> *node = table[index];
    HashNode<Key, Value> *pre = NULL;
    while (node)
    {
        if (node->_key == key)
        {
            if (pre == NULL)
            {
                table[index] = node->next;
            }else
            {
                pre->next = node->next;
                /* code */
            }
            delete node;
            return true;
            
        }
        pre = node;
        node = node->next;
        
    }
    
}

template <class Key, class Value, class HashFun, class EqualKey>
Value& HashMap<Key, Value, HashFun, EqualKey>::find(const Key& key)
{
    int index = hash(key)%_size;
    if (table[index] == NULL)
    {
        return ValueNULL;
    }else
    {
        HashNode<Key, Value> *node = table[index];
        while (node)
        {
            if (node->_key == key)
            {
                return node->_value;
            }
            node = node->next;
            
        }
        
    }
    
    
}

template <class Key, class Value, class HashFun, class EqualKey>
Value& HashMap<Key, Value, HashFun, EqualKey>::operator[](const Key& key)
{
    return find(key);
}






#endif // !HASHMAP_H_

测试代码

#include <string>
#include <iostream>
#include "hashMap.h"

using namespace std;

class HashFunc
{
public:
    int operator()(const string &key)
    {
        int hash = 0;
        for (size_t i = 0; i < key.length(); i++)
        {
            hash = (hash << 7 ^ key[i]);
        }        
        return 1;
    //return (hash & 0x7FFFFFFF);
        
    }
};

class EqualKey
{
public:
    bool operator()(const string& A, const string& B)
    {
        if(A.compare(B) == 0){
            return true;
        }else
        {
            return false;
        }

        return false;
        
    }
};

int main()
{
    HashMap<string,string,HashFunc,EqualKey> hashmap(100);

    hashmap.insert("hello","world");
    hashmap.insert("why","dream");
    hashmap.insert("hello","qican");
    hashmap.insert("nihao","china");

    std::cout<<"after insert"<<endl;
    cout<<hashmap.find("hello")<<endl;
    cout<<hashmap["nihao"]<<endl;

    if (hashmap.del("hello"))
    {
        cout<<"remove is ok"<<endl;
    }

    cout<<hashmap.find("hello")<<endl;

    hashmap["why" ] = "love";
    cout<<hashmap["why"]<<endl;
    

    return 0;
}





 

2. Hash冲突的解决

2.1 开放定址法

  • 简单线性再散列

当关键字key的哈希地址p=H(key)出现冲突时,依次向后查找,直到找到一个空闲的位置。

简单线性再散列
简单线性再散列
​​
  • 二次探测再散列

当关键字key的哈希地址p=H(key)出现冲突时,在表的左右进行跳跃式探测,比较灵活,规则为

 d_{i}=1^{2} \ , -1^{2} \ , 2^{2} \ , -2^{2} \ , k^{2} \ , -k^{2}

二次平方再散列
二次平方再散列
  • 伪随机探测再散列

首先生成伪随机码,如{2,3,5,9.....},当发生冲突时,将哈希地址加上伪随机码进行探测,直到找到空闲的地址。

2.2 链地址法

将所有哈希地址为i的相同元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元里,因而查找、插入和删除主要在同义词链中进行。链地址法适合经常进行插入和删除的情况。

2.3 再哈希法

构造多种不同的哈希函数,当出现冲突时,依次使用后面的哈希函数。

2.4 建立公共溢出区

将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素全部放入到溢出表中。

2.5 开放散列法和封闭散列法的优缺点

  • 开放散列/拉链法 (针对桶链结构)

优点

1)对于记录总数频繁可变的情况,处理的比较好,避免了动态调整的开销。

2)由于记录存储在节点中,而节点是动态分配的,不会造成内存的浪费。(另一种思路,没有存储数据的地方会造成内存的浪费)

3)删除记录时比较方便,直接通过指针操作即可。

缺点

1)存储的记录是随机存储在内存中的,查询记录时,哈希表的跳转回带来额外的时间开销,(相对于数组存储来说)

2)如果所有的key-value提前可知,可以认为创建一个不会产生冲突的完美hash函数,这样封闭散列比开放散列的性能更高。

3)由于使用指针,记录不容易序列化操作。

  • 封闭散列/开放定址法

优点

1)如果记录总数提前可知,可以创建完美hash函数,此时可以高效地处理数据。

2)记录更容易进行序列化操作

缺点

1)存储记录的数目不能超过桶数组的长度,如果超过就需要扩容,而扩容会导致某次操作的时间成本飙升,这在实时或者交互式应用中会是一个严重的缺陷。

2)使用探测序列,有可能其计算的成本过高,导致哈希表的处理性能降低。

3)由于记录存放在桶数组中,而桶数组必然存在空槽,但记录本身尺寸很大并且记录总数规模很大时,空槽占用的空间会导致明显的内存浪费。

4)删除记录时,比较麻烦。(数组存储,空槽设置删除标记或后面的记录往前补,需要额外的空间和操作)

3. 参考文献

1) https://www.cnblogs.com/myd620/p/6349552.html?utm_source=itdadao&utm_medium=referral (c++ 实现hashmap

2) https://www.cnblogs.com/novalist/p/6396410.html (hash冲突的四种办法

3) https://www.cnblogs.com/wuchaodzxx/p/7396599.html (解决hash冲突的三个方法

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值