目录
进一步实现哈希桶
引入
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;
    };
}


























