【漫画算法】哈希表:古代皇帝的秘密魔法书

news2024/10/5 16:50:13

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容,和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣!

  • 推荐:数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注
    在这里插入图片描述

  • 导航

    • LeetCode解锁1000题: 打怪升级之旅:每题都包括3-5种算法,以及详细的代码实现,刷题面试跳槽必备
    • 漫画版算法详解:通过漫画的形式和动态GIF图片把复杂的算法每一步进行详细可视解读,看一遍就掌握
    • python源码解读:解读python的源代码与调用关系,快速提升代码质量
    • python数据分析可视化:企业实战案例:企业级数据分析案例与可视化,提升数据分析思维和可视化能力
    • 程序员必备的数学知识与应用:全面详细的介绍了工程师都必备的数学知识

期待与您一起探索技术、持续学习、一步步打怪升级 欢迎订阅本专栏❤️❤️

引言

在一个神秘的魔法世界里,有一本充满魔力的书,能够快速找到任何被记录的信息。这本书的秘密就在于它使用了一种叫做“哈希表”的魔法算法。本文将通过一个有趣的故事和简洁的漫画,向大家介绍哈希表的基本原理及其实现。

故事背景

在魔法王国中,皇帝拥有一本强大的魔法书,记录着王国里每个居民的信息。每当皇帝需要查找某个居民的信息时,他都能迅速找到。这本魔法书之所以如此强大,是因为它使用了哈希表这一神奇的算法。

角色介绍

  • 皇帝:魔法国王的智慧皇帝,掌握哈希表的秘密。
  • 大臣:国王的得力助手,听从皇帝解释和管理哈希表。
  • 居民:魔法王国的居民,他们的信息被记录在魔法书中。

故事展开

一天,皇帝决定向他的子民们揭示魔法书的秘密。他召集了所有大臣,并开始解释哈希表的原理。
在这里插入图片描述

漫画情节

场景1:国王解释哈希表的基本原理

皇帝展开一卷古老的卷轴,上面刻画着神秘的符号。他对大臣们说道:“这本魔法书之所以能快速找到任何居民的信息,秘密就在于一种叫做哈希表的算法。让我来解释其中的奥秘。”

皇帝继续说道:“首先,每个居民的名字(键)都会通过一个神奇的公式(哈希函数)转换成一个特定的数字(哈希值)。这个数字决定了信息在魔法书中的位置(索引)。例如,当我们插入‘Alice’的电话时,哈希函数会将她的名字转换为数字8,这样她的信息就会存储在索引8的位置。”

“但是,有时候不同的名字可能会被转换成相同的数字,这就是所谓的哈希冲突。比如,当我们插入‘Eve’的电话时,她的名字也被转换成了数字8。为了处理这种冲突,我们会在索引8的位置建立一个链表,将所有冲突的键值对串联起来。”

皇帝用手指着卷轴上的图示:“看,这里是我们目前的哈希表结构。”
插入键值对:居民名字与对应的电话号码。

  1. 插入 “Alice” 的电话:12345
  2. 插入 “Bob” 的电话:67890
  3. 插入 “Charlie” 的电话:54321
  4. 插入 “Dave” 的电话:98765
  5. 插入 “Eve” 的电话:11111

在这里插入图片描述

场景2:哈希函数的作用

在大殿内,皇帝继续向大臣们讲解哈希表的奥秘,重点介绍了哈希函数的作用。

哈希函数的定义

皇帝展开手中的卷轴,上面描绘着一个复杂而神秘的公式。他说道:“哈希函数是哈希表的核心,它的作用是将输入的键(比如居民的名字)转换成一个固定范围内的整数(哈希值)。这个整数决定了键值对在哈希表中的存储位置。”

哈希函数的作用

  1. 将键映射到索引
    皇帝用手指着卷轴上的公式,解释道:“通过哈希函数,我们可以将每个键映射到一个特定的索引位置。比如,名字‘Alice’通过哈希函数计算得到哈希值‘8’,所以她的信息将存储在索引‘8’的位置。”

  2. 均匀分布键值对
    皇帝继续说道:“哈希函数的设计目的是尽可能均匀地分布键值对在哈希表中,避免出现太多的哈希冲突。这有助于提高查找和插入操作的效率。”

  3. 快速查找
    “当我们需要查找某个键对应的值时,比如查找‘Charlie’的电话,只需通过哈希函数计算出哈希值‘6’,然后直接访问索引‘6’的位置,就能快速找到他的电话号码‘54321’。”

  4. 处理哈希冲突
    皇帝解释道:“尽管哈希函数的设计尽量避免冲突,但在某些情况下,不同的键可能会映射到相同的索引位置,这就是哈希冲突。比如,‘Alice’和‘Eve’都映射到索引‘8’。我们可以通过链表法将这些冲突的键值对存储在同一个索引位置的链表中。”

示例

皇帝举了一个例子来说明哈希函数的作用:

大臣们根据皇帝的指示,开始往魔法书(哈希表)中插入居民的信息。

插入 Alice 的电话:12345

  • 哈希值 = (A:65 + l:108 + i:105 + c:99 + e:101) % 10 = 478 % 10 = 8
  • 将 Alice 和她的电话号码插入到索引 8 的位置。

插入 Bob 的电话:67890

  • 哈希值 = (B:66 + o:111 + b:98) % 10 = 275 % 10 = 5
  • 将 Bob 和他的电话号码插入到索引 5 的位置。

插入 Charlie 的电话:54321

  • 哈希值 = (C:67 + h:104 + a:97 + r:114 + l:108 + i:105 + e:101) % 10 = 696 % 10 = 6
  • 将 Charlie 和他的电话号码插入到索引 6 的位置。

插入 Dave 的电话:98765

  • 哈希值 = (D:68 + a:97 + v:118 + e:101) % 10 = 384 % 10 = 4
  • 将 Dave 和他的电话号码插入到索引 4 的位置。

插入 Eve 的电话:11111

  • 哈希值 = (E:69 + v:118 + e:101) % 10 = 288 % 10 = 8
  • 发现索引 8 已经被 Alice 占用,发生哈希冲突。使用链表法将 Eve 添加到索引 8 的链表中。

皇帝总结道:“哈希函数的主要作用是将键转换成固定范围内的整数,从而确定键值对在哈希表中的存储位置。通过合理设计哈希函数,我们可以有效地分配存储空间,提高查找和插入操作的效率,并处理哈希冲突。”

大臣们听完后纷纷点头称赞,深刻理解了哈希函数在哈希表中的重要作用和工作原理。

场景3:处理哈希冲突

皇帝继续说道:“哈希表虽然高效,但有时会遇到哈希冲突。今天,我将讲解如何处理这些冲突。”

哈希冲突的产生

当不同的键映射到相同的索引时,就会发生哈希冲突。例如:

  • Alice:哈希值 8
  • Eve:哈希值 8

解决哈希冲突的方法

皇帝解释了几种常用的方法来处理哈希冲突:

  1. 链地址法(Separate Chaining)

    • 在每个索引位置维护一个链表,将冲突的键值对存储在链表中。
    • 优点:实现简单,冲突处理灵活。
    • 缺点:需要额外的存储空间来维护链表。

    示例

    Index  |  Key                |  Value
    ------------------------------------
      0    |                     |       
      1    |                     |       
      2    |                     |       
      3    |                     |       
      4    | Dave                |  98765
      5    | Bob                 |  67890
      6    | Charlie             |  54321
      7    |                     |       
      8    | Alice -> Eve        | 12345 -> 11111
      9    |                     |       
    
  2. 开放地址法(Open Addressing)

    • 当发生冲突时,寻找下一个空闲的位置来存储键值对。
    • 优点:不需要额外的存储空间。
    • 缺点:查找和插入的时间复杂度可能增加。

    常用的开放地址法包括

    • 线性探测(Linear Probing):按顺序查找下一个空闲位置。
    • 二次探测(Quadratic Probing):按平方序列查找空闲位置。
    • 双重哈希(Double Hashing):使用第二个哈希函数查找空闲位置。

通过这些方法,哈希表可以有效地处理哈希冲突,确保数据能够正确存储和快速查找。

哈希表的实现

class Node:
    def __init__(self, key, value):
        self.key = key
        self.value = value
        self.next = None

class HashTable:
    def __init__(self):
        self.size = 10
        self.table = [None] * self.size

    def hash_function(self, key):
        return sum(ord(char) for char in key) % self.size

    def insert(self, key, value):
        index = self.hash_function(key)
        new_node = Node(key, value)
        if self.table[index] is None:
            self.table[index] = new_node
        else:
            current = self.table[index]
            while current.next:
                if current.key == key:
                    current.value = value
                    return
                current = current.next
            if current.key == key:
                current.value = value
            else:
                current.next = new_node

    def search(self, key):
        index = self.hash_function(key)
        current = self.table[index]
        while current:
            if current.key == key:
                return current.value
            current = current.next
        return None

# 使用示例
hash_table = HashTable()
hash_table.insert("Alice", 12345)
hash_table.insert("Bob", 67890)
hash_table.insert("Charlie", 54321)
hash_table.insert("Dave", 98765)
hash_table.insert("Eve", 11111)

print("查找 'Alice' 的电话:", hash_table.search("Alice"))  # 输出: 12345
print("查找 'Eve' 的电话:", hash_table.search("Eve"))    # 输出: 11111

查找操作

查找 Alice 的电话号码

  1. 计算哈希值:hash_function(“Alice”) = 8
  2. 在索引 8 查找键值对:
  • 发现 Alice 的键,返回电话号码 12345。
    查找 Eve 的电话号码
  1. 计算哈希值:hash_function(“Eve”) = 8
  2. 在索引 8 查找键值对:
  • 发现 Alice 的键,不匹配,继续下一个节点。
  • 发现 Eve 的键,匹配,返回电话号码 11111。

哈希表的优点

  1. 快速查找:通过哈希函数,能够快速定位信息的位置。
  2. 灵活存储:适用于各种类型的数据存储和查找。
  3. 高效管理:在处理大量数据时,哈希表能够保持高效的性能。

以下是哈希表、数组和链表的优缺点及时间复杂度的比较表格:

数据结构优点缺点查找时间复杂度插入时间复杂度删除时间复杂度
哈希表- 平均情况下查找、插入和删除操作非常快,时间复杂度为 O(1)
- 动态扩展,适合处理大量数据
- 不需要有序
- 可能会发生哈希冲突,最坏情况下查找、插入和删除的时间复杂度为 O(n)
- 需要额外的空间来存储哈希函数和链表
- 适用于等值查找,不适用于区间查找
平均 O(1)
最坏 O(n)
平均 O(1)
最坏 O(n)
平均 O(1)
最坏 O(n)
数组- 支持快速随机访问,时间复杂度为 O(1)
- 存储结构简单,内存连续
- 插入和删除操作效率低,需要移动大量数据,时间复杂度为 O(n)
- 固定大小,动态调整需要重新分配内存
O(1)O(n)O(n)
链表- 动态大小,可以高效地进行插入和删除操作,时间复杂度为 O(1)
- 内存利用率高,不需要预分配空间
- 不支持高效的随机访问,查找时间复杂度为 O(n)
- 需要额外的内存存储指针,导致空间开销较大
O(n)O(1)O(1)

总结

通过这个故事,我们了解了哈希表的基本原理和实现方式。哈希表通过哈希函数快速定位数据,提高了查找效率,并且可以有效处理冲突。希望这篇文章和漫画能帮助大家更好地理解哈希表的工作原理。如果你对算法有更多的兴趣,欢迎关注我,了解更多有趣的算法故事!

🌹🌹如果觉得这篇文对你有帮助的话,记得一键三连关注、赞👍🏻、收藏是对作者最大的鼓励,非常感谢 ❥(^_-)

❤️❤️关注公众号 数据分析螺丝钉 回复 学习资料 领取高价值免费学习资料❥(^_-)
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何确保大模型 RAG 生成的信息是基于可靠的数据源?

在不断发展的人工智能 (AI) 领域中,检索增强生成 (RAG) 已成为一种强大的技术。 RAG 弥合了大型语言模型 (LLM) 与外部知识源之间的差距,使 AI 系统能够提供更全面和信息丰富的响应。然而,一个关键因素有时会缺失——透明性。 我们如何能够…

Android窗口管理

一 概述 本篇文章主要讲 Window、WindowManager、WindowManagerService 三者之间的关系及其运行机制。总的来说 Window 表示的是一种抽象的功能集合,具体实现为 PhoneWindow。WindowManager 是外界访问 Window 的入口,对 Window 的访问必须通过 Window…

状态转换图

根据本章开头讲的结构化分析的第3条准则,在需求分析过程中应该建立起软件系统的行为模型。状态转换图(简称为状态图)通过描绘系统的状态及引起系统状态转换的事件,来表示系统的行为。此外,状态图还指明了作为特定事件的结果系统将做哪些动作(例如,处理数据)。因此,状态图提供了…

【轻松压缩,一键搞定】您的图片处理神器来了!

压图地址 一个功能强大的图片处理工具,它可以满足用户对于图片压缩、格式转换、质量调节以及长图片分割等多种需求。 【轻松压缩,一键搞定】您的图片处理神器来了! 🎉 您是否曾为图片太大无法上传而烦恼?是否为图片格…

rk3568_atomic

文章目录 前言一、atomic是什么?二、原子操作API函数1.atomic原子操作2.原子位操作API三、atomic驱动实验总结前言 本文记录的是正点原子rk3568开发板的atomic实验 一、atomic是什么? 不同的线程在进行读写的过程中,可能会冲突乱入,导致会有预想不到的结果。所以为了让数…

信息系统项目管理师0131:输出(8项目整合管理—8.7监控项目工作—8.7.3输出)

点击查看专栏目录 文章目录 8.7.3 输出8.7.3 输出 工作绩效报告工作绩效信息可以用实体或电子形式加以合并、记录和分发。基于工作绩效信息,以实体或电子形式编制形成工作绩效报告,以制定决策、采取行动或引起关注。根据项目沟通管理计划,通过沟通过程向项目干系人发送工作绩…

文档档案管理系统整体建设方案书(实际项目原件word2024)

1.系统概述 1.1.需求描述 1.2.需求分析 1.3.重难点分析 1.4.重难点解决措施 2.系统架构设计 2.1.系统架构图 2.2.关键技术 数据备份技术 3.系统功能设计 3.1.功能清单列表 3.2.基础数据管理 3.3.位置管理 3.4.文档使用 3.5.文档管理 软件全套资料包获取方式①:软件项…

[windows系统安装/重装系统][step-4][番外篇-2]N卡驱动重装 |解决:开机几小时后电脑卡顿 | 后台自动运行了上千个Rundll32进程问题

现象 开机几小时后,电脑变卡,打开后台管理器都卡,后台管理去转圈圈一小会儿后看到后台进程上千个,好多个Rundll32进程 重启下运行会稍快 重启后运行快,后台管理器反应也快 打开后台管理器不卡(几小时后打…

Hadoop3:HDFS中DataNode与NameNode的工作流程

一、DataNode中的数据情况 数据位置 /opt/module/hadoop-3.1.3/data/dfs/data/current/BP-823420375-192.168.31.102-1714395693863/current/finalized/subdir0/subdir0块信息 每个块信息,由两个文件保存,xxx.meta保存的是数据长度、校验和、时间戳&am…

基于Java、SpringBoot和uniapp在线考试系统安卓APP和微信小程序

摘要 基于Java、SpringBoot和uniapp的在线考试系统安卓APP微信小程序是一种结合了现代Web开发技术和移动应用技术的解决方案,旨在为教育机构提供一个方便、高效和灵活的在线考试平台。该系统采用Java语言进行后端开发,使用SpringBoot框架简化企业级应用…

netmask一键修改子网掩码(KALI工具系列八)

目录 1、KALI LINUX简介 2、netmask工具简介 3、在KALI中使用netmask 3.1 目标主机IP(win) 3.2 KALI的IP 4、命令示例 4.1 查看版本 4.2 修改etho的子网掩码 4.3 查看状态信息 4.4 查看子网掩码 4.5 查看范围 4.6 DNS查看 5.、总结 1、KAL…

【实际项目精选源码】ehr人力资源管理系统实现案例(java,vue)

一、项目介绍 一款全源码可二开,可基于云部署、私有部署的企业级数字化人力资源管理系统,涵盖了招聘、人事、考勤、绩效、社保、酬薪六大模块,解决了从人事招聘到酬薪计算的全周期人力资源管理,符合当下大中小型企业组织架构管理运…

Redis可视化工具:Another Redis Desktop Manager下载安装使用

1.Github下载 github下载地址: Releases qishibo/AnotherRedisDesktopManager GitHub 2. 安装 直接双击exe文件进行安装 3. 连接Redis服务 先启动Redis服务,具体启动过程可参考: Windows安装并启动Redis服务端(zip包&#xff09…

Linux -- 进程间通信的五种方式

IPC(InterProcess Communication)的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中Socket和Stream支持不同主机上的两个进程IPC。 管道(Pipes)&#xff1a…

618快到了,送大家一款自动化脚本工具,一起薅羊毛

前言 一年一次的618活动来了,大家做好准备了,奇谈君为大家准备好用的618神器,解放双手,简单操作就可以把红包拿到手。 京淘自动助手 首次使用前需要进行设置 将手机的无障碍权限和悬浮窗权限打开 设置完成后,可以把…

编程实战:类C语法的编译型脚本解释器(四)总入口和使用方法

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 系列入口: 编程实战…

深入了解数据库设计中的规范化与反规范化

目录 零、前言 一、一些基本术语 二、关系模式 2.1. 什么是关系模式 2.2. 示例 三、数据依赖 3.1. 函数依赖 3.1.1. 完全函数依赖 3.1.2. 部分函数依赖 3.1.3. 传递函数依赖 3.2. 多值依赖 3.3. 连接依赖 四、规范化 4.1. 第一范式(1NF) …

Redis 性能管理

一、Redis 性能管理 #查看Redis内存使用 172.168.1.11:6379> info memory 1. 内存碎片率 操作系统分配的内存值 used_memory_rss 除以 Redis 使用的内存总量值 used_memory 计算得出。内存值 used_memory_rss 表示该进程所占物理内存的大小,即为操作系统分配给…

批量化处理和矩阵(torch)

左边是权重,右边是变量 高维可以看成二维的堆叠 总结:二维是一维的堆叠,三维是二维的堆叠。但似乎是为了引入矩阵,本来应该是左上角是第一组权重和第一组变量的乘积这种表示表示来着,最后成了和列向量乘积&#xff…

Python | Leetcode Python题解之第111题二叉树的最小深度

题目: 题解: class Solution:def minDepth(self, root: TreeNode) -> int:if not root:return 0que collections.deque([(root, 1)])while que:node, depth que.popleft()if not node.left and not node.right:return depthif node.left:que.appen…