哈希表(散列表)基础概念与经典题目(Leetcode题解-Python语言)之上——原理与设计

哈希表Hash table,也叫散列表),是根据键(Key)而直接访问数据在内存中的储存位置(又叫做存储桶Buckets)的数据结构。也就是说,它通过计算一个关于键值的函数(哈希函数Hash function,也叫散列函数),将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度,存放记录的数组就叫做哈希表。最直观的例子,我们的手机通讯录中,如果王某某的电话号码是12345678910,我们只需要记个王某某:12345678910,以后直接找王某某就可以找到他的电话号码了。哈希表可以在O(1)时间内进行读写操作(插入与搜索),就是最大的优势。(leetcode对应章节)

哈希表的关键思想是使用哈希函数将键映射到存储桶

1.当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
2.当我们搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。

如果两个不同的键经过哈希函数映射之后,得到的值相同,这叫做哈希冲突或者碰撞(collision),解决方法后面再讲。

如何设计一个哈希表呢?最重要的肯定是哈希函数,完美的哈希函数是键与桶一 一对应,没有任何冲突或者空间浪费的,然而实际基本不可能达到。哈希表又分为两种:哈希集合哈希映射,所以设计题就分为设计哈希集合和设计哈希映射。

705. 设计哈希集合

集合是用来存储非重复值的数据结构,操作就三种:插入、查找(是否存在)、删除。做设计题必须留意键key的取值范围,此处为0到10的六次方,因此会有对于桶数(数组的长度)与桶大小(数组中一个格子的大小)的权衡。

最极端的,开辟一个超大数组(桶数为10的六次方、桶大小为1),方案如下:

class MyHashSet:def __init__(self):"""Initialize your data structure here."""self.HashSet = [False] * 1000001def add(self, key: int) -> None:self.HashSet[key] = Truedef remove(self, key: int) -> None:self.HashSet[key] = Falsedef contains(self, key: int) -> bool:"""Returns true if this set contains the specified element"""return self.HashSet[key]

初始化数组每一格都是False,插入就是对应格变为True,查找就是对应格的值(True or False),删除就是对应格变为False。

如果希望平衡桶数与桶大小相同,可以得到以下方案:

class MyHashSet:def __init__(self):self.table = [[0] * 1000 for _ in range(1001)] # 注意是1001def hash(self, key):return key // 1000, key % 1000  # 获取key在数组中的位置索引def add(self, key):hashkey, hashpos = self.hash(key)  # 获取key在数组中的位置索引self.table[hashkey][hashpos] = 1def remove(self, key):hashkey, hashpos = self.hash(key)self.table[hashkey][hashpos] = 0def contains(self, key):hashkey, hashpos = self.hash(key)return (self.table[hashkey] != []) and (self.table[hashkey][hashpos] == 1)

显然,这里我们开辟了一个长度 length 为1000的数组,哈希函数为取整除,冲突的解决方法是让一个格子不止存放一个 key,而是用数组把所有可能冲突的 key 都存放进来,只需要桶数(1000) * 桶大小(1001)的结果大于10的六次方即可。

hashkey是key经过哈希函数(取整除)映射后的值,可能会重复(冲突),用于确定key存放在哪个桶;

hashpos是key经过取余数后的值,可知不同的key得到相同的整除值后,它的余数一定是不同的,所以可以确定key存放在某一个桶中的哪个位置。

706. 设计哈希映射

映射是用来存储 (key, value) 键值对的数据结构,设计思路与集合类似,如下:

class MyHashMap(object):def __init__(self):self.map = [[-1] * 1000 for _ in range(1001)] # 注意是1001def hash(self, key):return key // 1000, key % 1000  # 获取key在数组中的位置索引def put(self, key, value):hashkey, hashpos = self.hash(key)self.map[hashkey][hashpos] = valuedef get(self, key):hashkey, hashpos = self.hash(key)return self.map[hashkey][hashpos]def remove(self, key):hashkey, hashpos  = self.hash(key)self.map[hashkey][hashpos] = -1

这里我使用的哈希函数还是取整除,桶数还是1001,桶大小还是1000。

注意:之所以是 range(1001) 而不是 1000 的原因是10的六次方除以 1000 会得到 1000,而 range(1000) 最大是999,导致 1000 溢出。

下面是更好的方法!!

705. 设计哈希集合

class MyHashSet:def __init__(self):self.buckets = 1001self.table = [[] for _ in range(self.buckets)]def hash(self, key):return key % self.bucketsdef add(self, key: int) -> None:hash_key = self.hash(key)if key in self.table[hash_key]:returnself.table[hash_key].append(key)def remove(self, key: int) -> None:hash_key = self.hash(key)if key not in self.table[hash_key]:returnself.table[hash_key].remove(key)def contains(self, key: int) -> bool:hash_key = self.hash(key)return key in self.table[hash_key]

只需要设计哈希函数找到桶的位置即可,让桶作为一个空的列表,里面的元素动态增加。

706. 设计哈希映射

class MyHashMap:def __init__(self):self.buckets = 1001self.table = [[] for _ in range(self.buckets)]def hash(self, key):return key % self.bucketsdef put(self, key: int, value: int) -> None:hash_key = self.hash(key)for item in self.table[hash_key]:if key == item[0]:item[1] = valuereturnself.table[hash_key].append([key, value])def get(self, key: int) -> int:hash_key = self.hash(key)for item in self.table[hash_key]:if key == item[0]:return item[1]return -1def remove(self, key: int) -> None:hash_key = self.hash(key)for i, item in enumerate(self.table[hash_key]):if key == item[0]:self.table[hash_key].pop(i)return

同理

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

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

相关文章

python编程中的小问题汇总

前言 本文记录了我在python编程中遇到的各种小问题,持续更新。 1. x x 1 VS x 1 辨析下面这两段代码: >>> x y [1, 2, 3, 4] >>> x [4] >>> x [1, 2, 3, 4, 4] >>> y [1, 2, 3, 4, 4]>>> x y …

都在讨论高并发,结果连并发量、TPS、QPS都分不清

“ 年年岁岁跳槽季,回回必问高并发!原因很简单,因为高并发能牵扯出太多问题,接口响应超时、CPU负载升高、GC频繁、死锁、大数据量存储等,能考察求职者的真实情况。而很多人在第一步就倒下了!因为对数据化的…

哈希表(散列表)基础概念与经典题目(Leetcode题解-Python语言)之中——实际应用

上一节介绍了哈希表的原理与设计方法,这一节则直接python中现有的哈希表类型:哈希集合 set(集合)和哈希映射 dict(字典)来解决实际应用(刷题)。 零、概念 在介绍实际应用之前&#…

leetcode518. 零钱兑换 II

一:题目 二:上码 class Solution { public:/**思路:1.分析题意这个满足答案的结果有很多种,所以我们可以用动态规划去做,那么题意中我们可以知道的是我们是可以输入一种面值的时候,我们是可以重复输入的,那么这就是背包类型中的完全背包了2.动态规划5步…

跟我一起学.NetCore之选项(Options)核心类型简介

前言.NetCore中提供的选项框架,我把其理解为配置组,主要是将服务中可供配置的项提取出来,封装成一个类型;从而服务可根据应用场景进行相关配置项的设置来满足需求,其中使用了依赖注入的形式,使得更加简单、…

哈希表(散列表)基础概念与经典题目(Leetcode题解-Python语言)之下——设计键

在很多应用中,我们会发现某种映射关系(模式),但它并不是简单一 一对应的。这时,我们就要从键 key 入手,通过设计合适的键,建立映射关系。leetbook的这个章节总结了一些常见的键,以供…

《ASP.NET Core项目开发实战入门》送书活动结果公布

截至2020.09.20 本次送书活动《ASP.NET Core项目开发实战入门》。下面把Top 5的留言截图给大家回顾一下。以下5位同学将获赠书籍一本:小林子鉴静红脸先生阿星Plus以上同学请在2020年9月25日24:00之前加小二微信领取赠书,超过时间视为放弃。小…

二分查找基础概念与经典题目(Leetcode题解-Python语言)二分索引型

二分查找的定义如下(引自Wiki): 在计算机科学中,二分查找算法(英语:binary search algorithm),也称折半搜索算法(英语:half-interval search algorithm&…

Magicodes.IE 2.3重磅发布——.NET Core开源导入导出库

在2.3这一版本的更新中,我们迎来了众多的使用者、贡献者,在这个里程碑中我们也添加并修复了一些功能。对于新特点的功能我将在下面进行详细的描述,当然也欢迎更多的人可以加入进来,再或者也很期待大家来提issues或者PR&#xff0c…

听说用 C# 写 TensorFlow 更高效?

经过半年呕心沥血的努力,SciSharp STACK终于把Tensorflow .NET绑定升级到可以使用 tensorflow 2.3, 新版本最大的优势是实现了Eager模式, 这个特性是让.NET C#/ F#成为机器学习模型开发工具的重要前置条件。NugGet包下载:https://www.nuget.org/packages/TensorFlow…

leetcode279. 完全平方数

一:题目 二:上码 class Solution { public:/**思路:1.分析题意这个就是将一个数分成几个数的和;然而的话,这几个数必须的是完全平方数,我们要求的是最少数量的完全平方数这个满足答案的有好几个;但是我们要求的是最少的数量2.动态规划五步走1>:确定dp数组的含义以及下标的含…

岛屿类问题的广度优先深度优先双解法(Leetcode题解-Python语言)

695. 岛屿的最大面积 先上最经典的题目,详细思路看这题的官方题解,简单来说的岛屿问题就是遍历二维数组,一般都是从一块陆地开始,进行深度优先或者广度优先搜索,每次上下左右四个方向选其一然后寻找下一块陆地&#x…

跟我一起学.NetCore之Options实例演示及分析

前言来啦!来啦!上一节一堆代码,是不是感觉甚是无味啊?没关系,这里结合上一节内容专注举例演示,绝不废话!走起~~~~~正文老规矩,一个WebApi项目走起,项目结构如下&#xff…

leetcode139. 单词拆分

一:题目 二:上码 class Solution { public:/**思路:1.分析题意单词就是物品;字符串就是背包;单词能否组成字符串就是在问,物品能不能将背包装满单词可以重复使用那么说明这是一个完全背包2.动态规划五步走1>:确定dp数组的与下标的含义(这里用下标i是由我们的遍历…

二分查找基础概念与经典题目(Leetcode题解-Python语言)二分数值型

二分查找的讲解请见上一篇文章。本文主要记录对数值进行二分的题目解法与思路。 374. 猜数字大小 class Solution:def guessNumber(self, n: int) -> int:left 1right nwhile left < right:mid left (right - left) // 2if guess(mid) 1: # mid < pickleft mi…

大数据下的质量体系建设

一、背景大数据、人工智能是当前也是未来几年IT部门的重点建设方向&#xff0c;新的技术可以为业务突破盈利瓶颈&#xff0c;带来新的增长点&#xff0c;同时我们也发现数据中台也频频在最近的企业财报予以体现&#xff0c;相关的技术岗位需求也是供不应求&#xff0c;与之形成…

Pandas中的 transform() 结合 groupby() 用法示例

首先&#xff0c;假设我们有如下餐厅数据集&#xff1a; import pandas as pddf pd.DataFrame({restaurant_id: [101,102,103,104,105,106,107],address: [A,B,C,D, E, F, G],city: [London,London,London,Oxford,Oxford, Durham, Durham],sales: [10,500,48,12,21,22,14] })…

跟我一起学.NetCore之日志(Log)模型核心

前言鲁迅都说&#xff1a;没有日志的系统不能上线(鲁迅说&#xff1a;这句我没说过&#xff0c;但是在理)&#xff01;日志对于一个系统而言&#xff0c;特别重要&#xff0c;不管是用于事务审计&#xff0c;还是用于系统排错&#xff0c;还是用于安全追踪.....都扮演了很重要的…

Numpy中数组创建函数的辨析

首先推荐Numpy官方的教程&#xff0c;网址。 很多人会对数组创建函数的参数中什么时候要用括号np.zeros((2, 3))&#xff0c;什么时候不用括号np.eye(3, 5)感到疑惑&#xff0c;这里对它们统一进行梳理。&#xff08;按照官方文档的分类方法&#xff09; 1. 一维数组创建函数…

leetcode213. 打家劫舍 II

一:题目 二:上码 class Solution { public:/**思路:1.既然成环了,我们如果选取得一条偷取路径是从头开始得那么我们就不能偷取最后一个,那就不算最后一个偷取一遍2.同理我们也可以不算第一个 偷取一遍计算一次偷取得结果*/int rob(vector<int>& nums) {if(nums.size…