数据结构小记【Python/C++版】——散列表篇

一,基础概念

散列表,英文名是hash table,又叫哈希表。

散列表通常使用顺序表来存储集合元素,集合元素以一种很分散的分布方式存储在顺序表中。

散列表是一个键值对(key-item)的组合,由键(key)和元素值(item)组成。键和它对应的元素值基于散列函数(hash function)进行一对一的映射,基于键查找到的元素值也可以称为散列值,查找公式:item = hash(key)。其中item可以是具体的值,也可以是具体的值对应的内存地址,也可以是链表或者链表的指针。

注意,有的教程里面喜欢把键值对称为(key, item),有的教程里面喜欢把键值对称为(index, value),其实是相同的意思。

散列表和数组相似的地方在于,都可以基于下标快速的访问数据,数组的下标是索引,散列表的下标是键。

散列表结构在生活中的抽象模型:一个班级所有学生的姓名和对应的学号。

二,散列表的图示结构

图一,key -> hash function -> hash table(key, item)

图二,哈希函数:item = key % 10

三,关于散列函数

最常见的散列函数: 

除数留余法:item = (key + 5) % 10

例如:key=50, item = 5。key = 44, item = 9

好的散列函数具有以下特性:

函数的设计不过于复杂。

大部分情况下,使用相同的键只会查找到同一个值。

键和元素值要均匀随机分布。

基于键查找每个元素值的时间是近似的,而不是查找有的值耗时很长,查找有的值耗时很短。

发生散列冲突的概率极低。

四,散列冲突处理

所谓散列冲突,是指不同的键映射到了相同的散列值。

例如,对于”item = key % 10“的哈希函数,key为12或者22得到的散列值都是2。

方式一,链表法

在链表法中,散列表中的每个key都映射到一个链表。因此,当两个key具有相同的item值时,这两个key都被添加到相同的链表中。

方式二,线性探测法

线性探测法是开放寻址法中的一种,所谓开放寻址,是指如果出现了散列冲突,在散列表中重新找一块儿没被使用过的内存地址,组成新的键值对。

具体操作

基于当前key生成的item值,没有被其他键值对占用时。则该item值可以和key组成键值对来放进散列表中。如果该item值对应了已有的其他的key,则将该key映射到散列表中还没被使用的下一个位置的item值,组成新的键值对来放进散列表中。

对于当前场景,具体步骤为

step.01: 采用item=key%10的方式来获得哈希值。

step.02: 发现采用item= key%10的方式获得的哈希值被别的key占用了,于是采用item=(key+1)%10的方式来获得新的哈希值。

step.03: 发现采用item=(key+1)%10的方式获得的新哈希值没被占用,就将此哈希值作为key的item,生成键值对放入到散列表。否则,继续采用item=(key+2)%10的方式来获得哈希值,以此类推。

例如

根据key=70获得的哈希值也是0。但是那个位置已经被(key=10, item=0)占用了。因此,根据线性探测法,我们将继续找到下一个位置 1。由于该位置暂时未被占用,我们依此生成(key=70, item=1)的键值对。

两种方式对比

五,散列表常见操作

a.插入元素

step1.计算key对应的散列值。

step2.如果散列值不在散列表中,则插入生成新的键值对。

step3.如果散列值已经在散列表中,则发生了散列冲突,return返回或覆盖旧散列值或调用专门处理散列冲突的函数。

b.查找元素

step1.计算key对应的散列值。

step2.如果散列值在散列表中,则查找成功,否则,查找失败。

c.删除元素

对于链接法,执行和链表一样的删除操作。

对于开放寻址法,将被删除节点置为“已删除”标记,查找时跳过此节点,插入时重新覆盖该节点。

六,代码实现

1.Python实现:

class HashTable:def __init__(self, size):self.size = sizeself.hash_table = self.create_buckets()def create_buckets(self):#存储key用的桶结构return [[] for _ in range(self.size)]def insert_val(self, key, val):hashed_key = hash(key) % self.sizebucket = self.hash_table[hashed_key]found_key = Falsefor index, record in enumerate(bucket):record_key, record_val = recordif record_key == key:found_key = Truebreakif found_key:#遇到散列冲突时,直接覆盖了旧的值bucket[index] = (key, val)else:bucket.append((key, val))def get_val(self, key):hashed_key = hash(key) % self.sizebucket = self.hash_table[hashed_key]found_key = Falsefor index, record in enumerate(bucket):record_key, record_val = recordif record_key == key:found_key = Truebreakif found_key:return record_valelse:return "No record found"def delete_val(self, key):hashed_key = hash(key) % self.sizebucket = self.hash_table[hashed_key]found_key = Falsefor index, record in enumerate(bucket):record_key, record_val = recordif record_key == key:found_key = Truebreakif found_key:bucket.pop(index)return#魔法函数,遍历对象中的元素def __str__(self):return "".join(str(item) for item in self.hash_table)hash_table = HashTable(5)
hash_table.insert_val('key_1', 'value_1')
hash_table.insert_val('key_2', 'value_2')
hash_table.insert_val('key_3', 'value_3')
print(hash_table)print("the value of key_2 is: ", hash_table.get_val('key_2'))
hash_table.delete_val('key_3')
print(hash_table)

运行结果:

[][][('key_3', 'value_3')][('key_1', 'value_1'), ('key_2', 'value_2')][]
the value of key_2 is:  value_2
[][][][('key_1', 'value_1'), ('key_2', 'value_2')][]

2.C++实现:

#include<iostream>
#include <list>
using namespace std;
class Hash
{int BUCKET;//每个散列值对应的链表list<int>* table;
public:Hash(int V);  //插入元素void insertItem(int x);//删除元素void deleteItem(int key);//散列函数int hashFunction(int x) {return (x % BUCKET);}void displayHash();
};
Hash::Hash(int b)
{this->BUCKET = b;table = new list<int>[BUCKET];
}
void Hash::insertItem(int key)
{int value = hashFunction(key);table[value].push_back(key);
}
void Hash::deleteItem(int key)
{//找到key对应的散列值int index = hashFunction(key);list <int> ::iterator i;for (i = table[index].begin();i != table[index].end(); i++) {if (*i == key)break;}//删除key对应的元素if (i != table[index].end())table[index].erase(i);
}
void Hash::displayHash() {for (int i = 0; i < BUCKET; i++) {cout << i;for (auto x : table[i])cout << " --> " << x;cout << endl;}
}
int main()
{int a[] = { 15, 11, 27, 8, 12 };int n = sizeof(a) / sizeof(a[0]);Hash h(7);  for (int i = 0; i < n; i++)h.insertItem(a[i]);h.deleteItem(12);h.displayHash();return 0;
}

运行结果:

0
1 --> 15 --> 8
2
3
4 --> 11
5
6 --> 27

3.内置数据类型实现

C++内置数据类型:STL标准库中的unordered_map容器

Python内置数据类型:Python字典dict

Demo1:

#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{unordered_map<string, double> umap = {{"One", 1},{"Two", 2},{"Three", 3}};//insert valueumap["PI"] = 3.14;umap["root2"] = 1.414;umap.insert(make_pair("e", 2.718));string key = "PI";if (umap.find(key) == umap.end())cout << key << " not found\n\n";elsecout << "Found " << key << "\n\n";unordered_map<string, double>::iterator itr;cout << "\nAll Elements : \n";for (itr = umap.begin();itr != umap.end(); itr++){cout << itr->first << " " <<itr->second << endl;}
}

运行结果:

Found PIAll Elements :
One 1
Two 2
Three 3
PI 3.14
root2 1.414
e 2.718

Demo2:

dict_obj = {"a":1, "b":2, "c":3, "d":4}#打印字典
print(dict_obj['a'])#增加键值对
dict_obj['e'] = 5#修改字典的值
dict_obj['a'] = 21#删除键值对
del dict_obj['d']
print(dict_obj)#清空字典
dict_obj.clear()
print(dict_obj)

运行结果:

1
{'a': 21, 'b': 2, 'c': 3, 'e': 5}
{}

七,参考阅读

《Problem Solving with Algorithms and Data Structures Using Python, Second Edition》

https://www.softwaretestinghelp.com/hash-table-cpp-programs/

https://www.digitalocean.com/community/tutorials/hash-table-in-c-plus-plus

https://www.geeksforgeeks.org/hash-map-in-python/

https://scanftree.com/programs/cpp/c-program-for-hashing-with-chaining/

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

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

相关文章

解密阿里巴巴面试题:如何设计一个微博?

亲爱的小米科技粉丝们,大家好呀!今天小米带来了一则热门话题——阿里巴巴面试题:如何设计一个微博?别着急,跟着小米一起来揭秘吧! 实现哪些功能? 在设计微博系统时,需要考虑实现哪些功能才能满足用户的需求。除了基本的发布推文、时间线、新闻推送、关注/不允许用户以…

【JavaScript 漫游】【034】AJAX

文章简介 本篇文章为【JavaScript 漫游】专栏的第 034 篇文章&#xff0c;对浏览器模型的 XMLHttpRequest 对象&#xff08;AJAX&#xff09;的知识点进行了总结。 XMLHttpRequest 对象概述 浏览器与服务器之间&#xff0c;采用 HTTP 协议通信。用户在浏览器地址栏键入一个网…

Java项目源码基于springboot的家政服务平台的设计与实现

大家好我是程序员阿存&#xff0c;在java圈的辛苦码农。辛辛苦苦板砖&#xff0c;今天要和大家聊的是一款Java项目源码基于springboot的家政服务平台的设计与实现&#xff0c;项目源码以及部署相关请联系存哥&#xff0c;文末附上联系信息 。 项目源码&#xff1a;Java基于spr…

虚拟机镜像iso下载

MSDN, 我告诉你 - 做一个安静的工具站 (itellyou.cn)

CANalyzer使用_00 概述

计划写一个专题&#xff0c;该专题主要介绍CANalyzer的使用&#xff0c;每次文档计划写一个点&#xff0c;自己不累&#xff0c;别人看着也不累&#xff0c;并且方便拓展。本文作为专题的开篇主要介绍下CANalyzer软件的背景&#xff0c;软件界面等信息。 1 软件介绍 CANalyze…

FastAPI 学习笔记

FastAPI 学习笔记 0. 引言1. 快速开始2. 升级示例代码 0. 引言 在 Python 这个充满活力的生态系统中&#xff0c;FastAPI 应运而生&#xff0c;它是一个现代的、快速的 Web 框架&#xff0c;专注于构建 RESTful API。 无论你是一名有经验的 Python 开发人员&#xff0c;还是一…

HTTP/2、HTTP/3对HTTP/1.1的性能改进和优化

HTTP/1.1 相比 HTTP/1.0 提高了什么性能&#xff1f; 性能上的改进&#xff1a; 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。 支持管道&#xff08;pipeline&#xff09;网络传输&#xff0c;只要第一个请求发出去了&#xff0c;不必等其回来&#xff0c;就可以…

Purple Pi OH鸿蒙开发板7天入门OpenHarmony开源鸿蒙教程【五】

在完成了Purple Pi OH大部分的接口测试之后&#xff0c;紧接着就是一个充满挑战的任务——利用SDK来编译生成我们自己的镜像文件。通过这一过程&#xff0c;不仅能够让你获得一个可在真实硬件上运行的系统镜像&#xff0c;更重要的是&#xff0c;它让你对OpenHarmony系统的构建…

Qt - 信号和槽

目录 一、信号 二、槽 三、信号和槽的使用 (一) 连接信号和槽 (二) 自定义槽 (三) 通过 Qt Creator生成信号槽代码 (四) 自定义信号 四、带参数的信号和槽 五、信号与槽的断开 六、Qt4版本信号与槽的连接 (一) Qt4版本信号与槽连接的优缺点 一、信号 在 Qt 中&…

CubeMX使用教程(5)——定时器PWM输出

本篇我们将利用CubeMX产生频率固定、占空比可调的两路PWM信号输出 例如PA6引脚输出100Hz的PWM&#xff1b;PA7引脚输出500Hz的PWM&#xff0c;双路同时输出 我们还是利用上一章定时器中断的工程进行学习&#xff0c;这样比较方便 首先打开CubeMX对PA6、PA7进行GPIO配置 注&a…

Mixamo动画素材导入UE5的最简单方法

一、Mixamo素材 官网&#xff1a;https://www.mixamo.com/ Mixamo是Adobe公司出品的免费动画库&#xff0c;可商用。软件分为characters(角色&#xff09;、Animations&#xff08;动画)两个部分。 二、辅助工具MIXAMO CONVERTER 官网&#xff1a;https://terribilisstudio…

C#与WPF通用类库

个人集成封装&#xff0c;仓库已公开 NetHelper 集成了一些常用的方法&#xff1b; 如通用的缓存静态操作类、常用的Wpf的ValueConverters、内置的委托类型、通用的反射加载dll操作类、Wpf的ViewModel、Command、Navigation、Messenger、部分常用UserControls(可绑定的Passwo…

通信总线协议之CAN-FD协议详解

文章目录 通信总线之CAN-FD总线协议详解1. CAN-FD 简介1.1 什么是CAN FD1.2 CAN FD的特点 2. CAN-FD总线协议2.1 帧起始2.2 仲裁段2.3 控制段2.4 数据段2.5 CRC段2.6 ACK段2.7 帧结束 3. 如何从传统的CAN升级到CAN FD 通信总线之CAN-FD总线协议详解 1. CAN-FD 简介 1.1 什么是…

selenium高级应用

常见控件应用 复杂的控件操作1.操作Ajax选项2.滑动滑块操作 WebDriver的特殊操作元素class值包含空格property、attribute、text的区别定位动态id 截图功能页面截图页面截图&#xff0c;返回截图的二进制数据页面截图&#xff0c;返回base64的字符串截取指定元素。先定位元素&a…

Redis常见数据类型下

目录 Hash 哈希 常用指令 HSET HGET HEXISTS HDEL HKEYS HVALS HGETALL HMGET 内部编码 Hash类型和关系型数据库 缓存方式对比 List 列表 特点 常用命令 LPUSH LPUSHX RPUSH RPUSHX LRANGE LPOP / RPOP LINDEX LINSERT 阻塞(BLOCK)版…

【Linux】文件缓冲区|理解文件系统

目录 预备知识 观察现象 第一&#xff1a;携带\n&#xff0c;不使用fork()&#xff0c;打印到显示器 第二&#xff1a;携带\n&#xff0c;使用fork()&#xff0c;打印到显示器 第三&#xff1a;携带\n&#xff0c;使用fork()&#xff0c;打印到文件里 第四&#xff1a;不携…

如何选择适合的G口大流量服务器?

G口大流量服务器是指接入互联网的带宽达到1Gbps及以上&#xff0c;并且能够提供大量数据传输服务的服务器。那么如何选择适合的G口大流量服务器&#xff0c;RAK部落小编为您整理发布选择适合的G口大流量服务器需要考虑哪些关键点。 选择适合的G口大流量服务器时&#xff0c;应该…

JavaSec 基础之 CC1 链

文章目录 背景环境以及配置分析0x1 终点(利用点分析)0x20x30x310x320x33 0x040x05 背景 Apache Commons Collections是Apache提供的一个Java库&#xff0c;它扩展了Java自带的集合框架。通过这个库&#xff0c;咱们可以使用更多种类的集合类型&#xff0c;以及各种实用的集合操…

星星魔方

星星魔方 1&#xff0c;魔方三要素 &#xff08;1&#xff09;组成部件 6个中心块和8个角块和三阶魔方同构&#xff0c;另外每个面还有构成五角星的十个块。 &#xff08;2&#xff09;可执行操作 一共12种操作&#xff0c;其中6种是每个层顺时针旋转90度&#xff0c;另外6…

HTML静态网页成品作业(HTML+CSS)——家乡漳州介绍设计制作(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…