哈希桶的完善+封装成unordered set和map

目录

进一步实现哈希桶

引入

keyofValue

迭代器

insert返回值

operator[ ]

key不能修改

模拟实现 

keyofValue

代码

迭代器

谁在前

普通迭代器转换为const迭代器

const *this 问题

代码

insert和erase

const迭代器转换为普通迭代器

key不能修改

完整版代码 

unordered set

代码

unordered map

思路

operator[ ]

erase

代码


进一步实现哈希桶

引入

之前实现的哈希桶,仅仅只是其中的一部分

keyofValue

  • 首先,为了和set,map适配,需要一个keyofValue的模板(用来处理set和map不同类型的元素)
  • set和map的元素类型,一个K,一个pair<K,V>,但我们有些接口都需要K类型的,所以为了map,set就陪着它一起用一个函数拿到K (K是模板,传入啥类型都行,参考红黑树的set和map)
  • (之后就简称了哈,其实ordered系列和红黑树版本的set,map区别真不大,封装出来的接口都是一样的,只不过底层实现不同而已)

迭代器

  • 其次,非常重要的迭代器需要实现,很多接口返回的都是迭代器,接受的也是迭代器
  • 所以,这里就会出现const迭代器和普通迭代器相互转换的问题!!! 后面细说
  • (全是因为set!!(咬牙切齿),它的迭代器实际上都是const类型,但调用hash的insert返回的迭代器又是普通类型的,就很难搞)

insert返回值

  • 我们之前的insert返回的是结点指针,但为了和stl保持一致,需要返回pair<iterator,bool>,bool用于标记是否完成插入

operator[ ]

  • map中有[ ]操作
  • 就是传入K类型,如果存在的话,返回对应的value;不存在就需要插入该key,value使用默认初始化

key不能修改

  • 千万不要忘了,set和map中的key值都是不能修改的!!!

模拟实现 

keyofValue

不需要在hash中实现,只需要在模板中加入即可

不同的类,让他自己传入相应的keyofValue就行

代码
template <class K, class T, class KeyOfValue, class HF>
class HashBucket;

set:

template <class K>
    class unordered_set
    {
        struct KeyOfV
        {
            const K& operator()(const K& data)
            {
                return data;
            }
        };
    }

map:

template <class K, class V>
    class unordered_map
    {
        // 通过key获取value的操作
        struct KeyOfValue
        {
            const K& operator()(const pair<K, V>& data)
            {
                return data.first;
            }
        };
    }

迭代器

观察一下哈希桶的结构,想象迭代器在上面走的样子

  • 因为我们可能需要知道下一条链的位置,所以,需要一个哈希桶的对象,那么,可以直接使用*this来获得哈希桶的资源(现成的指针,不用白不用)
谁在前
  • 但是这样就会导致一个问题 -- 哈希表需要用迭代器,但迭代器也需要用哈希表,到底谁在前呢?
  • 所以,我们给迭代器类一个哈希桶的前置声明,让迭代器先生成出来,这样就能生成哈希桶了,反过来又继续生成迭代器

  • 除此之外,还有最让人头疼的问题,const迭代器
  • 比如,在之前红黑树就出现过的将普通迭代器赋值给const迭代器的问题
普通迭代器转换为const迭代器
  • 这是红黑树中的(本质完全是一样的):
  • 所以,需要在迭代器的构造函数中,增加一个函数:
const *this 问题

接下来,还是set引发的问题:

  • 因为两种迭代器都是封装的const迭代器,所以只需要提供const类型的begin和end即可
  • 然后,set里的函数去调用了哈希桶里const版本的begin/end:
  • 但是!!!!迭代器的构造函数是直接传入对象指针的:
  • 所以需要将接收哈希桶对象指针的参数改为底层const类型
  • 但是,又会出现使用底层const的指针去初始化一个普通指针(一般来说,不知道会出现这些问题的话,是不会加const的)
  • 所以,最终,我们要把这个对象指针改成底层const类型的(改了后不会影响,因为迭代器内部不会修改哈希桶)
代码
template <class K, class T, class KeyOfValue, class HF>
    class HashBucket; // 前置声明,因为迭代器中要用到哈希桶,而哈希桶也要用到迭代器
                      // 但迭代器肯定是先的那个,所以给迭代器一个前置声明,让他可以先使用哈希桶(声明不需要给默认值)

    template <class K, class T, class Ptr, class Ref, class KeyOfValue, class HF>
    struct HBIterator
    {
        typedef HashBucket<K, T, KeyOfValue, HF> HB;
        typedef HashBucketNode<T>* PNode;
        typedef HBIterator<K, T, Ptr, Ref, KeyOfValue, HF> Self; // 自己,用于返回

        typedef HBIterator<K, T, T*, T&, KeyOfValue, HF> iterator; //用于解决set中插入的问题

        KeyOfValue kot;

        HBIterator(PNode pNode = nullptr, const HB* pHt = nullptr)  //因为会出现传入的指针是底层const类型的情况,所以这里也改
            : _pnode(pNode), _pHt(pHt) // 需要结点指针+哈希桶对象的指针
        {
        }
        HBIterator(const iterator& it)
            : _pnode(it._pnode), _pHt(it._pHt) // 需要结点指针+哈希桶对象的指针
            //虽然增加了构造,但会导致,用普通对象构造
        {
        }
        Self& operator++()
        {
            // 当前迭代器所指节点后还有节点时直接取其下一个节点
            if (_pnode->_next)
            {
                _pnode = _pnode->_next;
            }

            else
            {
                // 找下一个不空的桶,返回该桶中第一个节点

                size_t hashi = _pHt->HashFunc(kot(_pnode->_data)) + 1;
                _pnode = nullptr; // 这里提前赋值,以防;没有下一个桶后,可以返回指向空的迭代器
                for (; hashi < _pHt->BucketCount(); ++hashi)
                {
                    if (_pnode = _pHt->_table[hashi]) // 注意,这里用到了哈希桶的私密成员,所以需要让迭代器成为哈希桶的友元
                    {                                 // 好妙
                        break;
                    }
                    // if (_pHt->_ht[hashi])
                    // {
                    //     _pnode = _pHt->_ht[hashi];
                    //     break;
                    // }
                }
            }

            return *this;
        }
        Self operator++(int)
        {
            Self tmp(*this);
            ++(*this);
            return tmp;
        }
        Ref operator*()
        {
            return _pnode->_data;
        }
        Ptr operator->()
        {
            return &_pnode->_data;
        }
        bool operator==(const Self& it) const
        {
            return _pnode == it._pnode;
        }
        bool operator!=(const Self& it) const
        {
            return _pnode != it._pnode;
        }

        PNode _pnode; // 当前迭代器关联的节点(也就是迭代器的本质)
        const HB* _pHt;     // 哈希桶--为了找下一个位置
        //因为,会出现传入的指针是底层const类型的情况,所以这里可以直接定义为底层const类型的指针,因为迭代器内部不修改哈希桶
    };

insert和erase

insert的问题主要在于它的返回值,把返回值一改就行

其中存在的普通迭代器转const迭代器已经在上面解决了

但是!!!

由于有这种接口,接收const迭代器,返回普通迭代器,所以需要在erase内部转化一下

const迭代器转换为普通迭代器

其实很简单,将定义出的一个初始的const迭代器,++到要转化的普通迭代器的位置就行

            auto it = const_iterator(begin());
            int count = 0;
            while (it != cur) {
                ++it;
                ++count;
            }
            auto a = iterator(begin());
            for (int i = 0; i < count; ++i) {
                ++a;
            }
            return a;

key不能修改

只要将元素类型中的K,传入const K类型就行

完整版代码 

#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <type_traits>
using namespace std;

// 哈希桶
namespace my_hash_bucket
{
    // hsfunc
    // 用于拿到数据对应的整型值->然后可以得到对应的hashi
    template <class T>
    class HSFunc
    {
    public:
        size_t operator()(const T& val)
        {
            return val;
        }
    };
    template <>
    class HSFunc<string>
    {
    public:
        size_t operator()(const string& s)
        {
            int size = s.size();
            unsigned int seed = 131; // 31 131 1313 13131 131313 都可以
            unsigned int hashi = 0;
            for (size_t i = 0; i < size; ++i)
            {
                hashi = hashi * seed + s[i];
            }
            return hashi;
        }
    };

    // 结点
    template <class T>
    struct HashBucketNode // 每个位置下链接的结点
    {
        HashBucketNode(const T& data)
            : _next(nullptr), _data(data)
        {
        }
        HashBucketNode<T>* _next;
        T _data;
    };

    // 迭代器
    template <class K, class T, class KeyOfValue, class HF>
    class HashBucket; // 前置声明,因为迭代器中要用到哈希桶,而哈希桶也要用到迭代器
                      // 但迭代器肯定是先的那个,所以给迭代器一个前置声明,让他可以先使用哈希桶(声明不需要给默认值)

    template <class K, class T, class Ptr, class Ref, class KeyOfValue, class HF>
    struct HBIterator
    {
        typedef HashBucket<K, T, KeyOfValue, HF> HB;
        typedef HashBucketNode<T>* PNode;
        typedef HBIterator<K, T, Ptr, Ref, KeyOfValue, HF> Self; // 自己,用于返回

        typedef HBIterator<K, T, T*, T&, KeyOfValue, HF> iterator; //用于解决set中插入的问题

        KeyOfValue kot;

        HBIterator(PNode pNode = nullptr, const HB* pHt = nullptr)  //因为会出现传入的指针是底层const类型的情况,所以这里也改
            : _pnode(pNode), _pHt(pHt) // 需要结点指针+哈希桶对象的指针
        {
        }
        HBIterator(const iterator& it)
            : _pnode(it._pnode), _pHt(it._pHt) // 需要结点指针+哈希桶对象的指针
        {
        }
        Self& operator++()
        {
            // 当前迭代器所指节点后还有节点时直接取其下一个节点
            if (_pnode->_next)
            {
                _pnode = _pnode->_next;
            }

            else
            {
                // 找下一个不空的桶,返回该桶中第一个节点

                size_t hashi = _pHt->HashFunc(kot(_pnode->_data)) + 1;
                _pnode = nullptr; // 这里提前赋值,以防;没有下一个桶后,可以返回指向空的迭代器
                for (; hashi < _pHt->BucketCount(); ++hashi)
                {
                    if (_pnode = _pHt->_table[hashi]) // 注意,这里用到了哈希桶的私密成员,所以需要让迭代器成为哈希桶的友元
                    {                                 // 好妙
                        break;
                    }
                    // if (_pHt->_ht[hashi])
                    // {
                    //     _pnode = _pHt->_ht[hashi];
                    //     break;
                    // }
                }
            }

            return *this;
        }
        Self operator++(int)
        {
            Self tmp(*this);
            ++(*this);
            return tmp;
        }
        Ref operator*()
        {
            return _pnode->_data;
        }
        Ptr operator->()
        {
            return &_pnode->_data;
        }
        bool operator==(const Self& it) const
        {
            return _pnode == it._pnode;
        }
        bool operator!=(const Self& it) const
        {
            return _pnode != it._pnode;
        }

        PNode _pnode; // 当前迭代器关联的节点(也就是迭代器的本质)
        const HB* _pHt;     // 哈希桶--为了找下一个位置
        //因为,会出现传入的指针是底层const类型的情况,所以这里可以直接定义为底层const类型的指针,因为迭代器内部不修改哈希桶
    };

    // 哈希桶
    //  这里的key是唯一的
    template <class K, class T, class KeyOfValue, class HF = HSFunc<K>>
    // KeyOfValue用于不同类型的元素,返回key
    // HF用于不同类型的key,返回整型
    class HashBucket
    {
        template <class K, class T, class Ptr, class Ref, class KeyOfValue, class HF>
        friend struct HBIterator;

    public:
        typedef HashBucketNode<T> Node;
        typedef Node* PNode;

        typedef HashBucket<K, T, KeyOfValue, HF> Self;

        typedef HBIterator<K, T, T*, T&, KeyOfValue, HF> iterator;
        typedef HBIterator<K, T, const T*, const T&, KeyOfValue, HF> const_iterator;

    public:
        HashBucket(size_t capacity = 5)
            : _size(0)
        {
            _table.resize(capacity, nullptr);
        }

        ~HashBucket()
        {
            Clear();
        }

        iterator begin()
        {
            for (size_t i = 0; i < _table.size(); ++i) // 遍历数组,找到第一个桶
            {
                if (_table[i])
                {
                    return iterator(_table[i], this);
                }
            }
            return iterator(nullptr, this); // 很妙,this指针就是所需要的哈希桶对象的指针
        }
        iterator end()
        {
            return iterator(nullptr, this);
        }
        const_iterator begin() const
        {
            for (size_t i = 0; i < _table.size(); ++i) // 遍历数组,找到第一个桶
            {
                if (_table[i])
                {
                    return iterator(_table[i], this);
                }
            }
            return const_iterator(nullptr, this); // 很妙,this指针就是所需要的哈希桶对象的指针
        }
        const_iterator end() const
        {
            return const_iterator(nullptr, this); //这里的const,修饰的是*this,所以哈希桶对象是const的
                    //但是,迭代器的构造,是直接将传入的迭代器指针初始化,所以就会出现"const指针初始化普通指针"的问题
        }

        // 哈希桶中的元素不能重复
        pair<iterator, bool> Insert(const T& data)
        {
            KeyOfValue kot;
            auto it = Find(kot(data));
            if (it != end())
            {
                return make_pair(it, false);
            }
            if (CheckCapacity()) // 需要扩容了
            {
                size_t newsize = _size * 2;
                Self newhsb(newsize);

                for (size_t i = 0; i < _size; ++i)
                {
                    PNode cur = _table[i];
                    while (cur) // 把桶上的结点挂在新位置
                    {
                        PNode next = cur->_next;
                        size_t hashi = newhsb.HashFunc(kot(cur->_data));

                        cur->_next = newhsb._table[hashi];
                        newhsb._table[hashi] = cur;

                        cur = next;
                        ++newhsb._size;
                    }
                    _table[i] = nullptr;
                }
                Swap(newhsb);
            }
            PNode newnode = new Node(data);
            size_t hashi = HashFunc(kot(data));
            // 头插
            newnode->_next = _table[hashi];
            _table[hashi] = newnode;
            ++_size;
            return make_pair(newnode, true);
        }

        // 删除哈希桶中为data的元素(data不会重复)
        iterator Erase(const_iterator del)
        {
            KeyOfValue kot;
            if (del == end()) {
                return end();
            }
            if (del != Find(kot(*del)))  // 说明这个迭代器不正确
            {
                return end();
            }

            auto cur = const_iterator(del);
            ++cur;
            size_t hashi = HashFunc(kot(*del));
            PNode prev = _table[hashi];
            if (prev == del._pnode) // 如果删除的是第一个结点
            {
                _table[hashi] = del._pnode->_next;
            }
            else
            {
                while (prev && prev->_next->_data != *del) // 找到上一个结点
                {
                    prev = prev->_next;
                }
                prev->_next = del._pnode->_next;
            }
            --_size;
            auto it = const_iterator(begin());
            int count = 0;
            while (it != cur) {
                ++it;
                ++count;
            }
            auto a = iterator(begin());
            for (int i = 0; i < count; ++i) {
                ++a;
            }
            return a;
        }

        iterator Find(const K& data) const
        {
            KeyOfValue kot;
            size_t hashi = HashFunc(data);
            PNode cur = _table[hashi];
            while (cur)
            {
                if (kot(cur->_data) == data)
                {
                    return iterator(cur);
                }
                cur = cur->_next;
            }
            return iterator(nullptr);
        }

        size_t Size() const
        {
            return _size;
        }

        bool Empty() const
        {
            return 0 == _size;
        }

        void Print() const
        {
            for (size_t i = 0; i < _table.size(); ++i)
            {
                PNode cur = _table[i];
                printf("[%d]:", i);
                while (cur)
                {
                    PNode next = cur->_next;
                    cout << cur->_data << " ";
                    cur = next;
                }
                cout << endl;
            }
            cout << endl;
        }

        void Clear()
        {
            for (size_t i = 0; i < _table.size(); ++i)
            {
                PNode cur = _table[i];
                while (cur)
                {
                    PNode next = cur->_next;
                    delete cur;
                    cur = next;
                }
                _table[i] = nullptr;
            }
        }

        size_t BucketCount() const
        {
            return _table.size();
        }

        void Swap(Self& ht)
        {
            _table.swap(ht._table);
            swap(_size, ht._size);
        }

    private:
        size_t HashFunc(const K& data) const// 将数据转换成hashi
        {
            HF hf;
            return hf(data) % _table.size();
        }

        bool CheckCapacity() const
        {
            if (_size == _table.size())
            {
                return true;
            }
            else
            {
                return false;
            }
        }

    private:
        vector<PNode> _table;
        size_t _size; // 哈希表中有效元素的个数
    };
}

unordered set

这里的话,只要在哈希桶的迭代器类里,加入普通迭代器构造const迭代器之后,基本没啥问题了

代码

#include "hash.hpp"

namespace my_unordered_set
{
    template <class K>
    class unordered_set
    {
        struct KeyOfV
        {
            const K& operator()(const K& data)
            {
                return data;
            }
        };

    public:
        typedef my_hash_bucket::HashBucket<K, const K, KeyOfV> HT; // 使用哈希桶封装无序set
        typedef typename HT::const_iterator iterator;
        typedef typename HT::const_iterator const_iterator;

    public:
        unordered_set() : _ht()
        {
        }
        iterator begin() const
        {
            return _ht.begin(); //这里const修饰的是*this,那么哈希桶对象是const的
        }
        iterator end() const
        {
            return _ht.end();
        }

        // capacity
        size_t size() const
        {
            return _ht.size();
        }
        bool empty() const
        {
            return _ht.empty();
        }

        // lookup
        iterator find(const K& key)
        {
            return _ht.Find(key);
        }
        size_t count(const K& key)
        {
            return _ht.Count(key);
        }

        // modify
        pair<iterator, bool> insert(const K& value)
        {
            //这里和红黑树一样,都存在普通迭代器转化为const迭代器的问题
            //所以,和之前一样,要为迭代器增加一个构造函数
            auto it = _ht.Insert(value);
            return make_pair(it.first, it.second);
        }

        iterator erase(iterator position) //接收的是哈希桶的const迭代器
        {
            auto it = _ht.Erase(position); //返回的是普通迭代器
            return it;
        }

        // bucket
        size_t bucket_count()
        {
            return _ht.BucketCount();
        }
        size_t bucket_size(const K& key)
        {
            return _ht.BucketSize(key);
        }

    private:
        HT _ht;
    };
}

unordered map

思路

operator[ ]

因为[ ]是能实现当key不存在时,自动构造的

所以,需要借助insert

最终返回insert返回值中的value引用就行

erase

erase中存在的const迭代器转为普通的在上面已经处理过了,map中就不需要处理了

代码

#include "hash.hpp"

namespace my_unordered_map
{
    template <class K, class V>
    class unordered_map
    {
        // 通过key获取value的操作
        struct KeyOfValue
        {
            const K& operator()(const pair<K, V>& data)
            {
                return data.first;
            }
        };

    public:
        typedef my_hash_bucket::HashBucket<K, pair<const K, V>, KeyOfValue> HT;
        typedef typename HT::iterator iterator;
        typedef typename HT::const_iterator const_iterator;

    public:
        unordered_map() : _ht()
        {
        }

        iterator begin() {
            return _ht.begin();
        }
        iterator end() { 
            return _ht.end();
        }
        const_iterator begin() const {
            return _ht.begin();
        }
        const_iterator end() const{
            return _ht.end();
        }

        // capacity
        size_t size() const {
            return _ht.size();
        }
        bool empty() const { 
            return _ht.empty();
        }

        // Acess
        V& operator[](const K& key)
        {
            pair<iterator, bool> ret = _ht.Insert(pair<K, V>(key, V()));
            return ret.first->second;
        }
        const V& operator[](const K& key) const {
            pair<iterator, bool> ret = _ht.Insert(pair<K, V>(key, V()));
            return (ret.first)->second;
        }

        // lookup

        iterator find(const K& key) {
            return _ht.Find(key);
        }
        size_t count(const K& key) {
            return _ht.Count(key); 
        }

        // modify
        pair<iterator, bool> insert(const pair<K, V>& valye)
        {
            return _ht.Insert(valye);
        }

        iterator erase(const_iterator position)
        {
            return _ht.Erase(position);
        }

        // bucket
        size_t bucket_count() { 
            return _ht.BucketCount();
        }
        size_t bucket_size(const K& key) { 
            return _ht.BucketSize(key); 
        }

    private:
        HT _ht;
    };
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值