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

Python高级数据结构——散列表(Hash Table) 散列表是一种高效的数据结构,通过散列函数将键映射到槽位,实现了快速的数据检索。在Python中,可以使用内置的字典来轻松创建和操作散列表。理解散列表的基本概念、实现方式和应用场景,将有助于更好地应用散列表解决实际问题。 阅读详情

一,基础概念

散列表,英文名是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 = size
        self.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.size
        bucket = self.hash_table[hashed_key]

        found_key = False
        for index, record in enumerate(bucket):
            record_key, record_val = record
            if record_key == key:
                found_key = True
                break
        if found_key:
            #遇到散列冲突时,直接覆盖了旧的值
            bucket[index] = (key, val)
        else:
            bucket.append((key, val))

    def get_val(self, key):
        hashed_key = hash(key) % self.size
        bucket = self.hash_table[hashed_key]

        found_key = False
        for index, record in enumerate(bucket):
            record_key, record_val = record
            if record_key == key:
                found_key = True
                break
        if found_key:
            return record_val
        else:
            return "No record found"

    def delete_val(self, key):
        hashed_key = hash(key) % self.size
        bucket = self.hash_table[hashed_key]

        found_key = False
        for index, record in enumerate(bucket):
            record_key, record_val = record
            if record_key == key:
                found_key = True
                break
        if 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 value
       umap["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";
       else
              cout << "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 PI

All 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/

AWR射频仿真基本操作(一) 这里只讲述一个基本的操作: 软件是AWR 14版本 阅读详情

相关推荐

【信号与系统实战解析】—— 拉普拉斯变换在电路分析中的关键应用

本文深入解析了拉普拉斯变换在电路分析中的关键应用。通过将时域微分方程转换为复频域的代数方程,系统化处理初始条件,并利用零极点图分析电路稳定性与频率响应。文章以RLC串联电路为例,详细演示了s域建模、性质运用及综合分析方法,为工程师提供了分析动态电路的强大工具与清晰视角。

weixin_29093017的博客 174

Python实战开发及案例分析(13)—— 散列表

散列表(Hash Table)是一种高效的键值对数据结构,具有快速的查找、插入和删除操作。它通过哈希函数将键映射到散列表的索引,实现对数据的快速访问。

贾贾的博客 1035

Halcon实战:5分钟搞定金属表面划痕检测(附完整代码解析)

本文提供了一份基于Halcon的金属表面划痕检测实战指南。通过核心的动态阈值算法,快速从图像中提取局部对比度异常区域,并结合形态学后处理与特征筛选,精准识别划痕。文章详解了从代码搭建、参数优化到工业场景适配的全流程,帮助工程师在五分钟内构建稳定高效的机器视觉检测方案。

weixin_29084231的博客 496

Python——数据结构——散列表(哈希表)

Python——数据结构——散列表(哈希表)

qq_41188247的博客 942

嵌入式系统存储体系

主要分为三种:高速缓存(cache)、主存和外存。

weixin_43586667的博客 2265

图像特征(一)——颜色特征(颜色直方图,颜色矩,颜色集,颜色聚合向量和颜色相关图)

本文主要是为了更好地理解图像特征,理解各方法的原理。转载请注明出处https://mp.youkuaiyun.com/console/editor/html/105090183 图像模式识别的一般步骤为:输入图像--图像预处理--特征提取--特征选择--图像分类--输出结果。 图像特征主要有:颜色特征、形状特征、纹理特征和空间关系特征。 本文主要介绍颜色特征,并给出matlab上实现的demo。 颜...

gloria_iris的博客 3万+

数据结构-散列表的含义与C++实现

散列表是一种非常重要的数据结构,它可以实现快速的查找、插入和删除操作。散列表的核心是散列函数,一个好的散列函数可以提高散列表的性能。散列表的查找技术主要有直接寻址表、线性探测法、平方探测法和双散列法。散列表的优点是查找效率非常高,缺点是需要消耗大量的空间。

m0_61789994的博客 1691

ArcGIS Maps SDK for Unity实战:把真实世界地形‘搬进’游戏场景的保姆级教程

本文详细介绍了如何使用ArcGIS Maps SDK for Unity将真实世界地形导入游戏场景的完整流程。从高程数据获取、TPKX格式转换到Unity中的地形集成与优化,提供了保姆级教程,帮助开发者快速实现高精度地形建模,特别适用于开放世界游戏和数字孪生项目。

weixin_30621465的博客 346

散列表基本操作(c++)

但是,散列表的缺点是需要消耗大量的内存空间,因为需要预留足够的空间来存储散列表。此外,散列表的性能取决于哈希函数的设计,如果哈希函数不好,会导致散列表的性能下降。如果哈希函数设计得好,可以使得散列表的插入、删除和查找操作的时间复杂度都为O(1)。具体实现过程中,使用了哈希函数将关键字映射到散列表中的位置,然后使用链表来解决哈希冲突的问题。

m0_65690223的博客 535

DS18B20温度传感器项目实战:从驱动编写到LCD显示,打造你的第一个温控系统

本文详细介绍了如何使用DS18B20温度传感器和LCD1602显示屏构建一个完整的温控系统。从硬件连接、OneWire协议实现到温度数据采集与LCD显示,全面解析了系统整合与工程化实现的关键步骤,帮助开发者快速掌握嵌入式温度监测系统的开发技巧。

weixin_42525328的博客 349

深度解析:LPDDR5 Receiver 的挑战与应对

LPDDR5 的数据速率已经来到Tbit​DataRate1​≈156ps在6400Mbps时也就是说,。👉 这就是为什么成为 LPDDR5 的“标配”。

weixin_43199439的博客 211

算法设计与分析——散列表/哈希表(Hash Table)(三):散列表原理

直接寻址技术的缺点是非常明显的:如果全域U很大,则在一台标准的计算机可用内存容量中,要存储大小为∣U∣|U|∣U∣的一张表TTT也许不太实际,甚至是不可能的。还有,实际存储的关键字集合KKK相对UUU来说可能很小,使得分配给TTT的大部分空间都将浪费掉。 当存储在字典中的关键字集合KKK比所有可能的关键字的全域UUU要小许多时,散列表需要的存储空间要比直接寻址表少得多。特别地,我们能将散列表的存储需求降至Θ(∣K∣)\Theta(|K|)Θ(∣K∣),同时散列表中查找一个元素的优势仍得到保持,只需要O(1)

冯·诺依曼 1万+

Unity中实现俄罗斯方块

二:实现思路 ——创建每一个方块可移动到的位置点,可以理解为创建一个游戏地图,从(0,0)点开始依次向x轴和y轴延伸,例如x最大为9,y最大为19,则创建了一个20行10列的地图 ——制作每一个形状的预制体,Shape是每一个整体形状,Block是每一块小方块,CenterPos代表这个形状的旋转中心 ——创建GameController脚本控制游戏逻辑,挂载到面板物体上。创建Block脚本控制每个形状的操作,挂载到每个形状上 ——在GameController脚本中编...

L的博客 8559

【数据结构(C++版)】哈希表(散列表)

1. 散列表的概念 2. 散列函数的构造方法 3. 处理冲突的方法 4. 散列查找及性能分析 5. 哈希的应用

Qiuhan_909的博客 2404

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C++ 哈希

哈希 ,闭散列,开散列,封装unordered_map和unordered_set,

lmw3020083657的博客 561

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

散列表、哈希表、冲突

散列表(Hash Table),也称哈希表,是一种根据关键码值(Key-Value)进行访问的数据结构,通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。散列表可以使用数组来实现,每个数组元素对应一个桶,每个桶可以存储多条数据,每条数据的访问时间是O(1)的。散列表的核心就是散列函数,它把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变成固定长度的输出,该输出即为散列值。散列表中使用散列函数计算出每个关键字的散列值,并根据该值存储或查找对应的数据。

m0_58381606的博客 1266

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

散列表(哈希表)

散列表(哈希表)

weixin_61611746的博客 4346

C++ 数据结构学习 ---- 散列表

学习c++数据结构---散列表

qq_58240448的博客 1039

散列表 - 解析与C++实现

第99篇 C++数据结构(九)散列表 1.散列表简介 1.1.散列函数 1.2.散列冲突解决方案 2.数据节点 3.实现 3.1.变量 3.2.方法 4.测试 4.1.测试代码 4.2.输出结果 5.实现代码 6.总结 详细介绍: 大佬文章链接1 大佬文章链接21.散列表简介 散列表也叫哈希表(Hash table),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。1.

生活需要深度 2057

数据结构课程设计(五):散列表

(1)初始化散列表; (2)向散列表中插入一个元素; (3)从散列表中删除一个元素; (4)从散列表中查找一个元素。 #include <bits/stdc++.h> #define ElemType int #define HashSize 12 #define p 11 using namespace std; //数据:19 14 23 1 68 20 84 27 55 11 10 79 typedef struct FLNode { //散列主文件中的节点类型 Ele

小绵杨 1233

《哈希表》

桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端条件下,可能会导致一个桶节点非常多,会影响的哈希表的性能,因此在一定条件下需要对哈希表进行增容,那该条件怎么确认呢?研究表明:当表的长度为质数且表装载因子不超过0.5时,新的表项一定能够插入,而且任何一个位置不会被探测两次,因此只要表中有一半的空位置,就不会存在表满的问题,在搜索时可以不考虑表装满的情况,但在插入时必须确保表的状态因子不超过0.5,如果超出必须要进行增容,所以比散列的最大缺陷就是空间利用率低,这也是哈希的缺陷。

wuqin668的博客 1626

散列表的插入和删除

散列表进行插入和删除都要借助于hash函数,根据要插入或删除的值,对应到相应的位置,再对该位置进行操作。代码如下: void collsion(hash_node *hash[],int elem, hash_node *news)//*hash[]传的是指针数组,其实传的是指针数组的首地址,即地址的地址,如果直接传数组元素的地址,返回并不指向该数组中元素的地址。处理冲突 { if(hash

TBViwer 6786

C++数据结构——散列表(哈希表)

一:散列表的概念 定义:散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希函数。 基本概念: 若关键字为k,则其值存放在f(k)的存储位置上。由此,不需比较便可直

eyes++的博客 2786

Python数据结构13:散列(哈希)表,完美散列函数,解决冲突方法,开放寻址,负载因子,平均查找次数

散列表的概念。 散列表的实现。 开放寻址散列表。 数据项链列表实现

YangStudent的博客 4368

第99篇 C++数据结构(九)散列表

第99篇 C++数据结构(九)散列表1.散列表简介1.1.散列函数1.2.散列冲突解决方案2.数据节点3.实现3.1.变量3.2.方法4.测试4.1.测试代码4.2.输出结果5.实现代码6.总结 详细介绍: 大佬文章链接1 大佬文章链接2 1.散列表简介 散列表也叫哈希表(Hash table),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。 1.1.散列函数 散列函

weixin_49188222的博客 2423

【追求卓越09】算法--散列表(哈希表)

本章我们主要介绍一些散列表相关的概念:散列函数,散列冲突,散列值,关键值,以及散列冲突的解决方式。散列函数设计的好坏决定了散列冲突的概率,也决定了散列表的性能。想要设计一个好的散列表,需要从散列函数,装载因子,散列冲突解决方案着手。关于散列函数,我们不能设计的太复杂,并且尽量使散列值均匀分布,这样尽可能减少散列冲突,即便散列冲突,链表长度也较为均匀。关于装载因子,我们需要设置一个合理的装载因子上限,并在动态扩容的过程中,需要考虑均分搬移数据关于散列冲突解决方案,有开放地址法和链表法。

点滴路程 1651
上一篇: 基于VSCode安装Node.js开发环境
下一篇: 数据结构小记【Python/C++版】——树与二叉树篇
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员与背包客_CoderZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值