数据结构简介:哈希表

数据结构简介

数据结构简介

哈希表(散列表)

参考网址

  1. https://blog.csdn.net/stpeace/article/details/81274233
  2. https://blog.csdn.net/qq_36221862/article/details/73488162
  3. https://blog.csdn.net/xyzbaihaiping/article/details/51607770

1. 定义

  • 散列表的实现常常叫作散列(hashing),散列是一种用于以常数平均时间执行插入,删除和查找的技术.但是,那些需要元素间任何序信息的树操作在这里将不会得到有效的支持.
  • 注意:表的大小(tablesize)一般是素数,这样产生冲突的时候会有更多的备选空间,更好的散列分布

2. 散列表解决冲突的方法

  • 拉链法
  1. hash(x)=x mod tablesize
  2. 装填因子a约等于1(tablesize应该为素数)
  3. 产生冲突时,元素应插入对应链表的前端,因为不仅方便,而且这个元素最有可能不久又被访问
  • 开放地址法(线性探测法、平方探测法)
  1. 线性探测法
    (1)hi(x) = ( hash(x) + f(i) ) mod tablesize,且f(0)=0;f(i)=i;
    (2)装填因子a必须要小于等于0.5
    (3)惰性删除
  2. 平方探测法
    (1)hi(x) = ( hash(x) + f(i) ) mod tablesize,且f(0)=0;f(i)=i^2;
    (2)装填因子a必须要小于等于0.5,并且tablesize必须为素数.因为这样的话,表至少有一半为空时,产生冲突时,平方探测法总能够插入成功
    (3)惰性删除
  • 双散列
    f(i)=i * hash2(x)
    hash2(x) = R -(x mod R),R为小于tablesize的素数
    hi(x) = (hash(x) + f(i)) mod tablesize = (hash(x) + i * hash2(x)) mod tablesize

3. 以最坏情形O(1)访问的散列表

  • 完美散列
  • 杜鹃散列(布谷鸟散列)
  1. 常用较小的填装因子(小于0.5不包括0.5)且多于两个散列函数实现
  2. 示例代码
  • 跳房子散列
  1. 尝试改进线性探测算法
  2. 定义:通过预先确定的、在计算机结构体系的基础上优化的常数,来为探测序列的最大长度定界.

示例代码

基于哈希表简单实现HashMap(拉链法)

代码仅供参考:github地址

#ifndef __HASH_MAP1_HPP__
#define __HASH_MAP1_HPP__

#include <iostream>
#include <string>
#include <memory>
#include <functional>
using namespace std;

namespace hashmap1{

//hash table 内部节点
template <typename T,typename U>
class Node{
public:
    Node(const T& key,const T& value){
        m_key = key;
        m_value = value;
    }
    ~Node(){
        std::cout<<"~Node()"<<std::endl;
    }
public:
    T m_key;
    U m_value;
    std::unique_ptr<Node<T,U>> pNode = nullptr;
};

//基于hash table 实现的HashMap 冲突解决使用拉链法
template <typename T,typename U>
class HashMap{
public:
    explicit HashMap(const unsigned int size){
        this->m_size = size;
        m_ppNode = std::make_unique<unique_ptr<Node<T,U>>[]>(m_size);
        for(unsigned int index=0;index<m_size;index++){
            m_ppNode[index].reset(nullptr);
        }
    }
public:
    void Insert(const T& key,const U& value){
        int index = std::hash<T>{}(key)%m_size; //std::hash C++ 17
        std::unique_ptr<Node<T,U>> newNode = std::make_unique<Node<T,U>>(key,value);
        if(m_ppNode[index] == nullptr){
            m_ppNode[index] = std::move(newNode);//内存是否被正确回收?待验证
            return ;
        }
        //map容器考虑去重
        Node<T,U>* node = m_ppNode[index].get();
        while(node->pNode!= nullptr){
            if(node->m_key == key){
                node->m_value = value;
                return;
            }
            node = node->pNode.get();
        }
        if(node->m_key == key){ //判断循环最后一个元素
            node->m_value = value;
            return ;
        }
        //没有重复的元素 采用尾插法
        node->pNode = std::move(newNode);
    }

    pair<U,bool> Find(const T&key){
        int index = std::hash<T>{}(key)%m_size;
        auto p = m_ppNode[index].get();
        while(p!= nullptr){
            if(p->m_key == key){
                return make_pair(p->m_value, true);
            }
            p = p->pNode.get();
        }
        return make_pair(U(), false);
    }
private:
    std::unique_ptr<unique_ptr<Node<T,U>>[]> m_ppNode = nullptr;//unique_ptr对象数组
    unsigned int m_size=0;
};

void TestHashMap1(){
    {
        HashMap<string,string> map(100);
        map.Insert("a","aaa");
        map.Insert("b","bbb");

        pair<string,bool> ret1 = map.Find("a");
        if(ret1.second){
            std::cout<<"find:value="<<ret1.first<<std::endl;
        } else{
            std::cout<<"not find"<<std::endl;
        }

        pair<string,bool> ret2 = map.Find("b");
        if(ret2.second){
            std::cout<<"find:value="<<ret2.first<<std::endl;
        } else{
            std::cout<<"not find"<<std::endl;
        }

        pair<string,bool> ret3 = map.Find("c");
        if(ret3.second){
            std::cout<<"find:value="<<ret3.first<<std::endl;
        } else{
            std::cout<<"not find"<<std::endl;
        }
    }
}

}
#endif
基于哈希表简单实现HashMap(线性探查法、二次探查法)

代码仅供参考:github地址

#ifndef __HASH_MAP2_HPP__
#define __HASH_MAP2_HPP__

#include <iostream>
#include <string>
#include <vector>
#include <cmath>

using namespace std;

namespace hashmap2{
enum State{
    EMPTY,
    DELETE,
    EXIST
};

//hash table 内部节点
template <typename K,typename V>
class HashNode{
public:
    HashNode():m_state(EMPTY){}
public:
    std::pair<K,V> m_kv;
    State m_state;
};

//基于hash table 实现的HashMap 冲突解决使用开放地址法
template <typename K,typename V,bool isLine = true> //模板偏特化
class HashMap{
public:
    explicit HashMap(const unsigned int size = 10):m_size(0){
        m_table.resize(GetNextPrime(size));
    }
public:
    bool Insert(const K& key,const V& value){
        CheckSize();
        unsigned int hashAddr = std::hash<K>{}(key)%m_table.size();//C++ 17
        unsigned int index = hashAddr;
        int offset = 1;
        while(m_table[index].m_state == EXIST){
            if(m_table[index].m_kv.first == key){
                return false;
            }
            if(isLine){
                index = DetectedLine(index);
            }else{
                index = DetectedSquare(index,offset);
            }
        }
        m_table[index].m_kv = std::make_pair(key,value);
        m_table[index].m_state = EXIST;
        m_size++;
        return true;
    }

    std::pair<HashNode<K,V>*,bool> Find(const K&key){
        unsigned int hashAddr = std::hash<K>{}(key)%m_table.size();//C++ 17
        unsigned int index = hashAddr;
        HashNode<K, V>& elem = m_table[index];
        if(elem.m_kv.first!=key){ //产生冲突了 继续寻找
            if(isLine){//线性查找
                while(true){
                    index = DetectedLine(index);
                    if(index == hashAddr){
                        return std::make_pair(&elem,false);
                    }
                    if(m_table[index].m_kv.first == key && m_table[index].m_state == EXIST){
                        return std::make_pair(&m_table[index], true);
                    }
                }
            }else{//平方查找
                int offset =1;
                while(m_table[index].m_state != EMPTY){ //EXIST和DELETE都是要查找的目标
                    index = DetectedSquare(index,offset);
                    if(m_table[index].m_kv.first == key && m_table[index].m_state == EXIST){
                        return std::make_pair(&m_table[index], true);
                    }
                }
            }
        }else{
            if(m_table[index].m_state == EXIST){
                return make_pair(&elem, true);
            }
        }

        return make_pair(&elem, false);
    }

    bool Remove(const K& key){
        auto ret = Find(key);
        if(ret.second){
            ret.first->m_state = DELETE;
            m_size--;
            return true;
        }
        return false;
    }

    inline unsigned int Size(){
        return m_size;
    }

private:
    unsigned int DetectedLine(unsigned int hashAddr){ //线性探查法
        hashAddr++;
        if(hashAddr == m_table.size()){
            hashAddr = 0;
        }
        return hashAddr;
    }

    unsigned int DetectedSquare(unsigned int hashAddr,int& offset){//平方探查法
        /* 平方消解函数: H(i) = H(i-1) + 2i-1
         *
         * H(i) = H0 + i^2
         * H(i-1) = H0 + (i-1)^2
         * H(i) = H(i-1) + 2*i - 1 = H(i-1) + i<<1 - 1
         * */
        hashAddr += offset;
        offset+=2;
        hashAddr = hashAddr % m_table.size() ;
        return hashAddr;
    }

    void CheckSize(){
        if((m_size/m_table.size())>0.5){ //当装载因子a大于0.5时,需要将vector扩容处理
            m_table.resize(GetNextPrime(m_size));

            HashMap<K,V,isLine> hm;
            for(auto it : m_table){
                if(it.m_state == EXIST){
                    hm.Insert(it.m_kv.first,it.m_kv.second);
                }
            }
            this->Swap(hm);
        }
    }

    void Swap(HashMap<K,V,isLine> hm){
        std::swap(m_size,hm.m_size);
        m_table.swap(hm.m_table);
    }

    unsigned int GetNextPrime(const unsigned int size){//使用素数表对齐做哈希表的容量,降低哈希冲突
        const int _PrimeSize = 28;
        static const unsigned long _PrimeList[_PrimeSize] ={
                52ul, 97ul, 193ul, 389ul, 769ul,
                1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
                49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
                1572869ul, 3145739ul, 6291469ul, 12582917ul, 24165843ul,
                50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
                1610612741ul, 3221225473ul, 4294967291ul
        };

        for(unsigned i = 0; i < _PrimeSize; ++i){
            if (_PrimeList[i] > size){
                return _PrimeList[i];
            }
        }
        return _PrimeList[_PrimeSize-1];
    }

private:
    std::vector<HashNode<K,V>> m_table; //hash table
    unsigned int m_size=0;//hash table store element number
};

void TestHashMap2(){
    {
        HashMap<int,int,false> map;
        map.Insert(25, 1);
        map.Insert(25, 2);
        map.Insert(14, 2);
        map.Insert(36, 3);
        map.Insert(49, 4);
        map.Insert(68, 5);
        map.Insert(57, 6);
        map.Insert(11, 7);
        map.Insert(37, 8);

        cout<<map.Size()<<endl;

        auto ret = map.Find(25);
        if(ret.second){
            std::cout<<"Find:key="<<ret.first->m_kv.first<<" value="<<ret.first->m_kv.second<<std::endl;
        }else{
            std::cout<<"not find"<<std::endl;
        }

        ret = map.Find(11);
        if(ret.second){
            std::cout<<"Find:key="<<ret.first->m_kv.first<<" value="<<ret.first->m_kv.second<<std::endl;
        }else{
            std::cout<<"not find"<<std::endl;
        }

        ret = map.Find(12);
        if(ret.second){
            std::cout<<"Find:key="<<ret.first->m_kv.first<<" value="<<ret.first->m_kv.second<<std::endl;
        }else{
            std::cout<<"not find:12"<<std::endl;
        }

        map.Remove(25);
        ret = map.Find(25);
        if(ret.second){
            std::cout<<"Find:key="<<ret.first->m_kv.first<<" value="<<ret.first->m_kv.second<<std::endl;
        }else{
            std::cout<<"not find:25"<<std::endl;
        }

        map.Remove(14);
        cout<<map.Size()<<endl;
    }
}

}

#endif
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值