自学内容网 自学内容网

【C++】学习笔记——哈希_2


十八、哈希

3. 实现哈希表

哈希表的实现方法有蛮多种,这里我们选一个比较经典的开散列法来实现哈希表。由于STL库里的哈希表都是将负载因子(插入的数据/哈希表的容量)设置为1,所以我们这里也将负载因子设置为1。

哈希表的存储节点

开散列法实现的哈希表,是通过数组来当哈希表,但是数组中存储的都是指针,像一个链表一样,将同索引的数据在这个指针下串联起来。

// 哈希表数据节点类型
template<class K, class V>
struct HashNode
{
    // 指向同一个桶的下一个节点
HashNode<K, V>* _next;
    // 当前数据
std::pair<K, V> _kv;

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

哈希函数

哈希表是通过哈希函数来确定存储的位置的,但是对于不同的类型,我们无法统一看待。

// 哈希函数
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};

// 哈希函数模板特化
template<>
struct HashFunc<std::string>
{
size_t operator()(const std::string& s)
{
// 哈希值
size_t hash = 0;
for (auto e : s)
{
hash += e;
hash *= 131;
}

return hash;
}
};

哈希表的定义

// 哈希表, Hash是仿函数(哈希函数)
template<class K, class V, class Hash = HashFunc<K>>
class HashTable
{
typedef HashNode<K, V> Node;
public:
// 构造函数
HashTable()
{
_tables.resize(10, nullptr);
_n = 0;
}

// 析构函数
~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;
}
}
private:
    // 哈希表每个位置都是一个链表(桶)
std::vector<Node*> _tables;
    // 数据个数
size_t _n;
};

哈希表的插入

// 插入数据
bool Insert(const std::pair<K, V>& kv)
{
    // 该数据存在就不插入
if (Find(kv.first))
return false;

    // 创建哈希函数对象
    Hash hs;

// 负载因子到1就扩容(满了才扩容)
if (_n == _tables.size())
{
        // 创建临时哈希表对象
std::vector<Node*> newTables(_tables.size() * 2, nullptr);
        // 遍历每个桶
for (size_t i = 0; i < _tables.size(); ++i)
{
// 遍历桶中的每个数据节点
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;

// 通过哈希函数计算索引,将旧节点头插到新表
size_t hashi = hs(cur->_kv.first) % newTables.size();
                // 新节点指向原本的首元节点
cur->_next = newTables[hashi];
                // 头指针指向新节点
newTables[hashi] = cur;

cur = next;
}

_tables[i] = nullptr;
}

        // 交换表
_tables.swap(newTables);
}

    // 通过哈希函数计算索引
size_t hashi = hs(kv.first) % _tables.size();
Node* newnode = new Node(kv);

// 头插
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;

    // 数据个数+1
++_n;
return true;
}

哈希表的查找

由于哈希表是通过哈希函数来确定存储位置的,所以我们也可以通过哈希函数来找到数据。

// 查找数据
Node* Find(const K& key)
{
    // 哈希函数对象
Hash hs;
    // 通过哈希函数查找所在的桶
size_t hashi = hs(key) % _tables.size();
    // 遍历
Node* cur = _tables[hashi];
while (cur)
{
        // 找到了
if (cur->_kv.first == key)
{
return cur;
}

cur = cur->_next;
}

    // 没找到
return nullptr;
}

哈希表的删除

// 删除数据
bool Erase(const K& key)
{
    // 哈希函数对象
Hash hs;
    // 通过哈希函数查找所在的桶
size_t hashi = hs(key) % _tables.size();
    // 遍历
Node* prev = nullptr;
Node* cur = _tables[hashi];
while (cur)
{
if (cur->_kv.first == key)
{
// 删除
if (prev)
{
prev->_next = cur->_next;
}
else
{
_tables[hashi] = cur->_next;
}

delete cur;

--_n;
return true;
}

prev = cur;
cur = cur->_next;
}

return false;
}

测试函数

void TestHT1()
{
HashTable<int, int> ht;
int a[] = { 1,4,24,34,7,44,17,37 };
for (auto e : a)
{
ht.Insert(std::make_pair(e, e));
}

ht.Insert(std::make_pair(5, 5));
ht.Insert(std::make_pair(15, 15));
ht.Insert(std::make_pair(25, 25));

ht.Erase(5);
ht.Erase(15);
ht.Erase(25);
ht.Erase(35);

HashTable<std::string, std::string> dict;
dict.Insert(std::make_pair("sort", "排序"));
dict.Insert(std::make_pair("string", "字符串"));
}

完整代码+结果

HashTable.h

#pragma once

#include <iostream>
#include <vector>
#include <string>

// 哈希函数
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};

// 哈希函数模板特化
template<>
struct HashFunc<std::string>
{
size_t operator()(const std::string& s)
{
size_t hash = 0;
for (auto e : s)
{
hash += e;
hash *= 131;
}

return hash;
}
};

namespace hash_bucket
{
    // 哈希表数据节点类型
template<class K, class V>
struct HashNode
{
        // 指向同一个桶的下一个节点
HashNode<K, V>* _next;
        // 当前数据
std::pair<K, V> _kv;

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

    // 哈希表
template<class K, class V, class Hash = HashFunc<K>>
class HashTable
{
typedef HashNode<K, V> Node;
public:
        // 构造函数
HashTable()
{
_tables.resize(10, nullptr);
_n = 0;
}

        // 插入数据
bool Insert(const std::pair<K, V>& kv)
{
            // 该数据存在就不插入
if (Find(kv.first))
return false;

            // 创建哈希函数对象
            Hash hs;

// 负载因子到1就扩容(满了才扩容)
if (_n == _tables.size())
{
                // 创建临时哈希表对象
std::vector<Node*> newTables(_tables.size() * 2, nullptr);
                // 遍历每个桶
for (size_t i = 0; i < _tables.size(); ++i)
{
// 遍历桶中的每个数据节点
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;

// 通过哈希函数计算索引,将旧节点头插到新表
size_t hashi = hs(cur->_kv.first) % newTables.size();
                        // 新节点指向原本的首元节点
cur->_next = newTables[hashi];
                        // 头指针指向新节点
newTables[hashi] = cur;

cur = next;
}

_tables[i] = nullptr;
}

                // 交换表
_tables.swap(newTables);
}

            // 通过哈希函数计算索引
size_t hashi = hs(kv.first) % _tables.size();
Node* newnode = new Node(kv);

// 头插
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;

            // 数据个数+1
++_n;
return true;
}

        // 查找数据
Node* Find(const K& key)
{
            // 哈希函数对象
Hash hs;
            // 通过哈希函数查找所在的桶
size_t hashi = hs(key) % _tables.size();
            // 遍历
Node* cur = _tables[hashi];
while (cur)
{
                // 找到了
if (cur->_kv.first == key)
{
return cur;
}

cur = cur->_next;
}

            // 没找到
return nullptr;
}

        // 删除数据
bool Erase(const K& key)
{
            // 哈希函数对象
Hash hs;
            // 通过哈希函数查找所在的桶
size_t hashi = hs(key) % _tables.size();
            // 遍历
Node* prev = nullptr;
Node* cur = _tables[hashi];
while (cur)
{
if (cur->_kv.first == key)
{
// 删除
if (prev)
{
prev->_next = cur->_next;
}
else
{
_tables[hashi] = cur->_next;
}

delete cur;

--_n;
return true;
}

prev = cur;
cur = cur->_next;
}

return false;
}

// 析构函数
~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;
}
}
private:
        // 哈希表每个位置都是一个链表(桶)
std::vector<Node*> _tables;
        // 数据个数
size_t _n;
};

void TestHT1()
{
HashTable<int, int> ht;
int a[] = { 1,4,24,34,7,44,17,37 };
for (auto e : a)
{
ht.Insert(std::make_pair(e, e));
}

ht.Insert(std::make_pair(5, 5));
ht.Insert(std::make_pair(15, 15));
ht.Insert(std::make_pair(25, 25));

ht.Erase(5);
ht.Erase(15);
ht.Erase(25);
ht.Erase(35);

HashTable<std::string, std::string> dict;
dict.Insert(std::make_pair("sort", "排序"));
dict.Insert(std::make_pair("string", "字符串"));
}
}

test.cpp

#include <iostream>
#include "HashTable.h"
using namespace std;

int main()
{
    hash_bucket::TestHT1();
    return 0;
}

结果:
在这里插入图片描述
在这里插入图片描述


未完待续


原文地址:https://blog.csdn.net/m0_69828905/article/details/140638435

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!