• 模拟实现【哈希】


    哈希

    unordered系列关联式容器

    unordered_set

    和set接口和功能几乎一样。

    unordered_map

    1.unordered_map是储存键值对的关联式容器,允许通过key快速索引与其对应的value。 2.在unordered_map中,键值用于唯一的标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。 3.在内部,unordered_map没有对按照特定的舒徐排序,为了在常熟范围内找到key所对应的value,unordered_map将相同的键值放在了相同的桶中。 4.unordered_map容器通过key访问单个元素比map块,但它在遍历元素自己的范围迭代方面效率低。 5.unordered_map实现了直接访问操作符(operator[])。它允许key作为参数直接访问value。 6.它的迭代器至少是向前迭代器。

    unordered_map接口

    1.unordered_map构造

    函数声明功能介绍
    unordered_map构造不同格式的unordered_map对象

    2.unordered_map容量

    函数声明功能
    bool empty()const检测为空
    size_t size()const获取有效元素的个数

    3.unordered_map迭代器

    函数声明功能
    begin返回第一个元素的迭代器
    end返回最后一个元素的下一个位置的迭代器
    cbeginconst begin()const
    cendconst end()const

    4.unordered_map的元素访问

    函数声明功能
    operator[]返回key对应的value,没有固定的默认值(取决于key)
    *注意:该函数实际上调用哈希桶的插入操作,参数key与v()构造一个默认值往底层哈希桶插入
    插入成功,返回v() 失败返回key对应的value的值*

    5.unordered_map查询

    函数声明功能
    iterator find(const K& key)返回key在哈希桶中的位置
    size_t count(const K& key)返回哈希桶中有关key的键值对的个数
    unordered_map count返回的最大值为1,原因:不能重复

    6.unordered_map的修改

    函数声明功能
    insert插入键值对
    erase删除键值对
    void clear()清空有效元素的个数
    void swap(unordered_map&)交换两个容器中的元素(指针交换)

    7.unordered_map操作

    函数声明功能
    size_t bucket count()const返回哈希桶中的总个数
    size_t bucket size(size_t n)const返回n号桶有效元素的总个数
    size_tbucket(const K& key)返回元素key所在的桶号

    比较map和unordered_map的效率

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. using namespace std;
    8. void test_map()
    9. {
    10. const int n = 100000;
    11. srand(time(0));
    12. vector<int> v;
    13. v.reserve(n);
    14. for (int i = 0; i < n; i++)
    15. {
    16. v.push_back(rand()+i);//伪随机只有3.5w左右
    17. }
    18. //测试插入的速度
    19. map<int, int> p;
    20. unordered_map<int, int> up;
    21. clock_t start1 = clock();
    22. for (auto e : v)
    23. {
    24. p.insert(make_pair(e, e));
    25. }
    26. clock_t end1 = clock();
    27. clock_t start2 = clock();
    28. for (auto e : v)
    29. {
    30. up.insert(make_pair(e, e));
    31. }
    32. clock_t end2 = clock();
    33. //查找速度
    34. clock_t start3 = clock();
    35. for (auto e : v)
    36. {
    37. p.find(e);
    38. }
    39. clock_t end3 = clock();
    40. clock_t start4 = clock();
    41. for (auto e : v)
    42. {
    43. up.find(e);
    44. }
    45. clock_t end4 = clock();
    46. //删除速度
    47. clock_t start5 = clock();
    48. for (auto e : v)
    49. {
    50. p.erase(e);
    51. }
    52. clock_t end5 = clock();
    53. clock_t start6 = clock();
    54. for (auto e : v)
    55. {
    56. up.erase(e);
    57. }
    58. clock_t end6 = clock();
    59. cout << "插入速度:" << endl << "map:" << end1 - start1 << endl << "unordered_map:" << end2 - start2 << endl;
    60. cout << "find速度:" << endl << "map:" << end3 - start3 << endl << "unordered_map:" << end4 - start4 << endl;
    61. cout << "erase速度:" << endl << "map:" << end5 - start5 << endl << "unordered_map:" << end6 - start6 << endl;
    62. }
    63. int main()
    64. {
    65. test_map();
    66. return 0;
    67. }

    底层结构

    unordered系列之所以效率比较高,是因为底层使用了哈希结构。

    哈希概念

    顺序结构及平衡树中,元素关键码与其存储位置间没有对应关系,因此,在查找一个元素时,必须多次经过关键码的比较。顺序查找的时间复杂度为O(N).平衡树中为树的高度,即O(logN),搜索的效率取决于搜索过程中元素的比较次数。

    理想的搜索方法:可以不经过任何比较,一次直接从表中得到的要搜索的元素。 如果构造一种存储结构,通过某种函数(hashfunc)使元素的存储位置与他的该案件吗之间建立一一映射关系,那么在查找时通过该函数可以很快找到该元素。

    该结构中:

    插入元素 根据待插入元素的关键码,以此函数计算该元素的储存位置并按此位置进行存放。

    存放元素 对元素的关键码进行同样的计算,把求得的函数值当作元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

    该方式为哈希(散列)方法,哈希方法中使用的函数转化称为哈希(散列)函数,构造出来的结构称为哈希表(散列表)

    int a[] = {1,7,4,6,5,9};
    hashi(key)= key % capicity;

    用这种方法,几乎不用做多次关键码比较,搜索速度特别快。 但是我要insert(17)?,会出现哈希冲突的问题。

    哈希冲突

    概念

    不同关键字通过相同的哈希数计算相同的哈希地址,这种现象称为哈希冲突。 把具有相同哈希地址的数据元素称为”同义词“

    哈希函数

    能够触发哈希冲突,可能是哈希函数设计不够合理。 设计原则 1.哈希函数的定义域必须包括需要储存的全部关键码,而如果散列表允许有m个地址,其值域必须到[0,m-1]之间 2.哈希函数算出来的地址能均匀的分布在整个空间中。 3.哈希函数应该比较简单。

    常见哈希函数 1.直接定址法 取关键字的某个线性函数为散列地址:Hash(key) = A*key+B 优点:简单,均匀 缺点:需要事先知道关键字的分布情况 使用场景:适合查找比较小且连续的情况

    2.除留余数法 设散列表中允许的地址数为m,取一个不大于m,但接近或者等于m的质数作为除数,按照哈希函数:Hash(key) = key % p( p <= m ),将关键码换成哈希函数。

    注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突。

    哈希冲突的解决

    闭散列开散列

    闭散列

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

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

    思考: 哈希表啥时候可以进行扩容? 荷载因子: a = 填入表中的元素/散列表的长度 a在[0.7,0.8]为佳。

    线性探测特性
    优点实现非常简单
    缺点一旦发生哈希冲突,却数据量较大,容易引发”堵塞“,导致搜索效率降低

    二次探测 为避免数据过于集中导致的”堵塞“,使用二次探测可以避免该问题。 在线性探测中i = 1,在二次探测中i的值可以随机

    研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次,因此只要表中有一半的空位置,既不会存在表装满的问题。在搜索时可以不考虑表装满的情况,但插入时必须保证a<=0.5

    开散列的最大缺陷就是空间利用率比较低。这也是哈希缺陷 相关代码: 初版,能用但不是目前最优秀的表

    #include
    #include
    #include
    using namespace std;
    ​
    enum State
    {
        EMPTY,
        EXSIST,
        DELETE
    };
    template< class K,class V>
    struct HashData
    {
        pair _kv;
        State _state = EMPTY;//获取某个桶的状态,给个缺省值。
        
    };
    //仿函数,完成对内置类型和string的支持
    template
    struct DefaultHash
    {
        size_t operator()(const K& key)
        {
            return (size_t)key;
        }
    };
    ​
    //模板特化,支持string
    template<>
    struct DefaultHash
    {
        //使用BKDR算法
        size_t operator()(const string& key)
        {
            size_t hash = 0;
            for (auto ch : key)
            {
                hash = hash * 131 + ch;
            }
            return hash;
        }
    };
    ​
    template>
    class HashTable
    {
        typedef HashData Data;
    public:
        bool insert(const pair& kv)
        {
            //hash的扩容必须处理,一般情况下,hash是绝对不会被存满的。
            //就像二叉搜索树一样,欸一个关键字因子
            //
            Hashfunc hf;
            //去重
            if (find(kv.first))//存在就不要再插入了。
                return false;
            //首先判读size()是不是为0?
            //判断关键字因子是不是超过了70%
            if (_table.size() == 0 || _n * 10 / _table.size() >= 7)//这里次序不能颠倒,否则会报错
            {
                size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
                //一旦更新,个元素相对于hashtable的位置就会出错,需要单独调整
                //现代写法:通过船舰一个新的hashtable,通过调用insert函数,此时不会走if语句
                //编译器跑完以后,交换指针即可。
                HashTable hashtable;
                hashtable._table.resize(newsize);
                //
                for (auto& e : _table)
                {
                    if(e._state == EXSIST)
                        hashtable.insert(e._kv);
                }
                hashtable._table.swap(_table);//交换指针,因为hashtable是临时对象,生命周期结束后,会自己析构。
            }
    ​
            size_t starti = hf(kv.first);//获取key
            //将key%=size()获取映射在hashtable中的位置
            starti %= _table.size();
            size_t hashi = starti;
    ​
            //二次查找、扫描
            int i = 1;//采取的是闭散列法,挪到下一个位置上
            while (_table[hashi]._state == EXSIST)//当桶的状态为EXIST时,不能存放数据没。
            {
                hashi = hashi + i;
            }
    ​
            //赋值
            _table[hashi]._kv = kv;
            //更改状态
            _table[hashi]._state = EXSIST;
            _n++;
            return true;
        }
        Data* find(const K& key)//找到返回指针,找不到返回nullptr
        {
            if (_table.size() == 0)
            {
                return nullptr;
            }
            //为空的时候就停止,empty和delete继续
            //闭散列的存储方法,所找的数据都应该在[hashi,_table.end()]内,为空就结束了
            Hashfunc hf;
            size_t starti = hf(key);
            starti %= _table.size();
            size_t hashi = starti;
    ​
            int i = 1;
            while (_table[hashi]._state != EMPTY)
            {
                if (_table[hashi]._state == EXSIST && hf(_table[hashi]._kv.first) == hf(key))
                    //删除是伪删除法,本质是数据的覆盖,所以要state 和 值双重排定
                {
                    return &_table[hashi];
                }
                hashi = hashi + i;
                hashi %= _table.size();//防止hashi越界。
            }
            return nullptr;
        }
    ​
        bool erase(const K& key)
        {
            //为空,就不要删除了
            if (_table.size() == 0)
                return false;
            //找到元素
            Data* ret = find(key);
    ​
            if (ret)//存在
            {
                ret->_state = DELETE;
                _n--;//关键字因子个数要处理,因为exist变化了。
                return true;
            }
            else
            {
                return false;
            }
        }
    private:
        vector _table;
        size_t _n = 0; //关键字因子的个数
    };

    开散列

    概念

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

    开散列增容:

    桶数量是一定数量的,随着新元素的插入,每个桶中的元素不断增多,极端情况下,可能导致一个同种链表节点非常多,会影响哈希表的性能,所以条件合理的情况下,要对哈希表进行扩容。

    扩容条件:每个哈希桶中刚好挂一个节点,继续插入元素时,每一次都会发生哈希冲突。因此,在元素个数刚好等于桶的个数时,可以进行扩容。

    相关代码:

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. enum State
    6. {
    7. EMPTY,
    8. EXSIST,
    9. DELETE
    10. };
    11. template< class K,class V>
    12. struct HashData
    13. {
    14. pair _kv;
    15. State _state = EMPTY;//获取某个桶的状态,给个缺省值。
    16. };
    17. //仿函数,完成对内置类型和string的支持
    18. template<class K>
    19. struct DefaultHash
    20. {
    21. size_t operator()(const K& key)
    22. {
    23. return (size_t)key;
    24. }
    25. };
    26. //模板特化,支持string
    27. template<>
    28. struct DefaultHash
    29. {
    30. //使用BKDR算法
    31. size_t operator()(const string& key)
    32. {
    33. size_t hash = 0;
    34. for (auto ch : key)
    35. {
    36. hash = hash * 131 + ch;
    37. }
    38. return hash;
    39. }
    40. };
    41. template<class K,class V,class Hashfunc = DefaultHash>
    42. class HashTable
    43. {
    44. typedef HashData Data;
    45. public:
    46. bool insert(const pair& kv)
    47. {
    48. //hash的扩容必须处理,一般情况下,hash是绝对不会被存满的。
    49. //就像二叉搜索树一样,欸一个关键字因子
    50. //
    51. Hashfunc hf;
    52. //去重
    53. if (find(kv.first))//存在就不要再插入了。
    54. return false;
    55. //首先判读size()是不是为0?
    56. //判断关键字因子是不是超过了70%
    57. if (_table.size() == 0 || _n * 10 / _table.size() >= 7)//这里次序不能颠倒,否则会报错
    58. {
    59. size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
    60. //一旦更新,个元素相对于hashtable的位置就会出错,需要单独调整
    61. //现代写法:通过船舰一个新的hashtable,通过调用insert函数,此时不会走if语句
    62. //编译器跑完以后,交换指针即可。
    63. HashTable hashtable;
    64. hashtable._table.resize(newsize);
    65. //
    66. for (auto& e : _table)
    67. {
    68. if(e._state == EXSIST)
    69. hashtable.insert(e._kv);
    70. }
    71. hashtable._table.swap(_table);//交换指针,因为hashtable是临时对象,生命周期结束后,会自己析构。
    72. }
    73. size_t starti = hf(kv.first);//获取key
    74. //将key%=size()获取映射在hashtable中的位置
    75. starti %= _table.size();
    76. size_t hashi = starti;
    77. //二次查找、扫描
    78. int i = 1;//采取的是闭散列法,挪到下一个位置上
    79. while (_table[hashi]._state == EXSIST)//当桶的状态为EXIST时,不能存放数据没。
    80. {
    81. hashi = hashi + i;
    82. }
    83. //赋值
    84. _table[hashi]._kv = kv;
    85. //更改状态
    86. _table[hashi]._state = EXSIST;
    87. _n++;
    88. return true;
    89. }
    90. Data* find(const K& key)//找到返回指针,找不到返回nullptr
    91. {
    92. if (_table.size() == 0)
    93. {
    94. return nullptr;
    95. }
    96. //为空的时候就停止,empty和delete继续
    97. //闭散列的存储方法,所找的数据都应该在[hashi,_table.end()]内,为空就结束了
    98. Hashfunc hf;
    99. size_t starti = hf(key);
    100. starti %= _table.size();
    101. size_t hashi = starti;
    102. int i = 1;
    103. while (_table[hashi]._state != EMPTY)
    104. {
    105. if (_table[hashi]._state == EXSIST && hf(_table[hashi]._kv.first) == hf(key))
    106. //删除是伪删除法,本质是数据的覆盖,所以要state 和 值双重排定
    107. {
    108. return &_table[hashi];
    109. }
    110. hashi = hashi + i;
    111. hashi %= _table.size();//防止hashi越界。
    112. }
    113. return nullptr;
    114. }
    115. bool erase(const K& key)
    116. {
    117. //为空,就不要删除了
    118. if (_table.size() == 0)
    119. return false;
    120. //找到元素
    121. Data* ret = find(key);
    122. if (ret)//存在
    123. {
    124. ret->_state = DELETE;
    125. _n--;//关键字因子个数要处理,因为exist变化了。
    126. return true;
    127. }
    128. else
    129. {
    130. return false;
    131. }
    132. }
    133. private:
    134. vector _table;
    135. size_t _n = 0; //关键字因子的个数
    136. };
    137. namespace Bucket
    138. {
    139. template<class K,class V>
    140. struct HashNode
    141. {
    142. pair _kv;
    143. HashNode* _next;
    144. //构造函数
    145. HashNode(const pair& kv)
    146. :_kv(kv)
    147. ,_next(nullptr)
    148. {}
    149. };
    150. template<class K>
    151. struct DefaultHash
    152. {
    153. size_t operator()(const K& key)
    154. {
    155. return (size_t)key;
    156. }
    157. };
    158. template<>
    159. struct DefaultHash
    160. {
    161. size_t operator()(const string& key)
    162. {
    163. size_t hash = 0;
    164. for (auto ch : key)
    165. {
    166. hash += ch * 131;
    167. }
    168. return hash;
    169. }
    170. };
    171. template<class K,class V>
    172. class HashTable
    173. {
    174. typedef HashNode Node;
    175. public:
    176. ~HashTable()
    177. {
    178. for (int i = 0; i < _table.size(); i++)
    179. {
    180. Node* cur = _table[i];
    181. while (cur)
    182. {
    183. Node* next = cur->_next;
    184. delete cur;
    185. cur = next;
    186. }
    187. }
    188. }
    189. bool insert(const pair& kv)
    190. {
    191. if (find(kv.first))
    192. return false;
    193. //扩容否?
    194. if (_n == _table.size())
    195. {
    196. size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;
    197. //重新映射
    198. HashTable newHT;
    199. newHT.resize(newSize,nullptr);
    200. for (int i = 0; i < _table.size(); i++)
    201. {
    202. Node* cur = _table[i];
    203. while (cur)
    204. {
    205. newHT.insert(cur->_kv);
    206. cur = cur->_next;
    207. }
    208. }
    209. newHT._table.swap(_table);//需要析构函数
    210. //原因:vector自己调用析构函数,但是Node节点却不会自己析构,因为其是内置类型的。
    211. }
    212. size_t starti = kv.first;
    213. starti %= _table.size();
    214. size_t hashi = starti;
    215. Node* newnode = new Node(kv);
    216. //头插法链接
    217. newnode->_next = _table[hashi];
    218. _table[hashi] = newnode;
    219. _n++;
    220. }
    221. Node* find(const K& key)
    222. {
    223. if (_table.size() == 0)
    224. {
    225. return nullptr;
    226. }
    227. size_t starti = key;
    228. size_t hashi = starti %= _table.size();
    229. Node* cur = _table[hashi];//找到key所在的哪个桶
    230. while (cur)
    231. {
    232. if (cur->_kv.first == key)
    233. {
    234. return true;
    235. }
    236. cur = cur->_next;
    237. }
    238. return false;
    239. }
    240. bool erase(const K& key)
    241. {
    242. if (_table.size() == 0)
    243. return false;
    244. size_t starti = key;
    245. size_t hashi = starti %= _table.size();
    246. Node* cur = _table[hashi];
    247. Node* prev = nullptr;
    248. while (cur)
    249. {
    250. if (prev == nullptr)//头节点
    251. {
    252. _table[hashi] = cur->_next;
    253. delete cur;
    254. return true;
    255. }
    256. else//非头结点
    257. {
    258. prev->_next = cur->_next;
    259. delete cur;
    260. return true;
    261. }
    262. //更新
    263. prev = cur;
    264. cur = cur->_next;
    265. }
    266. return false;
    267. }
    268. private:
    269. vector _table;
    270. size_t _n = 0;
    271. };
    272. }

    哈希的模拟实现

    类似于map和set的封装 template

    hash.h

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. template<class K>
    7. struct DefaultHash
    8. {
    9. size_t operator()(const K& key)
    10. {
    11. return (size_t)key;
    12. }
    13. };
    14. template<>
    15. struct DefaultHash
    16. {
    17. size_t operator()(const string& key)
    18. {
    19. size_t hash = 0;
    20. for (auto ch : key)
    21. {
    22. hash = hash*131 + ch;
    23. }
    24. return hash;
    25. }
    26. };
    27. namespace Bucket
    28. {
    29. template<class T>
    30. struct HashNode
    31. {
    32. T _data;
    33. HashNode* _next;
    34. //构造函数
    35. HashNode(const T& data)
    36. :_data(data)
    37. , _next(nullptr)
    38. {}
    39. };
    40. //set
    41. //template
    42. /*map
    43. template*/
    44. //必须前置声明HashTable
    45. template<class K, class T, class KeyOfT, class hashFunc>
    46. class HashTable;
    47. template<class K, class T, class KeyOfT, class hashFunc>
    48. class __HTIterator
    49. {
    50. typedef HashNode Node;
    51. typedef __HTIterator Self;
    52. public:
    53. Node* _node;//迭代器返回的是节点
    54. HashTable* _pht;//表的指针
    55. //进行迭代器的处理
    56. __HTIterator(Node* node, HashTable* pht)
    57. :_node(node)
    58. , _pht(pht)
    59. {}
    60. //前置++
    61. Self& operator++()
    62. {
    63. if (_node->_next)//下一个不为空
    64. {
    65. _node = _node->_next;
    66. }
    67. else//为空
    68. {
    69. //确定_node的位置
    70. KeyOfT kot;
    71. hashFunc hf;
    72. size_t hashi = hf(kot(_node->_data));//转成key->hashi
    73. hashi %= _pht->_table.size();//找到映射位置
    74. ++hashi;
    75. for (; hashi < _pht->_table.size(); hashi++)
    76. {
    77. if (_pht->_table[hashi])//找到
    78. {
    79. _node = _pht->_table[hashi];
    80. break;
    81. }
    82. }
    83. if (hashi == _pht->_table.size())//没找到
    84. {
    85. _node = nullptr;
    86. }
    87. }
    88. return *this;
    89. }
    90. Self operator++(int)
    91. {
    92. Self tmp(*this);
    93. ++(this);
    94. return tmp;
    95. }
    96. T& operator*()
    97. {
    98. return _node->_data;
    99. }
    100. T* operator->()
    101. {
    102. return &_node->_data;
    103. }
    104. bool operator==(const Self& s)const
    105. {
    106. return _node == s._node;
    107. }
    108. bool operator!=(const Self& s)const
    109. {
    110. return _node != s._node;
    111. }
    112. };
    113. template<class K, class T, class KeyOfT, class hashFunc>
    114. class HashTable
    115. {
    116. template<class K, class T, class KeyOfT, class hashFunc>
    117. friend class __HTIterator;
    118. typedef HashNode Node;
    119. public:
    120. typedef __HTIterator iterator;
    121. //iterator 有两个参数 _node _pht
    122. iterator begin()
    123. {
    124. //找到第一个元素
    125. for (size_t i = 0; i < _table.size(); i++)
    126. {
    127. if (_table[i])
    128. {
    129. return iterator(_table[i], this);//单参数才支持隐士类型的转化
    130. }
    131. }
    132. //找不到
    133. return end();
    134. }
    135. iterator end()
    136. {
    137. return iterator(nullptr, this);//单参数才支持隐士类型的转化
    138. }
    139. public:
    140. ~HashTable()
    141. {
    142. size_t i = 0;
    143. for (; i < _table.size(); i++)
    144. {
    145. Node* cur = _table[i];
    146. while (cur)
    147. {
    148. Node* next = cur->_next;
    149. delete cur;
    150. cur = next;
    151. }
    152. _table[i] = nullptr;
    153. }
    154. }
    155. bool Insert(const T& data)
    156. {
    157. KeyOfT kot;//用于获取key的值
    158. hashFunc hf;//哈希函数,用于获取对应的hash
    159. if (Find(kot(data)))
    160. return false;
    161. //扩容否?
    162. if (_n == _table.size())
    163. {
    164. size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;
    165. //重新映射
    166. /*HashTable newHT;
    167. newHT.resize(newSize,nullptr);*/
    168. vector newHT;
    169. newHT.resize(newSize, nullptr);
    170. for (size_t i = 0; i < _table.size(); i++)
    171. {
    172. Node* cur = _table[i];
    173. while (cur)
    174. {
    175. /*newHT.Insert(cur->_kv);
    176. cur = cur->_next;*/
    177. //优化写法,获取原表的节点,直接链接到newHT
    178. Node* next = cur->_next;//手动记录下一个cur的下一个节点
    179. size_t starti = hf(kot(cur->_data));//获取到key以后,再获取key的映射hash
    180. size_t hashi = starti %= newSize;//获取映射的hashi
    181. //链接,头插法。
    182. cur->_next = newHT[hashi];
    183. newHT[hashi] = cur;
    184. cur = next;
    185. }
    186. _table[i] = nullptr;
    187. }
    188. newHT.swap(_table);//需要析构函数
    189. //原因:vector自己调用析构函数,但是Node节点却不会自己析构,因为其是内置类型的。
    190. }
    191. size_t starti = kot(data);
    192. starti %= _table.size();
    193. size_t hashi = hf(starti);
    194. Node* newnode = new Node(data);
    195. //头插法链接
    196. newnode->_next = _table[hashi];
    197. _table[hashi] = newnode;
    198. _n++;
    199. return true;
    200. }
    201. Node* Find(const K& key)
    202. {
    203. hashFunc hf;
    204. KeyOfT kot;
    205. if (_table.size() == 0)
    206. {
    207. return nullptr;
    208. }
    209. size_t starti = hf(key);
    210. size_t hashi = starti %= _table.size();
    211. Node* cur = _table[hashi];//找到key所在的哪个桶
    212. while (cur)
    213. {
    214. if (kot(cur->_data) == key)
    215. {
    216. return cur;
    217. }
    218. cur = cur->_next;
    219. }
    220. return nullptr;
    221. }
    222. bool Erase(const K& key)
    223. {
    224. hashFunc hf;
    225. if (_table.size() == 0)
    226. return false;
    227. size_t starti = hf(key);//获取hash,传递的是否类型是不知道的
    228. size_t hashi = starti %= _table.size();
    229. Node* cur = _table[hashi];
    230. Node* prev = nullptr;
    231. while (cur)
    232. {
    233. if (prev == nullptr)//头节点
    234. {
    235. _table[hashi] = cur->_next;
    236. delete cur;
    237. return true;
    238. }
    239. else//非头结点
    240. {
    241. prev->_next = cur->_next;
    242. delete cur;
    243. return true;
    244. }
    245. //更新
    246. prev = cur;
    247. cur = cur->_next;
    248. }
    249. return false;
    250. }
    251. private:
    252. vector _table;
    253. size_t _n = 0;
    254. };
    255. }
     
    

    unordered_set

    1. #include"Hash.h"
    2. namespace bit
    3. {
    4. template<class K,class hashFunc = DefaultHash>
    5. class set
    6. {
    7. struct SetKeyOfT
    8. {
    9. const K& operator()(const K& key)
    10. {
    11. return key;
    12. }
    13. };
    14. public:
    15. typedef typename Bucket::HashTable::iterator iterator;//去类里面的内嵌类型,需要typename
    16. iterator begin()
    17. {
    18. return _ht.begin();
    19. }
    20. iterator end()
    21. {
    22. return _ht.end();
    23. }
    24. bool insert(const K& key)
    25. {
    26. return _ht.Insert(key);
    27. }
    28. private:
    29. Bucket::HashTable _ht;
    30. };
    31. }

    unordered_map

    1. #include"Hash.h"
    2. namespace bit
    3. {
    4. template<class K,class V,class hashFunc = DefaultHash>
    5. class unordered_map
    6. {
    7. struct MapKeyOfT
    8. {
    9. const K& operator()(const pair& kv)
    10. {
    11. return kv.first;
    12. }
    13. };
    14. public:
    15. typedef typename Bucket::HashTable, MapKeyOfT, hashFunc>::iterator iterator;
    16. iterator begin()
    17. {
    18. return _ht.begin();
    19. }
    20. iterator end()
    21. {
    22. _ht.end();
    23. }
    24. bool insert(const pair& kv)
    25. {
    26. _ht.Insert(kv);
    27. }
    28. private:
    29. Bucket::HashTable, MapKeyOfT> _ht;
    30. };
    31. }

  • 相关阅读:
    【Python笔记-设计模式】原型模式
    华清 Qt day2 9月18
    prometheus基于文件发现及热加载
    【文件包含】phpmyadmin 文件包含(CVE-2014-8959)
    remote: Permission to xxxxx.git denied to xxxxx.
    (swjtu西南交大)数据库实验(数据库需求分析):音乐软件数据管理系统
    【视频处理】通过调用图像来重建新影片及计算颜色通道的平均灰度值,并检测帧与前一帧之间的差异(Matlab代码实现)
    Python基础入门篇【44】--文件读写实例:读取一个文件,并将其中的内容写入到创建的包里
    【21天算法挑战赛】排序算法——直接插入排序
    在HbuilderX中,@tap和@click的含义 与 区别 及 使用方式
  • 原文地址:https://blog.csdn.net/weixin_61932507/article/details/126809234