略谈set与map的pair封装与进入哈希

引子:之前我们讲了红黑树的自实现,与小小的接口实现,那set与map的pair封装是如何实现的呢?,今天我们来一探究竟,而且我们也要进入新章节--哈希

对于operator--()的封装:

注意:牢记思考的方向始终是中序

示意图:

stl代码实现:

  void decrement(){if (node->color == __rb_tree_red &&node->parent->parent == node)node = node->right;else if (node->left != 0) {base_ptr y = node->left;while (y->right != 0)y = y->right;node = y;}else {base_ptr y = node->parent;while (node == y->left) {node = y;y = y->parent;}node = y;}}
};

自实现:我们在传的时候要传一下_root,因为我们不是通过以上stl中hearer的方式

迭代器部分更改:

Node* _root;RBTreeIterator(Node*node,Node*root):_node(node),_root(root)
{}
self& operator--()
{if (_node == nullptr) {//找最右节点Node* rightMost = _root;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else if (_node->_left){Node* rightMost = _node->_left;while (rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}

对于set的insert封装:

//插入
pair<iterator,bool>insert(const T& data)
{//空树新增节点,也是红黑树Node* root = _root;if (root == nullptr){root = new Node(data);_root = root;_root->_col = BLACK;return make_pair(iterator(_root, _root), true);}//红黑树大逻辑K_of_T kot;Node* cur = _root;Node* parent = nullptr;//要先找到,插入位置while (cur){if (kot(data) < kot(cur->_Data)){parent = cur;cur = cur->_left;}else if (kot(data) > kot(cur->_Data)){parent = cur;cur = cur->_right;}else{return make_pair(iterator(cur, _root), false);}}cur = new Node(data);// 新增节点。颜色红色给红色cur->_col = RED;Node* newNode = cur;if (kot(parent->_Data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//更改颜色//现在cur为新增节点while (parent && parent->_col==RED){Node* grandfather = parent->_parent;//找出叔叔节点if (parent == grandfather->_left){Node* uncle = grandfather->_right;//情况一if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{//情况二//    g//  p   u//cif (parent->_left == cur){RotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{//情况三//    g//  p   u//    c// //双旋RotateL(parent);RotateR(grandfather);//注意cur与parent调了一下位置cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;//情况一if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{ //情况二//    g//  u   p//        cif (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}//情况三//    g//  u   p//    c    else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}//确保根节点为黑的_root->_col = BLACK;return make_pair(iterator(newNode, _root), true);
}

对于:map的话,只要将k对应的vaule输出就行!,注意调用的时候也要改为pair<iterator,bool>!

什么是哈希?

一,哈希是一种数学函数,它接受一个输入(或“消息”),然后返回一个通常更小的固定大小的输出,这个输出称为“哈希值”或“哈希码”。

二,哈希也是一种思想:映射:哈希思想通过哈希函数将任意长度的数据映射到固定长度的哈希值。这个映射过程是单向的,即从数据到哈希值是容易的,但从哈希值回溯到原始数据几乎是不可能的。快速性:哈希函数的设计旨在快速计算,以便在大数据集中实现高效的数据访问。均匀分布:理想情况下,哈希函数应该能够将输入数据均匀地分布在哈希值空间中,以减少冲突并提高查找效率。

哈希的应用:

哈希思想在数据库索引、密码存储、信息检索、数据同步、数字签名、区块链技术等多个领域都有广泛的应用。通过哈希,可以有效地管理和访问大量数据,同时保证数据的安全性和完整性。

由哈希来的哈希表(unordered)

一,背景:

在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到log_2N,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好 的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个 unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,

以下是unordered_set与set的区别图,我们可以更加看到底层结构为哈希表的优势!

对于调试性能,大家可以通过以下代码进行测试:

#include<unordered_set>
#include<iostream>
#include<set>using namespace std;int test_set2()
{const size_t N = 10000000;unordered_set<int> us;set<int> s;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; ++i){//v.push_back(rand()); // N比较大时,重复值比较多//v.push_back(rand()+i); // 重复值相对少v.push_back(i); // 没有重复,有序}size_t begin1 = clock();for (auto e : v){s.insert(e);}size_t end1 = clock();cout << "set insert:" << end1 - begin1 << endl;size_t begin2 = clock();for (auto e : v){us.insert(e);}size_t end2 = clock();cout << "unordered_set insert:" << end2 - begin2 << endl;int m1 = 0;size_t begin3 = clock();for (auto e : v){auto ret = s.find(e);if (ret != s.end()){++m1;}}size_t end3 = clock();cout << "set find:" << end3 - begin3 << "->" << m1 << endl;int m2 = 0;size_t begin4 = clock();for (auto e : v){auto ret = us.find(e);if (ret != us.end()){++m2;}}size_t end4 = clock();cout << "unorered_set find:" << end4 - begin4 << "->" << m2 << endl;cout << "插入数据个数:" << s.size() << endl;cout << "插入数据个数:" << us.size() << endl << endl;size_t begin5 = clock();for (auto e : v){s.erase(e);}size_t end5 = clock();cout << "set erase:" << end5 - begin5 << endl;size_t begin6 = clock();for (auto e : v){us.erase(e);}size_t end6 = clock();cout << "unordered_set erase:" << end6 - begin6 << endl << endl;return 0;
}int main()
{test_set2();return 0;
}

可以有以下的结果:只展示一种

哈希函数

引起哈希冲突的一个原因可能是:哈希函数设计不够合理。

哈希函数设计原则:

哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值 域必须在0到m-1之间

哈希函数计算出来的地址能均匀分布在整个空间中

哈希函数应该比较简单

常见的哈希函数包括以下几种类型:(最常用的,用颜色标出了)

直接定址法:使用关键字本身作为哈希地址,例如年龄作为关键字时,年龄值直接作为哈希地址 

数字分析法:选择数字的某些部分作为哈希地址,避免使用重复可能性大的数字前几位 

平方取中法:取关键字平方后的中间几位作为哈希地址 

折叠法:将关键字分割成位数相同的几部分,然后取这几部分的叠加和作为哈希地址 

除留余数法使用关键字除以一个不大于哈希表大小的数后的余数作为哈希地址,公式为 H(key) = key%p (p ≤ m) ;

随机数法:使用随机函数作为哈希地址,适用于关键字长度不等的情况 

加法哈希:通过将字符串中每个字符的ASCII值累加得到哈希值 

位运算Hash:利用位运算(如移位和异或)混合输入元素,例如旋转Hash 

乘法Hash:使用乘法的不相关性,例如使用乘数31的String类的hashCode()方法 

除法Hash:虽然不常用,但除法也具有不相关性,可以用于哈希函数 

查表Hash:如CRC系列算法,通过查找预设的表来实现快速哈希 

混合哈希算法:结合以上各种方式,例如MD5、Tiger等,它们通常用于需要高安全性的场合 

哈希冲突解决

哈希冲突

哈希冲突,也称为哈希碰撞,是指两个不同的输入值通过哈希函数计算后得到相同的哈希值。由于哈希函数的输出长度是固定的,而输入数据可以是无限的,理论上讲,任何哈希函数都可能发生冲突.

解决哈希冲突两种常见的方法是:闭散列和开散列

一,什么是闭散列

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

1.1,线性探测(需要枚举3种状态)
  1. 计算哈希值:首先,使用哈希函数计算键(key)的哈希值,确定它在哈希表中的理论位置。

  2. 检查冲突:如果该位置已被占用(即发生冲突),则按照固定间隔(通常是1)移动到下一个位置。

  3. 探测序列:继续线性地探测下一个位置,直到找到一个空闲位置。

  4. 插入元素:一旦找到空闲位置,将元素插入到该位置。

  5. 处理表满:如果探测到表的末尾仍未找到空闲位置,则循环回到表的开头继续探测。

  6. 查找元素:在查找元素时,也需要从哈希值对应的位置开始,按照相同的探测序列查找,直到找到目标元素或遇到一个空闲位置(表示元素不存在)。

  7. 删除元素:删除元素时,不能简单地将位置置为空,因为这会打断查找其他元素的探测序列。通常使用一个特殊的标记(如“已删除”标记)来代替真正的空位。

1.2,二次探测(需要枚举3种状态)
  1. 计算哈希值:首先,使用哈希函数计算键的哈希值,确定它在哈希表中的理论位置。

  2. 发生冲突:如果该位置已被占用,计算下一个探测位置,公式为: 探测位置=(原始位置)+i^2 其中 i 是探测的第几次尝试(i=1,2,3,…)

  3. 探测序列:探测位置是原始哈希值加上 i^2 的结果,这样探测的间隔会随着 i 的增加而增加(1, 4, 9, 16, ...)。

  4. 插入元素:当找到一个空闲位置时,将元素插入到该位置。

  5. 循环探测:如果探测到表尾,继续从表头开始探测,直到找到空闲位置。

  6. 查找元素:查找时,也需要按照相同的探测序列进行查找,直到找到目标元素或确定元素不存在。

  7. 删除元素:与线性探测类似,不能简单地将位置置为空,而是使用一个特殊的标记来表示该位置已被删除。

其他:平衡因子:

哈希的平衡因子,也称为荷载因子(Load Factor),是衡量哈希表性能的一个重要参数。它定义为哈希表中已存储元素的数量与哈希表的总槽位数(即哈希表的大小)之比。荷载因子用以下公式表示

荷载因子=已存储元素的数量​/哈希表的大小

荷载因子反映了哈希表的填充程度,对哈希表的性能有直接影响

二,什么是开散列,就是说

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

下节,我将详细讲解哈希表,已经自实现!,希望帮到大家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/396448.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一款.NET开发的AI无损放大工具

一款.NET开发的AI无损放大工具 思维导航 前言项目功能支持语言系统要求项目源代码项目运行小图片进行无损放大项目源码地址优秀项目和框架精选 前言 今天大姚给大家分享一款由.NET开源&#xff08;GPL-3.0 license&#xff09;、基于腾讯ARC Lab提供的Real-ESRGAN模型开发的A…

Linux知识复习第2期

RHCE 远程登录服务-CSDN博客 Linux 用户和组管理_linux用户和组的管理-CSDN博客 Linux 文件权限详解-CSDN博客 目录 1、sshd 免密登录 (1)纯净实验环境 (2)生成密钥 (3)上锁 2、用户管理 (1)添加新用户 (2)删除用户 (3)修改用户信息 (4)为用户账号设…

【Linux:环境变量】

目录 命令行参数&#xff1a; 环境变量&#xff1a; 命令行参数&#xff1a; argv是一个char*类型的数组&#xff0c;里面存放着字符、字符串的指针地址&#xff0c;且该数组必定是以NULL结尾 命令行中启动的进程都是Bash的子进程&#xff0c;命令行参数的存在本质上就是通过…

[qt] 多线程应用01

源码: 点击此处 一 多线程应用 实现一个多线程的网络时间服务器&#xff0c;利用多线程功能的技术&#xff0c;为每个客户端返回当前的时间&#xff0c;并且在返回后自动退出。同时&#xff0c;服务器也会记录当前受到的请求次数。其实这相当于一个ntp时间服务器 二 服务器实…

职场中,这些事情是禁忌

越级打报告 身处职场&#xff0c;一定要清晰地明确自己所处的位置。要了解部门的运营架构和人事结构&#xff0c;这是身为职场人对自己的最基本的要求。以此确保一旦工作中出现什么问题时&#xff0c;你能找到相应的负责人。但是这里一定要注意&#xff0c;千万不要故作聪明越…

【数据结构】顺序表实现

0. 前言 小伙伴们大家好&#xff0c;从今天开始&#xff0c;我们就开始学习《数据结构》这门课程~ 首先想给大家讲讲什么是数据结构&#xff1f; 0.1 数据结构是什么&#xff1f; 数据结构是由“数据”和“结构”两词组合⽽来。 什么是数据&#xff1f; 比如常⻅的数值1、…

【Material-UI】Button 中的点击事件处理(Handling clicks)详解

文章目录 一、点击事件处理基础1. 基本用法2. 事件处理器的传递 二、实际应用中的注意事项1. 事件处理逻辑的优化2. 避免过多的状态更新3. 使用合适的事件类型 三、关于文档中未提及的原生属性四、最佳实践1. 无障碍性2. 视觉反馈3. 防止重复点击 五、总结 在现代前端开发中&am…

【竞品分析】竞品分析的步骤

在产品经理的工作实际中,对产品的设计离不开竞品分析。 竞品分析可以辅助我们进行可行性评估、制定产品战略、优化产品迭代等。 可以说,竞品分析是贯穿产品生命周期的,是产品经理的必备专业技能。 个人认为&#xff0c;做自己家的产品是单一的视角&#xff0c;多做竞品分析会…

【微信小程序开发】——奶茶点餐小程序的制作(二)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

HTML 元素提供的附加信息--属性 ——WEB开发系列03

HTML 属性是指用于描述 HTML 元素的额外信息&#xff0c;它们提供了元素的特定配置或行为&#xff0c;属性通常包含在 HTML 元素的开始标签中。 元素也可以拥有属性&#xff0c;属性看起来像这样&#xff1a; 属性是元素的附加信息&#xff0c;它们不会显示在实际内容中。在前述…

Hack The Box-Resource

总体思路 phar反序列化->SSH CA私钥泄露->SSH CA私钥滥用->SSH脚本滥用 信息收集&端口利用 nmap -sSVC itrc.ssg.htb目标开放了两个ssh端口和一个80端口&#xff0c;先查看80端口 网站是一个SSG IT资源中心&#xff0c;主要用于解决网站问题、管理 SSH 访问、清…

【学习总结】MySQL篇

MySQL MySQL索引 B树 B树和作为索引&#xff0c;有两个明显特点 一是、他的层级非常低&#xff0c;我们都知道传统的平衡二叉树。它们的阶为2&#xff0c;如果数据量很大&#xff0c;AVL树&#xff08;传统的平衡二叉树&#xff09;的层级就非常深。但是B树&#xff0c;它是…

基于STM32F407+NBIOT+华为云IOT平台设计的环境检测系统

基于STM32F407NBIOT华为云IOT平台设计的环境检测系统实现的功能&#xff1a; 【1】能够采集本地环境的温度、湿度、烟雾浓度&#xff0c;火光信息&#xff0c;在OLED显示屏上显示。 如果检测到烟雾、温度、火光超过阀值会触发蜂鸣器报警。 【2】能够通过NBIOT将本地设备采集的信…

在 Django 表单中传递自定义表单值到视图

在Django中&#xff0c;我们可以通过表单的初始化参数initial来传递自定义的初始值给表单字段。如果我们想要在视图中设置表单的初始值&#xff0c;可以在视图中创建表单的实例时&#xff0c;传递一个字典给initial参数。 1、问题背景 我们遇到了这样一个问题&#xff1a;在使…

解决 MacOS 连接公司 VPN 成功但是不能网络的问题

目录 解决办法2024 Mac mini 爆料 解决办法 操作比较简单&#xff0c;修改配置文件即可&#xff08;如果没有则需要手动创建&#xff09;。 sudo vim /etc/ppp/options在此文件下&#xff0c;加入 plugin L2TP.ppp&#xff1a; plugin L2TP.ppp如果文件里有l2tpnoipsec&…

【SpringBoot系列】接口参数的默认值与必要性

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

结构化输出及其使用方法

在 LLM 应用程序中构建稳健性和确定性 图片来自作者 欢迎来到雲闪世界。OpenAI最近宣布其最新的gpt-4o-2024–08–06模型支持结构化输出。与大型语言模型 (LLM) 相关的结构化输出并不是什么新鲜事——开发人员要么使用各种快速工程技术&#xff0c;要么使用第三方工具。 在本文…

[ACP云计算]组件介绍

一、IaaS、PaaS、SaaS 二、交换机 三、VPC 四、ECS 云服务器ECS&#xff08;Elastic Compute Service&#xff09;是阿里云提供的性能卓越、稳定可靠、弹性扩展的IaaS&#xff08;Infrastructure as a Service&#xff09;级别云计算服务。云服务器ECS免去了您采购IT硬件的前期…

vue项目名修改、webstorm和idea创建的项目重命名、重构项目、修改项目名称

一、需求 就是创建了一个项目&#xff0c;后期需要重命名&#xff0c;怎么办&#xff1f;----> 直接修改&#xff1f;肯定不行&#xff0c;因为里面有些配置也需要修改&#xff0c;假如你只改文件夹名称的话&#xff0c;里面配置都没修改&#xff0c;后期可能会出问题。 二…

完美解决浏览器的输入框自动填入时,黄色背景问题,以及图标被遮住问题(最新)

用图说话↓↓↓ 首先用代码解决黄色背景问题&#xff0c;box-shadow颜色设置透明即可&#xff0c;延时渲染时间可修改为更久 :deep(input:-webkit-autofill) {box-shadow: 0 0 0 1000px transparent !important;/* 浏览器记住密码的底色的颜色 */-webkit-text-fill-color: #f…