当前位置: 首页 > news >正文

德州网站设计深圳网站 制作信科便宜

德州网站设计,深圳网站 制作信科便宜,防疫网站网页设计,网站建设行业资讯⭐上篇文章:37.C哈希2(哈希表底层分析与模拟实现-闭散列key模型与key-value模型)-CSDN博客 ⭐本篇代码:c学习/20.哈希与哈希表 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) ⭐标⭐是比较重要的部分 目录 一. 拉链…

⭐上篇文章:37.C++哈希2(哈希表底层分析与模拟实现-闭散列key模型与key-value模型)-CSDN博客

⭐本篇代码:c++学习/20.哈希与哈希表 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

⭐标⭐是比较重要的部分

目录

一. 拉链法与哈希桶

 二. 开散列哈希表代码实现

2.1 哈希节点

2.2 哈希表框架 

2.3 Insert

2.4 Print 

2.5 测试1

2.6 Find

2.7 Erase

三. 完整测试

3.1 key模型

​编辑 3.2 key-value模型


一. 拉链法与哈希桶

        哈希映射是有冲突的,闭散列通过探测的方式,如果发现自己映射的位置被抢占了,就去抢占其他空闲的位置,这会让哈希冲突变多,从而导致效率降低。

        而拉链法和哈希桶解决哈希冲突的方式是:将映射相同位置的所有节点以链表的方式连在一起,这样一来就不会去抢占其他人的位置!

        将具有相同地址映射的的节点归于一个子集,每一个子集称为一个哈希桶。桶中的元素通过链表或者其他方式连接起来,链表的头节点放在哈希表中。

 如下图,将相同冲突的元素通过链表进行连接。

 二. 开散列哈希表代码实现

2.1 哈希节点

        与闭散列不同,开散列的哈希节点不需要存放该节点的状态,而是需要存放一个next指针。只用指向下一个节点(如果使用红黑树等结构需要更多的指针)。

节点代码如下:

template <class T>
struct HashData
{T _data;HashData<T> *_next;// 构造函数HashData(const T &data = T()): _data(data), _next(nullptr) {}
};

2.2 哈希表框架 

        与闭散列一样,需要插入函数,删除函数,查找函数,遍历,以及获取当前数据的key值

        成员变量为:_num表示有效数据,_table表示哈希表,这里使用vector作为哈希桶,内部的数据类型的HashData*

代码如下:

// 使用KeyOfT来获取pair的key值
// unordered_set -> <Key,Key>
// unordered_map -> <Key,Value>
template <class K>
struct SetKeyOfT
{const K &operator()(const K &key){return key;}
};template <class K, class T>
struct MapKeyOfT
{const K &operator()(const pair<K, T> &kv){return kv.first;}
};//哈希表,默认为set(key模型)
template <class K, class T, class KeyOfT = SetKeyOfT<int>>
class OpenHashTable
{typedef ::HashData<T> HashData;public:bool Insert(const T &data) {}HashData *Find(const K &key) {}bool Erase(const K &key) {}void Print() {}private:vector<HashData *> _table;size_t _num = 0;
};

2.3 Insert⭐

        与闭散列一样,开散列也有一个负载因子,只要哈希表中的有效数据与哈希表的长度一样多时候。表明这个哈希表中的数据冲突已经达到了理想状态,此时就需要"扩容"。

        理想状态:每一个哈希桶桶只有一个数据,效率达到最高为O(1)。

        最糟状态:所有的有效数据都集中在一个哈希桶中,效率退化到O(N)。

        如何扩容,需要注意什么?扩容的时候需要遍历旧表,将旧表中的所有数据重新映射到新表中。由于取模的数字变大,每一次扩容都会降低哈希冲突!

        由于每一个哈希桶中都是链表,插入数据的时候采用头插法更简单!

代码如下: 

    bool Insert(const T &data){KeyOfT koft{};// 1. 判断是否需要扩容if (_table.size() == 0 || _num * 100 / _table.size() > 75){// 开辟新表,减少哈希冲突size_t newsize = (_table.size() == 0) ? 10 : _table.size() * 2;vector<HashData *> newtable;newtable.resize(newsize);// 遍历旧表,重新哈希映射到新表for (int i = 0; i < _table.size(); ++i){// 判断每一个哈希桶,通过头插法插入到新表中HashData *cur = _table[i];while (cur != nullptr){HashData *next = cur->_next;int index = koft(cur->_data) % newsize;cur->_next = newtable[index];newtable[index] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newtable); // 使用交换快速替换两张表,旧表直接不要了}// 2. 插入新的数据// 首先需要判断表中有无该数据int index = koft(data) % _table.size();HashData *cur = _table[index];while (cur != nullptr){// 只需要判断key值即可,因为key-value。不同的key可能存在value相同的情况if (koft(cur->_data) == koft(data))return false;cur = cur->_next;}HashData *newnode = new HashData(data);newnode->_next = _table[index];_table[index] = newnode;++_num; // 别忘了增加有效数据return true;}

2.4 Print 

        既然插入的代码写完了,这时候需要遍历来测试代码是否正确。遍历比较简单,只需要循环遍历哈希表,表头不为空遍历这个链表即可。

代码如下:这里只打印了data的key值

void Print(){KeyOfT koft{};for (int i = 0; i < _table.size(); ++i){HashData *cur = _table[i];std::cout << i << ": ";while (cur != nullptr){std::cout << koft(cur->_data) << " -> ";cur = cur->_next;}std::cout << "nullptr" << std::endl;}}

2.5 测试1

        随机插入20个数据,打印哈希表中的内容。查看有无bug

测试代码如下:

#include <iostream>
#include "HashTable.h"
using namespace std;void testset()
{OpenHashTable<int, int> ht;for (int i = 0; i < 20; i++){ht.Insert(rand() % 100);}ht.Print();
}int main()
{srand(time(0) ^ rand());testset();return 0;
}

测试结果如下:

可以看到,结果是正确的!

2.6 Find

        这个函数是用于查找某一个节点是否在哈希表中,不能去遍历查找,这样的效率是O(N)。而应该先求出该数据在哈希表中的映射位置,在这个哈希桶中查找。间效率提升至O(1)。

HashData *Find(const K &key){if (_table.empty())return nullptr;KeyOfT koft;size_t index = key % _table.size();HashData *cur = _table[index];while (cur != nullptr){if (koft(cur->_data) == key)return cur;cur = cur->_next;}return nullptr;}

2.7 Erase⭐

        不可以通过Find查找然后去删除,因为桶中是链表,删除某一个节点之后。需要将该节点的前后节点连接起来!

        链表删除头节点,只需要将头节点的next节点赋值给头节点即可

 bool Erase(const K &key){if (_table.empty())return false;KeyOfT koft;int index = key % _table.size();HashData *prev = nullptr;HashData *cur = _table[index];while (cur != nullptr){if (koft(cur->_data) == key){// 头节点就是删除的节点,需要将头节点置为nextif (prev != nullptr)prev->_next = cur->_next;else_table[index] = cur->_next;delete cur;return true;}prev = cur;cur = cur->_next;}return false;}

三. 完整测试

3.1 key模型

#include <iostream>
#include "HashTable.h"
using namespace std;void testset()
{OpenHashTable<int, int> ht;for (int i = 0; i < 15; i++){ht.Insert(rand() % 100);}ht.Print();while (true){cout << "删除数据输入1, 查找数据输入2, 退出输入0:";int n = 0;cin >> n;if (n == 0)break;else if (n == 1){cout << "请输入删除的数字:";int num;cin >> num;bool flag = ht.Erase(num);if (flag){cout << "删除成功,哈希表如下" << endl;ht.Print();}elsecout << "没有这个数据,删除失败!" << endl;}else if (n == 2){cout << "请输入查找的数字:";int num;cin >> num;HashData<int> *data = ht.Find(num);if (data == nullptr)cout << "这个数据不存在" << endl;elsecout << "这个数据存在" << data->_data << endl;}else{cout << "非法输入" << std::endl;continue;}}
}int main()
{srand(time(0) ^ rand());testset();return 0;
}

测试结果如下:

 3.2 key-value模型

        key-value模型需要修改print,让其打印kv键值对

    void PrintMap(){KeyOfT koft{};for (int i = 0; i < _table.size(); ++i){HashData *cur = _table[i];std::cout << i << ": ";while (cur != nullptr){std::cout << cur->_data.first << ":" << cur->_data.second << " -> ";cur = cur->_next;}std::cout << "nullptr" << std::endl;}}
#include <iostream>
#include "HashTable.h"
using namespace std;void testset()
{OpenHashTable<int, pair<int, char>, MapKeyOfT<int, char>> ht;for (int i = 0; i < 15; i++){int t = rand() % 26;ht.Insert(make_pair(i, (char)(i + 'a')));}ht.PrintMap();while (true){cout << "删除数据输入1, 查找数据输入2, 退出输入0:";int n = 0;cin >> n;if (n == 0)break;else if (n == 1){cout << "请输入删除的数字:";int num;cin >> num;bool flag = ht.Erase(num);if (flag){cout << "删除成功,哈希表如下" << endl;ht.PrintMap();}elsecout << "没有这个数据,删除失败!" << endl;}else if (n == 2){cout << "请输入查找的数字:";int num;cin >> num;HashData<pair<int, char>> *data = ht.Find(num);if (data == nullptr)cout << "这个数据不存在" << endl;elsecout << "这个数据存在" << data->_data.first << ":" << data->_data.second << endl;}else{cout << "非法输入" << std::endl;continue;}}
}int main()
{srand(time(0) ^ rand());testset();return 0;
}

 测试结果如下:

以上已经基本完成了哈希表的简单实现,还需要改进哈希函数(字符串映射),迭代器实现,封装为unordered_set 与 unordered_map 

http://www.dtcms.com/a/615049.html

相关文章:

  • 什么是协程
  • 积木城堡-DP
  • 魔兽做图下载网站wordpress切换语言 语言包
  • chrony组件和NTP组件的区别
  • 网站开发 相册wordpress熊掌
  • 计算机组成原理 刘宏伟 第六章 计算机的运算方法(下)
  • C语言编译软件文档 | 提供完整功能与使用指南,帮助开发者高效编译程序
  • 在线网站排名工具积分商城系统
  • 个人网站开发多少钱wordpress钩子大全
  • 在FreeBSD 14.3上部署轻量级Linux jail环境 仅仅占用10M内存
  • 室内设计师是干嘛的快速优化seo软件推广方法
  • Datawhale:吴恩达Post-training of LLMs,学习打卡4
  • ADC 药物:“生物导弹” 的精准抗癌机制与未来潜力
  • 网站管理助手山东 网站备案
  • 签证网站建设wordpress怎么做采集
  • dw做的网页在网站图片不显示网站开发范围说明书
  • 做网站的网页用什么软件好襄阳网站制作公司有哪些
  • 突破分割边界!多模态大模型X-SAM:从 “分割万物” 到 “任意分割”,实现全场景图像分割统一
  • 网站后台登录界面代码洛阳制作网站的公司吗
  • 个人可以建设头条网站吗关键词搜索指数
  • 西安网站建设推广专家安徽网站建设seo优化
  • 网站建设合同要求绍兴 网站建设
  • 字节技术总监笔记:linux多线程>>进程线程互斥管道
  • 个人网站备案 内容黑龙江建设兵团知青网站
  • AI 大模型如何给 CAD 3D 模型“建立语义”?
  • MYSQL指令合集
  • 北京企业网站百度wordpress结构化数据插件 sign 检测失败
  • 网站建设需要注意哪些事项前端网站开发课程
  • 周期购那个网站做的比较好太原seo整站优化
  • 加强网站安全建设说明报告范文湖南长沙招聘