自定义实现hashmap-python

news2024/11/20 21:35:20

前文

​ 今天这篇文章给大家讲讲hashmap,这个号称是所有前后端工程师都会的数据结构。

hashmap基本结构

​ hashmap这个数据结构其实并不难,它的结构非常清楚,说白了就是一个定长的链表,这个数组的每一个元素都是一个链表。我们把这个结构画出来,大家一看就明白了。

img

headers是一个定长的数组,数组当中的每一个元素都是一个链表。我们可以遍历这个链表。数组是定长的,但是链表是变长的,所以如果我们发生元素的增删改查,本质上都是通过链表来实现的。

hashmap的get和put算法时间复杂度空间复杂度是多少?

  • get时间复杂度

    get最好情况:O(1)
    - get最坏情况:O(n),即链表查询的时间复杂度
    - get平均:O(1)

  • put时间复杂度

    • put最好情况:O(1)
    • put最坏情况:O(1),即链表尾部插入
    • put平均:O(1)
  • hashmap的空间复杂度

    O(M), M为map元素的个数,因为几乎每多一个元素就多一个空间储存,多一个桶或者在桶内多一个位置。

hashmap完整代码

class Node:
    def __init__(self, key, val, prev=None, next_=None):
        self.key = key
        self.val = val
        self.prev = prev
        self.next_ = next_

    def __repr__(self):
        return str(self.val)


class LinkedList:
    def __init__(self):
        self.head = Node(None, "header")
        self.tail = Node(None, "tail")
        self.head.next_ = self.tail
        self.tail.prev = self.head
        self.size = 0

    def append(self, node):
        prev = self.tail.prev
        node.prev = prev
        node.next_ = prev.next_
        prev.next_ = node
        node.next_.prev = node
        self.size += 1

    def delete(self, node):
        prev = node.prev
        next_ = node.next_
        prev.next_, next_.prev = next_, prev
        self.size -= 1

    def delete_node_by_key(self, key) -> bool:
        node = self.head.next_
        while node != self.tail:
            if node.key == key:
                self.delete(node)
                return True
            node = node.next_
        return False

    def get_list(self):
        ret = []
        cur_node = self.head.next_
        while cur_node != self.tail:
            ret.append(cur_node)
            cur_node = cur_node.next_
        return ret

    def get_node_by_key(self, key):
        cur_node = self.head.next_
        while cur_node != self.tail:
            if cur_node.key == key:
                return cur_node
            cur_node = cur_node.next_
        return None


class HashMap:
    def __init__(self, capacity=16, load_factor=5):
        self.capacity = capacity
        self.load_factor = load_factor
        self.headers = [LinkedList() for _ in range(capacity)]

    def get_hash_key(self, key):
        return hash(key) & (self.capacity - 1)

    def _put(self, key, val):
        linked_list = self.headers[self.get_hash_key(key)]
        if linked_list.size >= self.capacity * self.load_factor:
            self.reset()
            linked_list = self.headers[self.get_hash_key(key)]
        node = linked_list.get_node_by_key(key)
        if node:
            node.val = val
        else:
            linked_list.append(Node(key, val))

    def get(self, key, default=None):
        linked_list = self.headers[self.get_hash_key(key)]
        node = linked_list.get_node_by_key(key)
        if node is None and default:
            return default
        return node

    def __getitem__(self, item):
        if self.get(item):
            return self.get(item)
        raise KeyError("无效的key")

    def __setitem__(self, key, value):
        self._put(key, value)

    def keys(self):
        for head in self.headers:
            for node in head.get_list():
                yield node.key

    def values(self):
        for head in self.headers:
            for node in head.get_list():
                yield node.val

    def items(self):
        for head in self.headers:
            for node in head.get_list():
                yield node.key, node.val

    def setdefault(self, key, default):
        if self.get(key):
            return default
        self._put(key, default)
        return True

    def delete(self, key) -> bool:
        linked_list = self.headers[self.get_hash_key(key)]
        return linked_list.delete_node_by_key(key)

    def reset(self):
        headers = [LinkedList() for _ in range(self.capacity * 2)]
        self.capacity = self.capacity * 2
        for linked_list in self.headers:
            nodes = linked_list.get_list()
            for node in nodes:
                hash_key = self.get_hash_key(node.key)
                linked_list_ = headers[hash_key]
                linked_list_.append(node)
        self.headers = headers


if __name__ == '__main__':
    # 创建字典
    m1 = HashMap()

    # 添加键值对
    m1["name"] = "马亚南"
    m1["age"] = 18

    # 获取键对应的值
    print(m1["name"], m1.get("age"))

    # 获取字典的容量
    # print("capacity", m1.capacity)
    # 1268不会扩容,1269自动扩容,1280是桶分配绝对均匀的情况,也即是说16*80=1280
    # for i in range(1269):
    #     m1[i] = i * 10
    # print("capacity", m1.capacity)

    # 删除元素
    print(m1.delete("name"), "删除成功")
    # print(m1["name"])  # 此语句会抛出KeyError错误
    print(m1.get("name", "默认值-哈哈哈"))

    # setdefault设置,跟python的实现等价
    name = m1.setdefault("name", "王五")
    print(name, "-setdefault")

    # keys
    for key in m1.keys():
        print(key)

    # values
    for val in m1.values():
        print(val)

    # items:
    for key, val in m1.items():
        print(key, val)

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

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

相关文章

AutoForm R11安装说明视频教程

AutoForm R11安装说明视频教程 安装AutoForm.Forming.R11.Win64_01在安装过程中选择安装许可证服务器并输入许可证服务器2375localhost和完成 安装 -继续解文件夹复制2个文件并在C:\Program files\RLM_v14.1BL3上替换它们作为管理员运行RLM.exe 确保您的系统环境如下所示&a…

(unordered)map和set封装(底层红黑树)

map和set封装 文章目录 map和set封装设计问题(知其所以然)为什么要对iterator进行封装?为什么要引入Self Ref Ptr这些模板参数?为什么是试图从non_const转变为const,而不是const转为non_const如何解决 为什么说能加con…

vscode 乱码解决

windows 10 系统 vs code 编译运行和调试 C/C_vscode windows编译_雪的期许的博客-CSDN博客 VS Code默认文件编码时UTF-8,这对大多数情况是没有问题的,却偏偏对C/C有问题。如果以UTF-8编码保存C/C代码,那么只能输出英文,另外使用…

【Java-LangChain:使用 ChatGPT API 搭建系统-4】评估输入-分类

第三章,评估输入-分类 如果您正在构建一个允许用户输入信息的系统,首先要确保人们在负责任地使用系统,以及他们没有试图以某种方式滥用系统,这是非常重要的。 在本章中,我们将介绍几种策略来实现这一目标。 我们将学习…

优先级队列--合并多个有序链表

如果本文章有不懂的,请进入预习链接:优先级队列_加瓦不加班的博客-CSDN博客 这道题目之前解答过,现在用刚学的优先级队列来实现一下 题目中要从小到大排列,因此选择用小顶堆来实现,自定义小顶堆如下 //小顶堆的操作与…

图像处理与计算机视觉--第五章-图像分割-Canny算子

文章目录 1.边缘检测算子分类2.Canny算子核心理论2.1.Canny算子简单介绍2.2.Canny算子边缘检测指标2.3.Canny算子基本原理 3.Canny算子处理流程3.1.高斯滤波去噪声化3.2.图像梯度搜寻3.3.非极大值抑制处理3.4.双阈值边界处理3.5.边界滞后技术跟踪3.6.Canny算子边缘检测的特点 4…

Linux使用之xshell、xftp保姆教学(含安装包,详细使用方法,连接失败解决方法)

前言 链接: FTP,SSH服务器介绍 这是我之前写的一篇博客,其中介绍了Ubuntu操作系统的一些常用命令以及服务器介绍,这篇文章就向大家详细介绍如何安装及应用这些服务器,我以xshell、xftp为例。 安装包,使用方法&#xf…

认识柔性数组

在C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员 限制条件是: 结构体中最后一个成员未知大小的数组 1.柔性数组的形式 那么我们怎样写一个柔性数组呢 typedef struct st_type {int i;int a[0];//柔性数组成员 }ty…

GD32F103x IIC通信

1. IIC通信 1.IIC的介绍 IIC总线有两条串行线,其一是时钟线SCK(同步),其二是数据线SDA。只有一条数据线属于半双工。应用中,单片机常常作为主机,外围器件可以挂载多个。(当然主机也可以有多个。…

【小程序 - 加强】自定义组件、使用npm包、全局数据共享、分包_05

目录 一、自定义组件 1. 组件的创建与引用 1.1 创建组件 1.2 引用组件 1.2.1 局部引用组件 1.2.2 全局引用组件 1.2.3 全局引用 VS 局部引用 1.2.4 组件和页面的区别 2. 样式 2.1 组件样式隔离 2.2 组件样式隔离的注意点 2.3 修改组件的样式隔离选项 2.4 styleIso…

stable diffusion学习笔记【2023-10-2】

L1:界面 CFG Scale:提示词相关性 denoising:重绘幅度 L2:文生图 女性常用的负面词 nsfw,NSFW,(NSFW:2),legs apart, paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, (…

位置编码器

目录 1、位置编码器的作用 2、代码演示 (1)、使用unsqueeze扩展维度 (2)、使用squeeze降维 (3)、显示张量维度 (4)、随机失活张量中的数值 3、定义位置编码器类,我…

SpringBoot 可以同时处理多少请求

一、前言 首先,在Spring Boot应用中,我们可以使用 Tomcat、Jetty、Undertow 等嵌入式 Web 服务器作为应用程序的运行容器。这些服务器都支持并发请求处理的能力。另外,Spring Boot 还提供了一些配置参数,可以对 Web 服务器进行调…

传输层协议——TCP、UDP

目录 1、UDP 协议(用户数据报协议) 协议特点 报文首部格式 2、TCP 协议(传输控制协议) 协议特点 报文首部格式 TCP连接建立时的三次握手 TCP拆除连接的四次挥手 TCP的流量控制 TCP的拥塞控制 3、传输层端口号 三类端口…

一维数组和二维数组的使用(int类型)

目录 导读1. 一维数组1.1 一维数组的创建1.2 数组的初始化1.3 一维数组的使用1.4 一维数组在内存中的存储 2. 二维数组2.1 二维数组的创建2.2 二维数组的初始化2.3 二维数组的使用2.4 二维数组在内存中的存储 博主有话说 导读 本篇主要讲解一维数组和二维数组的创建和使用&…

【软考】4.2 关系代数

《 关系代数 》 表和表之间的逻辑运算 笛卡尔积:S1 x S2 投影:π;选择某一列(属性);一个关系R的投影操作结果也是一个关系,记作Πa,它由从关系R中选出的A列元素构成;选择…

React18入门(第二篇)——React18+Ts项目配置husky、eslint、pretttier、commitLint

前言 我的项目版本如下: React: V18.2.0Node.js: V16.14.0TypeScript:最新版工具: VsCode 本文将采用图文详解的方式,手把手带你快速完成在React项目中配置husky、prettier、commitLint,实现编码规范的统…

OpenNebula的配置与应用(一)

学习了OpenNebula的安装之后,接下来就是配置OpenNebula,内容包括配置Sunstone,VDC和集群,设置影像,模板管理,虚拟机管理等。OpenNebula还有大量的工作要做,这些工作主要来自映像、模板和虚拟机管…

[架构之路-225]:计算机硬件与体系结构 - 分类方法大汇总: RISC, CISC

目录 一、分类方法汇总 二、指令流和数据流的关系分类 三、Flynn 分类:指令并行处理 四、根据指令集架构(ISA)分类 4.1 分类 4.2 开源的RISC-V与封闭的RISC指令集架构比较 4.3 RISC-V的演进路径 4.4 RISC-V与中国芯片自研 4.4 五阶流…

服务网关Gateway_入门案例

创建cloud-gateway-gateway9527工程 pom文件引入依赖 <dependencies><!-- 引入网关Gateway依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></depe…