【数据结构】哈希表

#pragma once
//
 开放定地址法/闭散列
//namespace OpenAddress
//{
//
//    enum status
//    {
//        Empty,
//        Exist,
//        Delete
//    };
//
//    template <class K, class V>
//    struct HashData
//    {
//        pair<K, V> _kv;
//
//        status _status=Empty ;//为了不影响删除元素后面的探测,这里的删除是一种伪删除法,用status记录当前存储位置的状态;
//
// 
//    };
//
//    template <class K, class V>
//    class HashTable
//    {
//    public:
//
//        bool Insert(const pair<K, V>& kv)
//        {
//            //去冗余
//            if (Find(kv.first))
//            {
//                return false;
//            }
//
//            //刚开始Tables为空 或 负载因子超过0.7就扩容;
//            if (Tables.size() == 0 || _n * 10 / Tables.size() >= 7)
//            {
//                size_t newsize = Tables.size() == 0 ? 10 : Tables.size() * 2;
//                if (newsize > 10)
//                {
//                    //构建一个新的哈希表对象
//                    HashTable<K, V> newht;
//                    //这个哈希表对象最初开始就扩容到原来的二倍;
//                    newht.Tables.resize(newsize);
//                    //遍历旧表映射到新表
//                    for (auto& data : Tables)
//                    {
//                        if (data._status == Exist)
//                        {
//                            newht.Insert(data._kv);
//                        }
//                    }
//
//                    //交换两个哈希表对象里面的底层结构Tables;
//                   // Tables.swap(newht.Tables);
//                    swap(Tables, newht.Tables);
//                }
//                //Tables.size()==0的情况,此时表内所有HashData都是默认构造的,状态自然为Empty;
//                else
//                {
//                    Tables.resize(newsize);
//                }
//
//            }
//            //注意hashi是用什么计算的,如果Tables的size改变了,那么很有可能直接找不到原本可以找到的HashData了
//            size_t hashi = kv.first % Tables.size();
//            size_t i = 1;
//            size_t index = hashi;
//
//            //int flag = 0;
//            while (Tables[index]._status == Exist)
//            {
//
//                index = hashi + i;
//                index %= Tables.size();//当index==Tables.size()时,index又会从0开始;
//                ++i;
//
//                /* if (flag == 7)
//                 {
//                     int s = 0;
//                 }*/
//            }
//
//            Tables[index]._kv = kv;
//            Tables[index]._status = Exist;
//            _n++;
//            return true;
//        }
//
//        HashData<K, V>* Find(const K& key)
//        {
//            if (Tables.size() == 0)
//                return nullptr;
//            size_t hashi = key % Tables.size();
//            size_t i = 1;
//            size_t index = hashi;
//
//
//            //在插入前删除其中一部分数据,负载因子会减减,所以如果再插入几个数据刚好插入到Empty,并不会扩容,
//            //那么再去调用find函数时除了Exist就是Delete,会陷入死循环;
//            while (Tables[index]._status != Empty)
//            {
//                if (Tables[index]._status != Delete && Tables[index]._kv.first == key)
//                {
//                    return &Tables[index];//返回这块的地址;
//                }
//
//                //index从hashi+1开始走;
//                index = hashi + i;
//                index %= Tables.size();
//                ++i;
//
//                //说明走了一圈,查找不到;
//                if (hashi == index)
//                    break;
//            }
//            return nullptr;
//        }
//
//        bool Erase(const K& key)
//        {
//            HashData<K, V>* ret = Find(key);
//            if (ret)
//            {
//                ret->_status = Delete;
//                --_n;//负载因子
//                return true;
//            }
//            else
//            {
//                return false;
//            }
//
//        }
//
//    private:
//        vector<HashData<K, V>> Tables;//自定义类型会默认构造;
//        size_t _n;//存储的数据个数(用来计算负载因子)
//        /*HashDate* Tables;
//        size_t _size;
//        size_t _capacity;*/
//    };
//
//
//    //测试1
//    void test_Hash_Table1()
//    {
//        int arr[] = { 3,33,2,13,5,12,102 };
//        HashTable<int, int> ht1;
//
//        for (auto e : arr)
//        {
//            ht1.Insert(make_pair(e, e));
//        }
//
//        ht1.Insert(make_pair(679, 679));
//    }
//}



//哈希桶 / 开散列
namespace HashBucket
{
    //每个节点包括一个指向下一个同样节点的指针,和一个类型,可能是pair键值对也可能是key;
    template<class T>
    struct HashNode
    {
        T _data;//有可能是pair,也有可能是单纯的key;
        HashNode<T>* _next;
        
        HashNode(const T& data)
            :_data(data)
            , _next(nullptr)
        {}
    };

    //配合string使用,这里特意使用仿函数来处理string不能取模的情况;
    template<class K>
    struct HashFunc
    {
        size_t operator()(const K& key)
        {
            //整形家族可以直接返回
            return key;
        }
    };

    //BKDR HASH(特化)
    template<>
    struct HashFunc<string>
    {
        size_t operator()(const string& str)
        {
            //把字符串中所有字符都加起来变成整型;
            size_t ret = 0;
            for (auto& ch : str)
            {
                ret += ch;
                ret *= 31;//防止哈希冲突;
            }
            return ret;
        }

    };

    //前置声明
    template<class K, class T, class KeyOfT, class Hash>
    class HashTable;

    //迭代器

    
    template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
    struct _HashIterator
    {
        typedef HashNode<T> Node; 
        typedef HashTable<K, T, KeyOfT, Hash> HT;
        typedef _HashIterator<K, T, KeyOfT, Hash> Self;
        
        
        
        Node* _node;
        HT* _ht;

        //构造迭代器需要节点指针和哈希表指针;
        _HashIterator(Node* node,HT* ht)
            :_node(node)
            ,_ht(ht)
        {}

       const T& operator* ()
        {
            return _node->_data;
        }

       T* operator->()
       {
           return &_node->_data;
       }

       bool operator!=(const Self& s)
       {
           return _node != s._node;//用两个迭代器里面的指针去比较;
       }

       Self& operator++()
       {
           if (_node->_next)
           {
               _node = _node->_next;
           }

       
           else
           {
               //找下一个不为空的桶

               //算出当前位置
               KeyOfT kot;
               Hash hash;

               //算出当前桶位置;
               size_t hashi = hash(kot(_node->_data)) % (_ht->_tables.size());
               ++hashi;
               while (hashi < _ht->_tables.size())
               {
                   if (_ht->_tables[hashi])
                   {
                       _node =_ht-> _tables[hashi];
                       return *this;
                   }
                   //直到找到不是空桶或者走完哈希表为止;
                   else
                   {
                       ++hashi;
                   }
               }

               //没有找到,把迭代器里面的指针置为空,然后返回这个迭代器;
               _node = nullptr;
               return *this;

           }
       }


    };

    //底层数据结构--哈希表
    template<class K,class T,class KeyOfT,class Hash=HashFunc<K>>
    class HashTable
    {
        typedef HashNode<T> Node;

        //为了让迭代器访问哈希表的私有成员变量,设置一个友元;
        template<class K, class T, class KeyOfT, class Hash>
        friend struct _HashIterator;
    public:
        //在哈希表里面重定义迭代器名称,便于操作;
        typedef _HashIterator<K, T, KeyOfT, Hash>  iterator;
        typedef _HashIterator<const K&, const T&, KeyOfT, Hash>  const_iterator;


        iterator begin()
        {
            Node* cur = nullptr;
            for (auto& e: _tables)
            {
                if (e)
                {
                    return iterator(e,this);
                }
            }
            return iterator(nullptr,this);
        }

        iterator end()
        {
            return iterator(nullptr,this);
        }

        ~HashTable()
        {
            for (auto& first:_tables)
            {
                if (first)
                {
                    Node* cur = first;
                    while (cur)
                    {
                        Node* tmp = cur;
                        cur = cur->_next;
                        delete tmp;
                    }
                }
                first = nullptr;
            }
        }

        //如果data是个键值对,那么仿函数KeyOfT就是从Unordered_map传过来的,就一定是返回键值对的first;
        //如果data是个单纯的值,说明仿函数KeyOfT就是从Unordered_set传过来的,就一定是直接返回该值;
       pair<iterator,bool> Insert(const T& data)
        {
           KeyOfT kot;
           iterator it=Find(kot(data));
           if (it != end())
           {
               //说明找到了

               //那么就返回一个pair,这个pair由迭代器和布尔值构成;
               return make_pair(it,false);
           }
     
          
            //负载因子等于1时扩容

            //即使刚开始_tables还没有初始化,这里的负载因子因为和_tables.size()相等都为0,所以也可以扩容;
            if (_n==_tables.size())
            {
                //以下这种较为麻烦;
                //size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
              /*  HashTable<K, V> newht;
                newht._tables.resize(newsize);

                for (auto& cur : _tables)
                {
                   while(cur)
                    {
                        newht.Insert(cur->_kv);
                        cur = cur->_next;
                    }
                }

                _tables.swap(newht._tables);*/

                size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
                
                //创建一个新tables,全部初始化为nullptr;
                vector<Node*> newtables(newsize,nullptr);

                KeyOfT kot;
                Hash hash;
                //遍历旧tables,把旧tables的数据挪过来;
                for (auto& cur:_tables)
                {
                    while (cur)
                    {
                        //记录下一个节点;
                        Node* next=cur->_next;
                        size_t hashi = hash( kot(cur->_data) ) % newtables.size();
                        
                        //头插
                       cur->_next=newtables[hashi];
                       newtables[hashi] = cur;
                       
                       //更新cur
                       cur = next;
                    }

                }

                //交换新表与旧表
                _tables.swap(newtables);
            }
           

            //正式插入
            size_t hashi = hash(kot(data)) % _tables.size();
            Node* newnode = new Node(data);

            newnode->_next = _tables[hashi];
            _tables[hashi] = newnode;
            _n++;
            
            //插入成功,返回键值对 pair<iterator,true>;
            return make_pair(iterator(newnode,this), true);
          
        }

       iterator Find(const K& key)
        {
            //刚开始顺序表为空,说明没有任何数据,直接返回nullptr;
            if (_tables.size() == 0)
                return end();

            KeyOfT kot;
            Hash hash;
            size_t hashi = hash(key) % _tables.size();
            Node* cur = _tables[hashi];
            while (cur)
            {
              if (hash(kot(cur->_data)) == hash(key))
                    return iterator(cur,this);
                cur = cur->_next;
            }
            return end();
        }

        bool Erase(const K& key)
        {
            if (_tables.size() == 0)
                return false;
            
            KeyOfT kot;
            Hash hash;
            //开始找
            size_t hashi = hash(key) % _tables.size();
            Node* cur = _tables[hashi];
            Node* prev =nullptr;

            while (cur)
            {
                if (hash(kot(cur->_data)) == hash(key))
                {
                    if (prev)
                    {
                        Node* next = cur->next;
                        prev->_next = next;
             
                    }
                    else//说明头结点就是
                    {
                        _tables[hashi] = cur->_next;         
                    }
                    delete cur;
                    return true;
                }
                prev = cur;
                cur = cur->_next;
            }
            return false;
        }


    private:
        vector<Node*> _tables;//指针数组;
        size_t _n=0;//存储数据的有效个数;
    };

   
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值