哈希算法:冲突解决与高效查找

一、哈希概念

又称散列,是一种组织数据的方式。本质就是通过哈希函数把关键字key跟存储位置建立一个映射关系,查找时通过这个哈希函数,计算出key存储的位置,进行快速查找。

1.1直接定位法

当关键字的范围比较集中时,简单高效,但局限性太大(而且必须是整形)

1.2哈希冲突

假设我们只有数据范围是[0,9999)的N个值,我们要映射到一个M个空间的数组中(一般情况下M。=N),那么就要借助哈希函数,关键字key被放到数组的h位置,注意h计算的值必须在[0,M)之间。

那么这里就会存在一个问题,两个不同的key可能会映射到用一个位置,这是无法避免的,所以就要尽可能的设计优秀的哈希函数

1.3负载因子

假设哈希表中已经映射存储了N给值,哈希表大小是M,负载因子为N/M。负载因子越大,哈希冲突的概率越高,空间利用率越高;负载因子越小,哈希冲突的概率越小,空间利用率越低。

越小越好。

1.4将关键字转为整形

我们把关键字映射到数组中位置时,一般时整形好做映射计算。

1.5哈希函数

一个好的哈希函数应该让N个关键字被等概率的均匀的散列分布到哈希表的M个空间中,但实际却和很难做到,所以需要认真设计

1.51除法散列法/除留余数法

假设N==100,[0,9999],哈希表的大小为M,那么通过key除以M的余数作为映射位置的下标

h(key)=key/M

 避免M是2或10的幂,key会重复很多,建议M取不太接近2的整数次幂的一个质数

eg.key =123456

int hashi = key%2^16

int hashi = key&(1<<16-1)  key只取后16位

int n =16;

int hashi = key&(1<<n-1)

用key’=key>>16,将key和key‘异或结果作为哈希值,尽量让key所有的位都参与计算

 

1.6处理哈希冲突

1.61开放定址法(比较搓)

在开放地址法中所有的元素都放到哈希表里,当一个关键字key用哈希函数计算出的位置冲突了,则按照某种规则找到一个没有存储数据的位置进行存储。

有三种规则:线性探测,二次探测,双重探测

eg{19,30,5,36,13,20,21,12}映射到M=11的表中

a、线性探测,但会出现堆积

看似只有19和30的hash0位置冲突,但其实20和21也在抢位置

hc = (key.i)=hashi = (hashi0+i)/M   i={1,2,3,...,M-1}  最多探测M-1次

注意给每一个存储值的位置加一个状态标识,否则删除一些值以后,会影响后面冲突的值的查找

{EXIST,EMPTY,DELETE}

从发生冲突的位置开始,依次线性向后探测,直到寻找到下一个没有存储数据的位置为止,如果走到哈希表尾,则回绕到哈希表头的位置

b、二次探测

依次左右按二次方跳跃探测

h(key)= hash0=key%M

hc(key,i) = hashi=(hash0+-i^2)%M,i={1,2,3,...M/2}

当hashi=(hash0-i^2)%M时,当hashi<0时,需要hashi+=M

eg.将{19,30,52,63,11,22}映射到M=11表中

h(19)=8  h(30)=8  h(52)=8  h(63)=8  h(11)=0  h(22)=0

c、双重散列(了解即可,不实用)

第一个哈希函数计算出的值发生冲突,使用第二个哈希函数,计算出一个跟key相关的偏移值,不断往后探测

h1(key)=hasg09=key%M

hc(key,i)=hashi=(hash0+i*h2(key))%M  i={1,2,3,...,M}

1.5.2乘法散列法(对哈希表大小M没有要求)

用关键字乘上常数A(0<A<1),并抽取出k*A的小数部分,后再用M乘以k*A的小数部分,再向下取整。

1.5.3全域散列法

如果存在一个恶意的对手,他针对我们提供的散列函数,特意构造出一个发生严重冲突的数据集,比如让所有关键字全部落入同一位置中

方法:见招拆招,给散列函数增加随机性,攻击者无法找到确定可以导致最坏情况的数据,即全域散列

ha(key)=((a*key+b)%p)%M

p需要选一个足够大的质数,a选[1,p-1],b选[0,p-1]

需要注意每次初始化哈希表时,随机选取全域散列函数组中的一个散列函数使用,后读增删查改都固定使用这个散列函数,否则每次哈希都是随机选一个散列函数,那么插入是一个散列函数,查找又是另一个函数,就会导致找不到插入的key了

1.6.3 链地址法

开放地址法负载因子必须小于1,链地址法的负载因子就没有限制了,可以大于1,但为了哈希冲突概率低,空间利用率,大部分还是控制在1,大于1就扩容

 

二、代码实现

A、标准

1、插入

但是上面这种扩容有些太复杂了,可改用如下代码

能交换必须优先考虑,赋值麻烦还会涉及深拷贝

 

2、查找

3、删除

B、链式

1、插入

2、查找

3、删除

Read more

从0到1打造RISC-V智能家居中控:硬件+固件+通信全链路实战

从0到1打造RISC-V智能家居中控:硬件+固件+通信全链路实战

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * 从0到1打造RISC-V智能家居中控:硬件+固件+通信全链路实战 🏠💡 * 为什么选择RISC-V?🤔 * 系统整体架构概览 🧩 * 第一步:硬件选型与电路搭建 🔌 * 主控芯片选择 * 外设连接 * 第二步:开发环境搭建 🛠️ * 安装步骤(以Ubuntu为例) * 第三步:裸机驱动开发(Bare Metal)⚡ * 示例1:DHT11温湿度读取(Bit-banging) * 示例2:BH1750光照传感器(I2C) * 第四步:引入FreeRTOS实现多任务调度 🔄 * 第五步:Wi-Fi连接与MQTT通信 ☁️📡 * 连接Wi-Fi * MQTT客户端(使用esp-mqtt库) * 第六步:BLE本地控制(无需Wi-Fi)📱

By Ne0inhk
机器人远程监控与OTA升级

机器人远程监控与OTA升级

7.4.1 远程监控的理论框架 远程监控是物联网和工业4.0时代的核心技术,其理论任务是通过网络通信手段,实现对分布式机器人设备的实时状态感知、故障预警和远程干预 。对于机器人系统而言,远程监控不仅是数据可视化的问题,更是一个涉及数据采集、传输、处理、分析和决策的闭环系统工程。 远程监控系统的三层理论架构: 感知层解决“数据从哪里来”的问题。包括机器人本体上的各类传感器(温度、振动、电流、位置)、控制器状态(CPU负载、内存使用、存储寿命)以及运行日志的采集 。感知层的理论基础是传感器技术和信号处理,其核心挑战是在不影响机器人实时控制的前提下,高效、可靠地获取状态数据。 传输层解决“数据怎么传”的问题。根据应用场景的不同,可采用Wi-Fi(室内短距)、4G/5G(广域移动)、工业以太网(固定工位)等不同通信方式 。传输层的理论基础是网络通信协议栈,其核心挑战是保证数据在复杂工业环境下的实时性、可靠性和安全性。 应用层解决“数据怎么用”

By Ne0inhk

neo4j desktop2 安装与使用

1. Neo4j Desktop 2 简介 1.1 Neo4j Desktop 2 的核心功能与优势 Neo4j Desktop 2 是 Neo4j 官方推出的图形化数据库管理工具,专为开发者和数据科学家设计。 其主要优势包括: 一体化开发环境:集成了数据库实例管理、查询编辑、数据可视化和扩展管理 本地开发友好:支持在本地机器上快速创建和测试图数据库实例 多版本管理:可同时管理多个 Neo4j 数据库版本 插件生态系统:内置插件市场,轻松安装常用扩展  项目管理:以项目为单位组织数据库、查询和配置   1.2 适用场景 图数据库开发:为应用程序开发提供本地图数据库环境 本地测试:在部署到生产环境前进行数据模型测试和查询验证 项目管理:管理多个图数据库项目,保持环境隔离 教育与学习:学习 Cypher 查询语言和图数据库概念 2.

By Ne0inhk
手把手教你配置飞书 OpenClaw 机器人,打造企业级 AI 智能助手

手把手教你配置飞书 OpenClaw 机器人,打造企业级 AI 智能助手

目标:在飞书(Feishu/Lark)中添加 OpenClaw 机器人,实现 7×24 小时 AI 智能对话与自动化办公。 OpenClaw GitHub | feishu-openclaw 桥接项目 想让你的机器人具备语音交互能力?试试 Seeed Studio 的 ReSpeaker 系列吧! 我会后续出reSpeaker XVF3800与Openclaw联动实现语音输入的教程,完全开放源码。 reSpeaker XVF3800 是一款基于 XMOS XVF3800 芯片的专业级 4 麦克风圆形阵列麦克风,即使在嘈杂的环境中也能清晰地拾取目标语音。它具备双模式、360° 远场语音拾取(最远 5 米)、自动回声消除 (AEC)、自动增益控制 (AGC)、声源定位 (DoA)、去混响、波束成形和噪声抑制等功能。

By Ne0inhk