哈希桶的实现(拉链法)

简介

为了解决线性探测实现哈希表时出现哈希冲突后导致数据堆聚的现象,我们采取了另一种结构来处理哈希冲突,哈希桶(拉链法),拉链法解决冲突的做法是将所有通过哈希函数计算出来的哈希地址相同的结点存放在一个单链表之中。
实现原理就是将哈希表定义为一个由N个头指针组成的指针数组,经过哈希函数计算得到的哈希地址相同的数据全部连在对于的头指针下面。它继承了数组的易于查找和链表便于删除插入的特点。

拉链法

时间复杂度

拉链法因为加入了链表,所以在查找删除的时候时间复杂度也退化为了O(N),但是如果在哈希函数给的合理,并且哈希表的容量足够的时候,拉链法的时间复杂度肯定是远远小于O(N)。

拉链法的优缺点

优点
解决了哈希表堆聚现象,减小了平均查找长度。删除结点相对于线性探测更加易于实现,只需要找到相应结点再删除就可以了,而开放地址法中不能这样做,因为在哈希表中置空数组中的结点会导致后面的数据无法访问。

缺陷
当数据量比较小的时候,开放地址法不用开辟空间,如果相对于拉链法节省的结点空间用来扩大散列表则可以使装填因子(结点数与表长的比值),这样也就减少了开放地址法的冲突,提高平均查找速度。

代码实现

代码在整体框架上的设计和线性探测基本一致,只是在插入删除的操作方式上有了区别,不是使用更改状态来表示删除,而是采用了链表的删除和插入操作。因为采取了链表和vector结合的做法,所以在结点设计上删去了_status(状态),增加了一个指向下一个结点的指针_next。

template<class K,class V>
struct HashTableBucketNode
{
    K _key;
    V _value;

    HashTableBucketNode<K, V>* _next;

    HashTableBucketNode(const K& key, const V& value)
        :_key(key)
        , _value(value)
        , _next(NULL)
    {}
};

template<class K>
struct _HashFunc
{//用来计算key的仿函数
    size_t operator()(const K& key)
    {
        return key;
    }
};

template<>
struct _HashFunc<string>
{//特化string版本

    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);
    }

    size_t operator()(const string& key)
    {
        return BKDRHash(key.c_str());
    }
};

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
};


template<class K,class V,class HashFunC = _HashFunc<K>>
class HashTableBucket
{
    typedef HashTableBucketNode<K,V> Node;
public:
    HashTableBucket(size_t size)
        :_size(0)
    {
        size_t newsize = GetPrime(size);
        _v.resize(newsize);
    }

    size_t HashFunc(const K& key)
    {//哈希函数
        HashFunC hs;
        size_t hash = hs(key);
        return hash % _v.size();
    }

    pair<Node*, bool> Insert(const K& key, const V& value)
    {//插入
        CheckCapacity();
        size_t index = HashFunc(key);

        if (!_v[index])
        {//头指针为空
            Node* newNode = new Node(key, value);
            _v[index] = newNode;
            ++_size;
            return make_pair(newNode, true);
        }

        //在链表中查找插入位置
        Node* prev = NULL;
        Node* cur = _v[index];
        while (cur)
        {
            if (cur->_key == key)
                return make_pair(cur, false);

            prev = cur;
            cur = cur->_next;
        }
        Node* newNode =new Node(key, value);
        prev->_next = newNode;
        ++_size;
        return make_pair(newNode, true);
    }

    //Node* Find(const K& key)
    //{//查找的第一种实现,直接按位置查找
    //  size_t index = HashFunc(key);

    //  Node* cur = _v[index];
    //  while (cur)
    //  {
    //      if (cur->_key == key)
    //          return cur;

    //      cur = cur->_next;
    //  }

    //  return NULL;
    //}

    Node* Find(const K& key)
    {//遍历查找
        for (size_t index = 0; index < _size; ++index)
        {
            Node* cur = _v[index];
            while (cur)
            {
                if (cur->_key = key)
                    return cur;

                cur = cur->_next;
            }
        }

        return NULL;
    }

    bool Remove(const K& key)
    {//删除
        size_t index = HashFunc(key);

        Node* prev = NULL;
        Node* cur = _v[index];

        while (cur)
        {
            if (cur->_key == key)
            {
                if (prev == NULL)
                    _v[index] = NULL;   
                else
                    prev->_next = cur->_next;

                delete cur;
                return true;
            }
        }
        return false;
    }


protected:

    size_t GetPrime(size_t size)
    {//获得素数表素数
        for (size_t i = 0; i < _PrimeSize; ++i)
        {
            if (_PrimeList[i] > size)
            {
                return _PrimeList[i];
            }

        }
        return _PrimeList[_PrimeSize - 1];
    }

    void Swap(HashTableBucket<K, V> tmp)
    {//交换
        std::swap(_v, tmp._v);
        std::swap(_size, tmp._size);
    }

    void CheckCapacity()
    {//容量检测
        if (_size == _v.size())
        {
            HashTableBucket<K, V> tmp(_v.size());

            for (size_t index = 0; index < _size; ++_size)
            {
                Node* cur = _v[index];
                while (cur)
                {
                    Node* cur = _v[index];
                    while (cur)
                    {
                        tmp.Insert(cur->_key, cur->_value);
                        cur = cur->_next;
                    }
                }
            }
            Swap(tmp);
        }
    }
private:
    vector<Node*> _v;
    size_t _size;
};
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值