0


哈希结构的实现

unordered系列关联式容器

哈希的简介

哈希表闭散列实现

哈希表开散列实现

用哈希表来封装map和set

位图

布隆过滤器与哈希切割

全部代码


unordered系列关联式容器

unordered系列关联式容器有四种,****这里主要对unordered_map和unordered_set这两种容器进行介绍

unordered_map

unordered_map是存储<key, value>键值对的关联式容器,其允许通过keys快速的索引到与其对应的value

在内部,unordered_map没有对<kye, value>按照任何特定的顺序排序

unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低

unordered_maps实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value

unordered_set

unordered_set和unordered_map在性质上差不多,因为底层结构是一致的,都是哈希结构,区别在与,unordered_set不支持[],因为它存储的是key与value相同,所以无需支持[]

哈希的简介

概念

如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素

哈希函数有多种,这里采用除留余数法

哈希冲突

比如14和4取模后都是4,而4对应的位置只能存储一个数字,那么就出现了哈希冲突,而解决哈希冲突的常见两种方法有闭散列和开散列

哈希表闭散列实现

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去

寻找下一空位置有线性探测和二次探测两种

线性探测

从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止

** 二次探测**

从发生冲突的位置开始,跨越探测,直到寻找到下一个空位置为止

index是哈希表的下标

首先因为有闭散列和开散列两种,所以采用命名空间的形式来防止命名冲突

然后定义一个枚举类型,用来判断该下标的空间的状态,是存在数据,还是删除过数据,或者没有数据,便于插入,删除,及查找数据

封装节点,开始必然为每个下标所在的空间必然为空状态,所以初始化为空状态

因为要是除留取余法,必然要是整数,所以还需要借助仿函数来实现

内置类型

string类型

string类型转整形,可以将字符串的每个字母的ASCII码值加起来,但要是只是顺序不同,那总数也会相同,出现哈希冲突,为了尽量减少哈希冲突,就有了下图的方法,其中31是累乘因子

string类型有下列两种写法,任选其一即可

**对于其它自定义类型,比如日期,则按照特定方式取整即可 **

哈希表同样采用模板的形式实现,另外私有成员有中的_n是有效数据个数,便于后续插入数据扩容使用

查找数据

如果表为空,则直接返回nullptr即可

首先用待查找的key模表的长度取模作为待查找的初始下标,然后一直往后查找,同时往后走时,最后别忘了再取模一次,因为可能会存在初始下标空间的前面空间,找不到时,则直接返回nullptr

删除数据

首先先查找数据,找到数据了有效数据就减少一个,然后将那个节点的状态置为删除状态,删除成功返回true即可,没找到就表示删除失败,返回false

插入数据

首先先查找数据,如果存在,就表示待插入的数据已经有了,所以无需再插入,返回false即可

根据有效数据个数,得到负载因子,如果负载因子大于等于0.7则表示需要扩容了,初始没有空间时,也一样需要扩容

负载因子越小,冲突概率越低,效率越高,空间浪费越多

负载因子越大,冲突概率越高,效率越低,空间浪费越少

如果是初始时则开10个空间,其它情况则直接开原来的2倍空间

创建一个新表,开辟新空间大小的空间,再将其数据拷贝到新表,然后将新表对象中的vector成员与this对象中的vector成员交换即可

如果不需要开辟新空间

则先将key取模,找到对应的下标,如果有空间,则直接插入数据,没有,则继续往后面找,与find()类似,最后有效数据+1,返回true即可

闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷

哈希表开散列实现

概念

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中,开散列中每个桶中放的都是发生哈希冲突的元素,如下图

封装节点,其实就是单链表的节点封装

私有成员与闭散列一致,同时,还是采用除留去余法,所以会用到仿函数,与上面闭散列一致

查找数据

如果哈希表为空,则直接返回nullptr

找到待查找的数据节点的下标,以及声明一个局部变量来保存这条链的头节点

从头开始往后找,找到了返回节点地址,没找到返回nullptr

删除数据

如果哈希表为空,返回false,表示删除失败

找到待删除数据节点的那条链的头节点

如果找到了,则分为两种情况,如果就是头节点,那就直接头删,否则就中间删除

如果没找到,则继续往后面找,找不到则返回false,表示删除失败

** 插入数据**

首先先查找数据,如果存在,就表示待插入的数据已经有了,所以无需再插入,返回false即可

如果负载因子等于1时,则需要扩容,初始开空间时开10个空间,其余情况开原来的2倍空间

然后将原表的数据按头插拷贝到新表,将原表每条链的表头置空,最后再交换this对象的vector成员与新表的vector成员即可

如果不需要扩容,则直接头插,然后将有效数据个数+1,再返回true即可

为了减少哈希冲突,每次扩容后的空间大小都为素数

用哈希表来封装map和set

改善哈希表

因为既要封装map,又要封装set,所以将数据类型从pair类型变为了T类型

迭代器

**因为会用到哈希表,所以提前声明一下 **

因为要封装map和set,所以多加了一个模板参数KeyOfT,采用了仿函数

因为map和set存储的数据不同,一个是pair<key,value>,一个是key所以对*和对->都要重载

判断相不相等,比较的是节点的地址相不相等

重载++运算符

如果这条链上还有节点,即没有遇到nullptr,则直接往后走即可

如果为nullptr,则从哈希表的下一个元素开始找,比如下图,走完11后,则继续往后面走,下一个元素表头为空,所以就走到了13,不为nullptr,则跳出循环

如果是循环自动结束的,即走完整个哈希表了,则直接置nullptr即可

如果是break出来的,则直接返回到那个节点,比如上述所说的13

拷贝构造

别忘了写一个默认构造函数,因为有了拷贝构造之后,编译器不会默认生成了

这里采用的是头插法,所以顺序会有变化

重载赋值运算符

将实参传给形参时会有一次拷贝构造,所以只需将this对象的两个成员与形参的交换即可

析构函数

从哈希表第一个元素开始销毁节点,直到走完哈希表结束

初始节点

如果哈希表第一个元素的头节点不为nullptr,则它就是初始节点,否则直接返回nullptr的迭代器

结尾节点,即nullptr

重载[]运算符

[]可查找value,也可修改或插入

位图

位图的作用:数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在,比如在一堆数据中找5在不在,数据很多时,可以开辟整形最大值那么大的以bit为单位的空间,然后一个整数对应一个比特位,如果5对应的比特位是1,那就在这堆数据中,是0就不在

1byte=8bit,所以开空间数如下图

将某个bit位置为1

将某个bit位置为0

检查某个bit位是否为1

0为假,非0为真

位图:节省空间,效率高,但只能处理整数

布隆过滤器与哈希切割

概念

布隆过滤器是由布隆在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。既可以提升查询效率,也可以节省大量的内存空间

作用:用来解决字符串,自定义类型对象在不在的问题

如果采用将字符串转整数,即将字符串的各个字符加起来取模作为下标,那就可能会存在哈希冲突,比如下图中eat和ate

这种方式,判断不在是准确的,比如eat和ate那个位置的比特位为0,则eat肯定不在,只是判断在会存在误判

解决方式

将一个元素用多个哈希函数映射到一个位图中,比如ate,只要三个种有一个bit位为0,则就表示ate不在,降低了误判率

这里以哈希函数中三个不同的哈希函数为例模板参数

//hashTable-副本
#pragma once
#include<vector>

template<class K>
struct Hash
{
    size_t operator()(const K& key)
    {
        return key;
    }
};

//特化
template<>
struct Hash< string >
{
    size_t operator()(const string& s)
    {
        //BKDR
        size_t value = 0;
        for (auto ch : s)
        {
            value *= 31;
            value += ch;
        }
        return value;
    }
};

namespace CloseHash
{
    enum Status
    {
        EXIST,
        EMPTY,
        DELETE
    };

    template<class K,class V>
    struct HashData
    {
        pair<K, V> _kv;
        Status _status = EMPTY;
    };

    struct HashStr
    {
        size_t operator()(const string& s)
        {
            //BKDR
            size_t value = 0;
            for (auto ch : s)
            {
                value *= 31;
                value += ch;
            }
            return value;
        }
    };

    template<class K,class V,class HashFunc = Hash<K>>
    class HashTable
    {
    public:
        bool Erase(const K& key)
        {
            HashData<K, V>* ret = Find(key);
            if (ret == nullptr)
            {
                return false;
            }
            else
            {
                --_n;
                ret->_status = DELETE;
                return true;
            }
        }

        HashData<K, V>* Find(const K& key)
        {
            if (_tables.size() == 0)
            {
                return nullptr;
            }

            HashFunc hf;
            size_t start = hf(key) % _tables.size();
            size_t i = 0;
            size_t index = start;
            //线性探测 or 二次探测
            while (_tables[index]._status != EMPTY)
            {
                if (_tables[index]._kv.first == key && _tables[index]._status == EXIST)
                {
                    return &_tables[index];
                }

                ++i;
                //index = start + i*i;
                index = start + i;

                index %= _tables.size();
            }

            return nullptr;
        }

        bool Insert(const pair<K, V>& kv)
        {
            HashData<K, V>* ret = Find(kv.first);
            if (ret)
            {
                return false;
            }

            //负载因子到0.7,就扩容
            //负载因子越小,冲突概率越低,效率越高,空间浪费越多
            //负载因子越大,冲突概率越高,效率越低,空间浪费越少
            if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
            {
                //扩容
                size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
                //vector<HashData<K,V>> newTables;
                //newTables.resize(newSize);
                //遍历原表,把原表中的数据,重新按newSize映射到新表
                //for(size_t i = 0;i < _tables.size();++i)
                //{
                    //
                //}
                //_tables.swap(newTables);
                HashTable<K, V, HashFunc> newHT;
                newHT._tables.resize(newSize);
                for (size_t i = 0; i < _tables.size(); ++i)
                {
                    if (_tables[i]._status == EXIST)
                    {
                        newHT.Insert(_tables[i]._kv);
                    }
                }

                _tables.swap(newHT._tables);
            }

            HashFunc hf;
            size_t start = hf(kv.first) % _tables.size();
            size_t i = 0;
            size_t index = start;
            //线性探测 or 二次探测
            while (_tables[index]._status == EXIST)
            {
                ++i;
                //index = start + i * i;
                index = start + i;

                index %= _tables.size();
            }

            _tables[index]._kv = kv;
            _tables[index]._status = EXIST;
            ++_n;

            return true;
        }
    private:
        vector<HashData<K, V>> _tables;
        size_t _n = 0;//有效数据个数
    };

    void TestHashTable1()
    {
        //HashTable<int,int,Hash<int>> ht;
        HashTable<int, int> ht;

        int a[] = { 2,12,22,32,42,52,62 };
        for (auto e : a)
        {
            ht.Insert(make_pair(e, e));
        }

        ht.Insert(make_pair(72, 72));
        ht.Insert(make_pair(72, 72));
        ht.Insert(make_pair(-1, -1));
        ht.Insert(make_pair(-999, -999));

        Hash<int> hs;
        cout << hs(9) << endl;
        cout << hs(-9) << endl;

        cout << ht.Find(12) << endl;
        ht.Erase(12);
        cout << ht.Find(12) << endl;
    }

    struct Date
    {

    };

    struct HashDate
    {
        size_t operator()(const Date& d)
        {
            //...
        }
    };

    void TestHashTable2()
    {
        HashStr hs;
        cout << hs("sort") << endl;
        cout << hs("insert") << endl;
        cout << hs("eat") << endl;
        cout << hs("ate") << endl;
        cout << hs("abcd") << endl;
        cout << hs("aadd") << endl;

        HashTable<string, string> ht;
        ht.Insert(make_pair("sort", "排序"));
        ht.Insert(make_pair("string", "字符串"));

        //当key是一个定义类型时,需要配置一个仿函数,将key转成整形
        HashTable<Date, string, HashDate> htds;
    }
}

namespace LinkHash
{
    template<class K,class V>
    struct HashNode
    {
        pair<K, V> _kv;
        HashNode<K, V>* _next;

        HashNode(const pair<K, V>& kv):_kv(kv),_next(nullptr)
        {}
    };

    template<class K,class V,class HashFunc = Hash<K>>
    class HashTable
    {
        typedef HashNode<K, V> Node;
    public:

        bool Erase(const K& key)
        {
            if (_tables.empty())
            {
                return false;
            }

            HashFunc hf;
            size_t index = hf(key) % _tables.size();
            Node* prev = nullptr;
            Node* cur = _tables[index];
            while (cur)
            {
                if (cur->_kv.first == key)
                {
                    if (prev == nullptr) //头删
                    {
                        _tables[index] = cur->_next;
                    }
                    else //中间删除
                    {
                        prev->_next = cur->_next;
                    }

                    --_n;

                    delete cur;

                    return true;
                }
                else
                {
                    prev = cur;
                    cur = cur->_next;
                }
            }

            return false;
        }

        Node* Find(const K& key)
        {
            if (_tables.empty())
            {
                return nullptr;
            }

            HashFunc hf;
            size_t index = hf(key) % _tables.size();
            Node* cur = _tables[index];
            while (cur)
            {
                if (cur->_kv.first == key)
                {
                    return cur;
                }
                else
                {
                    cur = cur->_next;
                }
            }

            return nullptr;
        }

        bool Insert(const pair<K, V>& kv)
        {
            Node* ret = Find(kv.first);
            if (ret)
                return false;

            HashFunc hf;
            if (_n == _tables.size())
            {
                size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
                //...
                vector<Node*> newTables;
                newTables.resize(newSize);
                for (size_t i = 0; i < _tables.size(); ++i)
                {
                    Node* cur = _tables[i];
                    while (cur)
                    {
                        Node* next = cur->_next;

                        size_t index = hf(cur->_kv.first) % newTables.size();
                        //头插
                        cur->_next = newTables[index];
                        newTables[index] = cur;

                        cur = next;
                    }

                    _tables[i] = nullptr;
                }

                _tables.swap(newTables);
            }
            size_t index = hf(kv.first) % _tables.size();
            Node* newnode = new Node(kv);
            //头插
            newnode->_next = _tables[index];
            _tables[index] = newnode;

            ++_n;
            return true;
        }

    private:
        /*struct Data
        {
            forward_list<T> _list;
            set<T> _rbtree;
            size_t len;
        };
        vector<Data> _table;*/
        vector<Node*> _tables;

        size_t _n = 0;//有效数据的个数
    };

    void TestHashTable()
    {
        int a[] = { 4,24,14,7,37,27,57,67,34,14,54 };
        HashTable<int, int> ht;
        for (auto e : a)
        {
            ht.Insert(make_pair(e, e));
        }

        ht.Insert(make_pair(84, 84));
    }
};

//hashTable
#pragma once
#include<vector>
#include<string>

template<class K>
struct Hash
{
    size_t operator()(const K& key)
    {
        return key;
    }
};

//特化
template<>
struct Hash< string >
{
    size_t operator()(const string& s)
    {
        //BKDR
        size_t value = 0;
        for (auto ch : s)
        {
            value *= 31;
            value += ch;
        }
        return value;
    }
};

namespace CloseHash
{
    enum Status
    {
        EXIST,
        EMPTY,
        DELETE
    };

    template<class K, class V>
    struct HashData
    {
        pair<K, V> _kv;
        Status _status = EMPTY;
    };

    //struct HashStr
    //{
    //    size_t operator()(const string& s)
    //    {
    //        //BKDR
    //        size_t value = 0;
    //        for (auto ch : s)
    //        {
    //            value *= 31;
    //            value += ch;
    //        }
    //        return value;
    //    }
    //};

    template<class K, class V, class HashFunc = Hash<K>>
    class HashTable
    {
    public:
        bool Erase(const K& key)
        {
            HashData<K, V>* ret = Find(key);
            if (ret == nullptr)
            {
                return false;
            }
            else
            {
                --_n;
                ret->_status = DELETE;
                return true;
            }
        }

        HashData<K, V>* Find(const K& key)
        {
            if (_tables.size() == 0)
            {
                return nullptr;
            }

            HashFunc hf;
            size_t start = hf(key) % _tables.size();
            size_t i = 0;
            size_t index = start;
            //线性探测 or 二次探测
            while (_tables[index]._status != EMPTY)
            {
                if (_tables[index]._kv.first == key && _tables[index]._status == EXIST)
                {
                    return &_tables[index];
                }

                ++i;
                //index = start + i*i;
                index = start + i;

                index %= _tables.size();
            }

            return nullptr;
        }

        bool Insert(const pair<K, V>& kv)
        {
            HashData<K, V>* ret = Find(kv.first);
            if (ret)
            {
                return false;
            }

            //负载因子到0.7,就扩容
            //负载因子越小,冲突概率越低,效率越高,空间浪费越多
            //负载因子越大,冲突概率越高,效率越低,空间浪费越少
            if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
            {
                //扩容
                size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
                //vector<HashData<K,V>> newTables;
                //newTables.resize(newSize);
                //遍历原表,把原表中的数据,重新按newSize映射到新表
                //for(size_t i = 0;i < _tables.size();++i)
                //{
                    //
                //}
                //_tables.swap(newTables);
                HashTable<K, V, HashFunc> newHT;
                newHT._tables.resize(newSize);
                for (size_t i = 0; i < _tables.size(); ++i)
                {
                    if (_tables[i]._status == EXIST)
                    {
                        newHT.Insert(_tables[i]._kv);
                    }
                }

                _tables.swap(newHT._tables);
            }

            HashFunc hf;
            size_t start = hf(kv.first) % _tables.size();
            size_t i = 0;
            size_t index = start;
            //线性探测 or 二次探测
            while (_tables[index]._status == EXIST)
            {
                ++i;
                //index = start + i * i;
                index = start + i;

                index %= _tables.size();
            }

            _tables[index]._kv = kv;
            _tables[index]._status = EXIST;
            ++_n;

            return true;
        }
    private:
        vector<HashData<K, V>> _tables;
        size_t _n = 0;//有效数据个数
    };

    void TestHashTable1()
    {
        //HashTable<int,int,Hash<int>> ht;
        HashTable<int, int> ht;

        int a[] = { 2,12,22,32,42,52,62 };
        for (auto e : a)
        {
            ht.Insert(make_pair(e, e));
        }

        ht.Insert(make_pair(72, 72));
        ht.Insert(make_pair(72, 72));
        ht.Insert(make_pair(-1, -1));
        ht.Insert(make_pair(-999, -999));

        Hash<int> hs;
        cout << hs(9) << endl;
        cout << hs(-9) << endl;

        cout << ht.Find(12) << endl;
        ht.Erase(12);
        cout << ht.Find(12) << endl;
    }

    struct Date
    {

    };

    struct HashDate
    {
        size_t operator()(const Date& d)
        {
            //...
        }
    };

    void TestHashTable2()
    {
        /*HashStr hs;
        cout << hs("sort") << endl;
        cout << hs("insert") << endl;
        cout << hs("eat") << endl;
        cout << hs("ate") << endl;
        cout << hs("abcd") << endl;
        cout << hs("aadd") << endl;*/

        HashTable<string, string> ht;
        ht.Insert(make_pair("sort", "排序"));
        ht.Insert(make_pair("string", "字符串"));

        //当key是一个定义类型时,需要配置一个仿函数,将key转成整形
        HashTable<Date, string, HashDate> htds;
    }
}

namespace LinkHash
{
    template<class T>
    struct HashNode
    {
        T _data;
        HashNode<T>* _next;

        HashNode(const T& data) :_data(data), _next(nullptr)
        {}
    };

    template<class K, class T,class KeyOfT, class HashFunc>
    class HashTable;

    template<class K,class T,class Ref,class Ptr,class KeyOfT,class HashFunc>
    struct __HTIterator
    {
        typedef HashNode<T> Node;
        typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, HashFunc> Self;

        Node* _node;
        HashTable<K, T, KeyOfT, HashFunc>* _pht;

        __HTIterator(Node* node,HashTable<K,T,KeyOfT,HashFunc>* pht):_node(node),_pht(pht)
        {}

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

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

        Self& operator++()
        {
            if (_node->_next)
            {
                _node = _node->_next;
            }
            else
            {
                KeyOfT kot;
                HashFunc hf;
                size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
                ++index;
                //找下一个不为空的值
                while (index < _pht->_tables.size())
                {
                    if (_pht->_tables[index])
                    {
                        break;
                    }
                    else
                    {
                        ++index;
                    }
                }

                //表走完了,都没有找到下一个桶
                if (index == _pht->_tables.size())
                {
                    _node = nullptr;
                }
                else
                {
                    _node = _pht->_tables[index];
                }
            }
            return *this;
        }

        bool operator!=(const Self& s) const
        {
            return _node != s._node;
        }

        bool operator==(const Self& s) const
        {
            return _node = s._node;
        }
    };

    template<class K,class T,class KeyOfT,class HashFunc>
    class HashTable
    {
        typedef HashNode<T> Node;
        
        template<class K,class T,class Ref,class Ptr,class KeyOfT,class HashFunc>
        friend struct __HTIterator;
        typedef HashTable<K, T, KeyOfT, HashFunc> self;
    public:
        typedef __HTIterator<K, T, T&, T*, KeyOfT, HashFunc> iterator;

        HashTable()
        {}
        //显示指定编译器去生成默认构造函数
        //HashTable() = default;

        HashTable(const self& ht)
        {
            _tables.resize(ht._tables.size());
            for (size_t i = 0; i < ht._tables.size(); ++i)
            {
                Node* cur = ht._tables[i];
                while (cur)
                {
                    Node* copy = new Node(cur->_data);
                    copy->_next = _tables[i];
                    _tables[i] = copy;

                    cur = cur->_next;
                }
            }
        }

        //ht1 = ht2
        self& operator=(self copy)
        {
            swap(_n, copy._n);
            _tables.swap(copy._tables);
            return *this;
        }

        ~HashTable()
        {
            for (size_t i = 0; i < _tables.size(); ++i)
            {
                Node* cur = _tables[i];
                while (cur)
                {
                    Node* next = cur->_next;
                    delete cur;
                    cur = next;
                }
                _tables[i] = nullptr;
            }
        }

        iterator begin()
        {
            for (size_t i = 0; i < _tables.size(); ++i)
            {
                if (_tables[i])
                {
                    return iterator(_tables[i], this);
                }
            }

            return end();
        }

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

        bool Erase(const K& key)
        {
            if (_tables.empty())
            {
                return false;
            }

            HashFunc hf;
            size_t index = hf(key) % _tables.size();
            Node* prev = nullptr;
            Node* cur = _tables[index];
            KeyOfT kot;
            while (cur)
            {
                if (kot(cur->_data) == key)
                {
                    if (prev == nullptr) //头删
                    {
                        _tables[index] = cur->_next;
                    }
                    else //中间删除
                    {
                        prev->_next = cur->_next;
                    }

                    --_n;

                    delete cur;

                    return true;
                }
                else
                {
                    prev = cur;
                    cur = cur->_next;
                }
            }

            return false;
        }

        iterator Find(const K& key)
        {
            if (_tables.empty())
            {
                return end();
            }

            HashFunc hf;
            size_t index = hf(key) % _tables.size();
            Node* cur = _tables[index];
            KeyOfT kot;
            while (cur)
            {
                if (kot(cur->_data) == key)
                {
                    return iterator(cur,this);
                }
                else
                {
                    cur = cur->_next;
                }
            }

            return end();
        }

        size_t GetNextPrime(size_t num)
        {
            static const unsigned long __stl_prime_list[28] =
            {
                53,97,193,389,769,
                1543,3079,6151,12289,24593,
                49157,98317,196613,393241,786433,
                1572869,3145739,6291469,12582917,25165843,
                50331653,100663319,201326611,402653189,805306457,
                1610612741,3221225473,4294967291
            };

            for(size_t i = 0;i < 28;++i)
            {
                if (__stl_prime_list[i] > num)
                {
                    return __stl_prime_list[i];
                }
            }
            return __stl_prime_list[27];
        }

        pair<iterator,bool> Insert(const T& data)
        {
            KeyOfT kot;
            iterator ret = Find(kot(data));
            if (ret != end())
                return make_pair(ret,false);

            HashFunc hf;

            //负载因子 == 1时扩容
            if (_n == _tables.size())
            {
                //size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
                //...
                size_t newSize = GetNextPrime(_tables.size());
                /*if (newSize == _tables.size())
                {
                    break;
                }*/
                vector<Node*> newTables;
                newTables.resize(newSize);
                for (size_t i = 0; i < _tables.size(); ++i)
                {
                    Node* cur = _tables[i];
                    while (cur)
                    {
                        Node* next = cur->_next;

                        size_t index = hf(kot(cur->_data)) % newTables.size();
                        //头插
                        cur->_next = newTables[index];
                        newTables[index] = cur;

                        cur = next;
                    }

                    _tables[i] = nullptr;
                }

                _tables.swap(newTables);
            }
            size_t index = hf(kot(data)) % _tables.size();
            Node* newnode = new Node(data);
            //头插
            newnode->_next = _tables[index];
            _tables[index] = newnode;

            ++_n;
            return make_pair(iterator(newnode,this),false);
        }

    private:
        /*struct Data
        {
            forward_list<T> _list;
            set<T> _rbtree;
            size_t len;
        };
        vector<Data> _table;*/
        vector<Node*> _tables;

        size_t _n = 0;//有效数据的个数
    };
}

//unordered-map
#pragma once
#include"HashTable.h"

namespace bit
{
    template<class K,class V,class hash = Hash<K>>
    class unordered_map
    {
        struct MapKeyOfT
        {
            const K& operator()(const pair<K, V>& kv)
            {
                return kv.first;
            }
        };
    public:
        typedef typename LinkHash::HashTable<K, pair<K, V>, MapKeyOfT, hash>::iterator iterator;
        iterator begin()
        {
            return _ht.begin();
        }

        iterator end()
        {
            return _ht.end();
        }

        V& operator[](const K& key)
        {
            auto ret = _ht.Insert(make_pair(key, V()));
            return ret.first->second;
        }

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

    private:
        LinkHash::HashTable<K, pair<K,V>, MapKeyOfT, hash > _ht;
    };

    void test_unordered_map()
    {
        unordered_map<string, string> dict;
        dict.insert(make_pair("sort", "排序"));
        dict.insert(make_pair("string", "字符串"));
        dict.insert(make_pair("map", "地图、映射"));
        /*cout << dict["sort"] << endl;
        cout << dict["string"] << endl;
        cout << dict["map"] << endl;*/

        //dict["right"] = "右边";

        unordered_map<string, string>::iterator it = dict.begin();
        while (it != dict.end())
        {
            cout << it->first << ":" << it->second << endl;
            ++it;
        }
        cout << endl;

        unordered_map<string, string> copy(dict);
        for (auto& kv : copy)
        {
            cout << kv.first << ":" << kv.second << endl;
        }
        cout << endl;
    }
}

//unordered-set
#pragma once
namespace bit
{
    template<class K,class hash = Hash<K>>
    class unordered_set
    {
        struct SetKeyOfT
        {
            const K& operator()(const K& key)
            {
                return key;
            }
        };
    public:
        typedef typename LinkHash::HashTable<K, K, SetKeyOfT, hash>::iterator iterator;
        iterator begin()
        {
            return _ht.begin();
        }

        iterator end()
        {
            return _ht.end();
        }

        pair<iterator,bool> insert(const K& key)
        {
            return _ht.Insert(key);
        }
    private:
        LinkHash::HashTable<K, K, SetKeyOfT, hash> _ht;
    };

    void test_unordered_set()
    {
        unordered_set<int> us;
        us.insert(4);
        us.insert(14);
        us.insert(34);
        us.insert(7);
        us.insert(24);
        us.insert(17);
        unordered_set<int>::iterator it = us.begin();
        while (it != us.end())
        {
            cout << *it << " ";
            ++it;
        }
        cout << endl;

        unordered_set<int> uss(us);
        unordered_set<int>::iterator It = uss.begin();
        while (It != uss.end())
        {
            cout << *It << " ";
            ++It;
        }
        cout << endl;

        /*unordered_set<string> uss;
        uss.insert("sort");
        uss.insert("hash");*/

        /*unordered_set<int> us;
        for (size_t i = 0; i < 100; ++i)
        {
            us.insert(i);
        }*/
    }
}

//bitSet
#pragma once
#include<vector>

namespace bit
{
    template<size_t N>
    class bitset
    {
    public:
        bitset()
        {
            _bits.resize(N / 8 + 1, 0);
        }

        void set(size_t x)
        {
            size_t i = x / 8;
            size_t j = x % 8;

            _bits[i] |= (1 << j);
        }

        void reset(size_t x)
        {
            size_t i = x / 8;
            size_t j = x % 8;

            _bits[i] &= (~(1 << j));
        }

        bool test(size_t x)
        {
            size_t i = x / 8;
            size_t j = x % 8;

            return _bits[i] & (1 << j);
        }

    private:
        std::vector<char> _bits;
        //std::vector<int> _bits;
    };

    void test_bitset()
    {
        bitset<100> bs;
        bs.set(5);
        bs.set(4);
        bs.set(10);
        bs.set(20);

        cout << bs.test(5) << endl;
        cout << bs.test(4) << endl;
        cout << bs.test(10) << endl;
        cout << bs.test(20) << endl;
        cout << bs.test(21) << endl;
        cout << bs.test(6) << endl << endl;

        bs.reset(20);
        bs.reset(10);
        bs.reset(5);

        cout << bs.test(5) << endl;
        cout << bs.test(4) << endl;
        cout << bs.test(10) << endl;
        cout << bs.test(20) << endl;
        cout << bs.test(21) << endl;
        cout << bs.test(6) << endl;

        //bitset<0xffffffff> bs;
        //bitset<-1> bs;
    }
}

template<size_t N>
class TwoBitSet
{
public:
    void Set(size_t x)
    {
        if (!_bs1.test(x) && !_bs2.test(x)) //00 -> 01
        {
            _bs2.set(x);
        }
        else if (!_bs1.test(x) && _bs2.test(x)) //01 -> 10
        {
            _bs1.set(x);
            _bs2.reset(x);
        }
        //10 表示已经出现2次或以上,不用处理
    }

    void PrintOnceNum()
    {
        for (size_t i = 0; i < N; ++i)
        {
            if (!_bs1.test(i) && _bs2.test(i)) // 01
            {
                cout << i << endl;
            }
        }
    }
private:
    bit::bitset<N> _bs1;
    bit::bitset<N> _bs2;
};

void TestTwoBitSet()
{
    int a[] = { 99,0,4,50,33,44,2,5,99,0,50,99,50,2 };
    TwoBitSet<100> bs;
    for (auto e : a)
    {
        bs.Set(e);
    }
    bs.PrintOnceNum();
}

void TestFindIntersection()
{
    int a1[] = { 5,30,1,99,10 };
    int a2[] = { 8,10,11,9,30,10,30 };
}

//BloomFilter
#pragma once

#include<bitset>
#include<string>
#include<time.h>

struct BKDRHash
{
    size_t operator()(const string& s)
    {
        //BKDR
        size_t value = 0;
        for (auto ch : s)
        {
            value *= 3;
            value += ch;
        }
        return value;
    }
};
struct APHash
{
    size_t operator()(const string& s)
    {
        size_t hash = 0;
        for (long i = 0; i < s.size(); i++)
        {
            if ((i & 1) == 0)
            {
                hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3));
            }
            else
            {
                hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5)));
            }
        }
        return hash;
    }
};

struct DJBHash
{
    size_t operator()(const string& s)
    {
        size_t hash = 5381;
        for(auto ch : s)
        {
            hash += (hash << 5) + ch;
        }
        return hash;
    }
};

template<size_t N,
size_t X = 8,
class K = string,
class HashFunc1 = BKDRHash,
class HashFunc2 = APHash,
class HashFunc3 = DJBHash>
class BloomFilter
{
public:
    void Set(const K& key)
    {
        size_t len = X * N;
        size_t index1 = HashFunc1()(key) % len;
        size_t index2 = HashFunc2()(key) % len;
        size_t index3 = HashFunc3()(key) % len;
        cout << index1 << endl;
        cout << index2 << endl;
        cout << index3 << endl << endl;

        _bs.set(index1);
        _bs.set(index2);
        _bs.set(index3);
    }

    bool Test(const K& key)
    {
        size_t len = X * N;
        size_t index1 = HashFunc1()(key) % len;
        if (_bs.test(index1) == false)
            return false;

        size_t index2 = HashFunc2()(key) % len;
        if (_bs.test(index2) == false)
            return false;

        size_t index3 = HashFunc3()(key) % len;
        if (_bs.test(index3) == false)
            return false;

        return true;  //存在误判的
    }

    //不支持删除,删除可能会影响其它值。
    void Reset(const K& key);
private:
    bitset<X* N> _bs;
};

void TestBloomFilter1()
{
    BloomFilter<100> bm;
    bm.Set("sort");
    bm.Set("left");
    bm.Set("right");
    bm.Set("eat");
    bm.Set("ate");
    bm.Set("https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html");

}

void TestBloomFilter2()
{
    BloomFilter<100> bf;
    bf.Set("张三");
    bf.Set("李四");
    bf.Set("牛魔王");
    bf.Set("红孩儿");
    bf.Set("eat");

    cout << bf.Test("张三") << endl;
    cout << bf.Test("李四") << endl;
    cout << bf.Test("牛魔王") << endl;
    cout << bf.Test("红孩儿") << endl;
    cout << bf.Test("孙悟空") << endl;
    cout << bf.Test("二郎神") << endl;
    cout << bf.Test("猪八戒") << endl;
    cout << bf.Test("ate") << endl;

    //BloomFilter<100> bf;

    srand(time(0));
    size_t N = 100;
    std::vector<std::string> v1;
    for (size_t i = 0; i < N; ++i)
    {
        std::string ur1 = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
        ur1 += std::to_string(1234 + i);
        v1.push_back(ur1);
    }

    for (auto& str : v1)
    {
        bf.Set(str);
    }

    for (auto& str : v1)
    {
        cout << bf.Test(str) << endl;
    }
    cout << endl << endl;

    std::vector<std::string> v2;
    for (size_t i = 0; i < N; ++i)
    {
        std::string ur1 = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
        ur1 += std::to_string(6789 + i);
        v2.push_back(ur1);
    }

    size_t n2 = 0;
    for (auto& str : v2)
    {
        if (bf.Test(str))
        {
            ++n2;
        }
    }
    cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;

    std::vector<std::string> v3;
    for (size_t i = 0; i < N; ++i)
    {
        string ur1 = "zhihu.com";
        //std::string ur1 = "https://mail.qq.com/cgi-bin/frame_html?sid=0QvJ6bvfhn3EC1Iw&r=5976c09322eae24513a5ff315428cd86&lang=zh";
        //std::string ur1 = "https://www.sogou.com/tx?query=%E5%88%98%E5%BC%BA%E4%B8%9C%E4%BA%8B%E4%BB%B6%20%E5%A5%B3%E6%96%B9%E7%A7%B0%E8%87%AA%E6%84%BF%E5%8F%91%E7%94%9F%E5%85%B3%E7%B3%BB&hdq=sogou-site-706608cfdbcc1886&ekv=3&ie=utf8&";
        //std::string ur1 = "https://www.sogou.com/sogou?ie=utf8&pid=sogou-wsse-af64b05ee108fa0c&query=%E4%BA%BA%E6%B0%91%E7%BD%91%3A%E5%BE%B7%E4%BA%91%E7%A4%BE%E8%AF%A5%E8%87%AA%E6%88%91%E6%A3%80%E8%A7%86%E4%BA%86";
        ur1 += std::to_string(rand());
        v3.push_back(ur1);
    }

    size_t n3 = 0;
    for (auto& str : v3)
    {
        if (bf.Test(str))
        {
            ++n3;
        }
    }
    cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;
}

设置为1

首先先用三个哈希函数算出对应的下标

再复用前面的位图的函数将其bit位设为1即可

注意:当len越大时,即空间开的越大时,误判率会越低

检查在不在

三个bit位,只要有一个bit位为0,则表示不在

注意:布隆过滤器不支持删除,因为支持删除会消耗很多空间,而且会存在误删,比如上图中的ate和eat,如果删除ate,那就会改变eat中的其中一个bit位

哈希切割

例:给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法,这里就要用到哈希切割

**注意:如果Ai和Bi都太大,可以考虑换个哈希函数,再切割一次 **

全部代码


本文转载自: https://blog.csdn.net/weixin_58867976/article/details/125464703
版权归原作者 风影66666 所有, 如有侵权,请联系我们删除。

“哈希结构的实现”的评论:

还没有评论