使用拉链法实现哈希

开散列

  • 开散列又叫链地址法,首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合成为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头节点存储在哈希表中。

请添加图片描述

​ 通过结构的分析我们不难看出使用开散列比开放定址法的好处在于减少了因哈希冲突而导致的额外寻找,在某中意义上实现了存储位置与关键字的真正对应,但是开散列仍然需要扩容,也就是还需要负载因子,因为可能很多数据映射在同一个桶中导致一个链表过长,扩容的目的可以降低各个桶的长度,从而增加查找速度

  • 代码实现
//使用开散列来实现hash
namespace openlist{
    template<typename K, typename V>
    struct HashNode{
        HashNode(const pair<K, V>& data)
        :_data(data),
        _next(nullptr)
        {}

        pair<K, V> _data;
        shared_ptr<HashNode> _next;
    };

    template<typename K, typename V, typename hash = HashFunc<K>>
    class Hash{
    private:
        using Node = HashNode<K,V>;
    public:
        Hash():_n(0) { _table.resize(10); }
        bool insert(const pair<K, V>& kv);
        Node* find(const K& key); 
        bool erase(const K& key);
    private:
        vector<shared_ptr<Node>> _table;
        size_t _n = 0;              //记录插入的个数,可以用来计算平衡因子
    };
    template<typename K, typename V, typename hash>
    HashNode<K,V>* Hash<K, V, hash>::find(const K& key){
        hash hs;
        size_t sz = _table.size(), hashi = hs(key) % sz; 
        //当这个桶执行的元素历遍完了,如果还是没有找到就证明没有这个元素
        shared_ptr<Node> cur = _table[hashi];
        while(cur){
            if(cur->_data.first == key) return HashIterator(this, cur);
            else cur = (cur->_next);
        }
        return HashIterator(this, nullptr);
    }

    //这里扩容时有一个效率的问题,就是我们是像开放定址法一样新创建一个_table
    //然后逐渐进行插入吗,如果是这样的化,那么效率太低,所以可以重复利用原来创建好的指针
    template<typename K, typename V, typename hash>
    bool Hash<K,V,hash>::insert(const pair<K, V>& kv){
        HashIterator node = find(kv.first);
        if(node) ;
        hash hs;
        size_t old_sz = _table.size();
        //当桶中元素的数量和桶的数量相同时进行扩容操作
        if(_n == old_sz){
            size_t new_sz = old_sz*2;
            vector<shared_ptr<Node>> new_table;
            new_table.resize(new_sz);
            for(int i = 0; i < old_sz; ++i){
                //unique<Node>
                auto cur = _table[i];
                while(cur){
                    size_t hashi = hs(cur->_data.first) % new_sz;
                    auto next = (cur->_next);
                    shared_ptr<Node> ptr = new_table[hashi];
                    cur->_next = ptr;
                    new_table[hashi] = move(cur);
                    cur = next;
                }
            }
            _table.swap(new_table);
        } 
        //插入新节点
        size_t hashi = hs(kv.first) % _table.size();
        shared_ptr<Node> old_node = _table[hashi];
        shared_ptr<Node> new_node = make_shared<Node>(kv);
        new_node->_next = move(old_node);
        _table[hashi] = move(new_node);
        _n++;
    }

    template<typename K, typename V, typename hash>
    bool Hash<K,V,hash>::erase(const K& key){
        hash hs;
        size_t sz = _table.size(), hashi = hs(key)%sz;
        shared_ptr<Node> cur = _table[hashi];
        shared_ptr<Node> prev;
        while(cur){
            if(cur->_data.first == key){
                if(!prev) {
                    _table[hashi] = move(cur->_next);
                }
                else{
                    prev->_next = move(cur->_next);
                }
                return true;
            }
            prev = cur;
            cur = cur->_next;
        } 
        return false;
    }
}
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值