在计算机科学中,哈希算法(Hash Algorithm)是一种将任意长度的输入数据映射到固定长度输出的技术,其输出称为哈希值(Hash Value)或散列值。哈希算法凭借高效的查找、插入和删除性能,在数据存储、密码学、数据校验等领域发挥着核心作用。

哈希算法的基本概念

哈希算法的核心是映射函数(哈希函数),它将输入数据(如字符串、数字、文件等)转换为固定长度的哈希值。例如,将字符串"hello"通过哈希函数映射为0a4d55a8d778e5022fab701977c5d840bbc486d0(SHA-1 哈希值),将数字123映射为数组索引5(用于哈希表存储)。

哈希算法的关键特性包括:

  • 确定性:相同输入必然产生相同哈希值。
  • 高效性:计算哈希值的过程快速,时间复杂度接近O(1)。
  • 抗碰撞性:不同输入产生相同哈希值(碰撞)的概率极低(密码学哈希算法的核心要求)。
  • 雪崩效应:输入的微小变化会导致哈希值的剧烈变化(如"hello"和"hellp"的哈希值差异极大)。

在数据结构中,哈希表(Hash Table)是哈希算法的典型应用,它通过哈希函数将键(Key)映射到数组的索引,实现高效的数据访问。例如,使用哈希表存储学生信息时,可将学生 ID 作为键,通过哈希函数映射到数组位置,从而快速查找学生信息。

哈希算法的思想

哈希算法的核心思想是通过映射函数实现快速定位,具体可分为以下两类场景:

2.1 哈希表中的映射思想

在哈希表中,哈希算法的目标是将键均匀分布到数组中,实现O(1)级别的查找、插入和删除操作。其核心步骤包括:

  1. 哈希函数设计:将键key映射为数组索引index,常见方法有:
    • 直接定址法:index = a * key + b(适用于键为整数且范围较小的场景)。
    • 除留余数法:index = key % tableSize(最常用,需选择合适的tableSize减少碰撞)。
    • 数字分析法:提取键中分布均匀的部分作为索引(适用于键为固定长度的数字)。
    • 折叠法:将键分割为若干部分,合并后取余(适用于长键)。
  1. 解决碰撞:由于哈希函数的映射是多对一的,必然存在碰撞(不同键映射到同一索引),常见解决方法:

链地址法的图示如下:

(索引 1 处发生碰撞,键值对 1 和 2 通过链表存储)

    • 开放定址法:当碰撞发生时,通过一定规则(如线性探测、二次探测、双重哈希)寻找下一个空闲位置。例如,线性探测的公式为index = (hash(key) + i) % tableSize(i为探测次数)。
    • 链地址法:将哈希值相同的键值对存储在同一个链表中,数组的每个位置指向一个链表的头节点。当查找时,先通过哈希函数定位到链表,再遍历链表查找目标键。

2.2 密码学中的哈希思想

密码学哈希算法(如 MD5、SHA-256)的核心思想是通过复杂的数学运算实现不可逆映射,确保数据的完整性和安全性。其设计目标包括:

  • 不可逆性:无法从哈希值反推原始输入。
  • 强抗碰撞性:无法找到两个不同输入产生相同哈希值。

例如,在用户密码存储中,系统不会直接存储明文密码,而是存储密码的哈希值。当用户登录时,系统计算输入密码的哈希值,与存储的哈希值比对,从而验证身份(避免明文密码泄露)。

哈希算法的解题思路

使用哈希算法解决实际问题时,需根据场景选择合适的策略:

3.1 哈希表的解题步骤

当需要高效查找、去重或计数时,优先使用哈希表,步骤如下:

  1. 确定键值对:明确需要存储的键(用于查找的关键字)和值(需关联的数据)。
  1. 选择哈希结构:Java 中常用HashMap(无序)、HashSet(仅存键,用于去重)、LinkedHashMap(保持插入顺序)等。
  1. 处理碰撞:Java 的HashMap默认使用链地址法 + 红黑树(当链表长度超过 8 时转为红黑树)解决碰撞,无需手动处理。
  1. 操作数据
    • 查找:通过get(key)获取值,判断是否存在。
    • 插入:通过put(key, value)存储键值对。
    • 删除:通过remove(key)移除键值对。
    • 计数:统计键出现的次数(如用HashMap<Key, Integer>记录次数)。

3.2 密码学哈希的解题步骤

在涉及数据校验、签名等场景时,使用密码学哈希算法,步骤如下:

  1. 选择哈希函数:根据安全性要求选择(如 SHA-256 优于 MD5)。
  1. 计算哈希值:对输入数据(如文件、字符串)计算哈希值。
  1. 验证或比对:通过比对哈希值判断数据是否被篡改(如文件传输前后的哈希值是否一致)。

LeetCode 例题及 Java 代码实现

例题 1:两数之和(LeetCode 1)

给定一个整数数组nums和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案,且同样的元素不能被重复利用。

解题思路

使用哈希表存储已遍历的元素及其索引,对于当前元素nums[i],计算互补值target - nums[i],若哈希表中存在该互补值,则返回两者的索引;否则将当前元素存入哈希表。时间复杂度O(n),空间复杂度O(n)。

代码实现

import java.util.HashMap;import java.util.Map;public class TwoSum {public int[] twoSum(int[] nums, int target) {Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums.length; i++) {int complement = target - nums[i];if (map.containsKey(complement)) {return new int[]{map.get(complement), i};}map.put(nums[i], i);}throw new IllegalArgumentException("No solution");}public static void main(String[] args) {TwoSum solution = new TwoSum();int[] nums = {2, 7, 11, 15};int[] result = solution.twoSum(nums, 9);System.out.println(result[0] + ", " + result[1]); // 输出:0, 1}}

例题 2:存在重复元素(LeetCode 217)

给定一个整数数组,判断是否存在重复元素。如果存在一值在数组中出现至少两次,函数返回true。如果数组中每个元素都不相同,则返回false。

解题思路

使用HashSet存储元素,遍历数组时若元素已在集合中,说明存在重复,返回true;否则加入集合。时间复杂度O(n),空间复杂度O(n)。

代码实现

import java.util.HashSet;import java.util.Set;public class ContainsDuplicate {public boolean containsDuplicate(int[] nums) {Set<Integer> set = new HashSet<>();for (int num : nums) {if (set.contains(num)) {return true;}set.add(num);}return false;}public static void main(String[] args) {ContainsDuplicate solution = new ContainsDuplicate();System.out.println(solution.containsDuplicate(new int[]{1, 2, 3, 1})); // 输出:trueSystem.out.println(solution.containsDuplicate(new int[]{1, 2, 3, 4})); // 输出:false}}

例题 3:有效的字母异位词(LeetCode 242)

给定两个字符串s和t,编写一个函数来判断t是否是s的字母异位词。字母异位词指字母相同,但排列不同的字符串。

解题思路

使用哈希表(或数组,因字符范围有限)统计每个字符的出现次数。先遍历s增加计数,再遍历t减少计数,若最终所有计数为0,则为异位词。时间复杂度O(n),空间复杂度O(1)(字符集大小固定)。

代码实现
public class ValidAnagram {public boolean isAnagram(String s, String t) {if (s.length() != t.length()) {return false;}int[] count = new int[26]; // 假设仅包含小写字母for (char c : s.toCharArray()) {count[c - 'a']++;}for (char c : t.toCharArray()) {count[c - 'a']--;if (count[c - 'a'] < 0) {return false;}}return true;}public static void main(String[] args) {ValidAnagram solution = new ValidAnagram();System.out.println(solution.isAnagram("anagram", "nagaram")); // 输出:trueSystem.out.println(solution.isAnagram("rat", "car")); // 输出:false}}

哈希算法与考研 408

在计算机考研 408 中,哈希算法是数据结构部分的核心考点,主要涉及以下内容:

5.1 哈希表的构造与碰撞处理

考研 408 重点考查哈希表的实现细节,包括:

  • 哈希函数的设计:掌握除留余数法、直接定址法等常用方法,能根据场景选择合适的哈希函数(如键为整数时优先使用除留余数法)。
  • 碰撞解决方法:深入理解开放定址法(线性探测、二次探测)和链地址法的原理、优缺点及操作过程:
    • 开放定址法:优点是数据存储在数组中,缓存利用率高;缺点是存在聚集现象(线性探测的主要问题),删除操作复杂(需标记为 “已删除” 而非直接清空)。
    • 链地址法:优点是碰撞处理简单,删除操作方便,无聚集现象;缺点是指针开销大,缓存利用率低。

5.2 哈希表的性能分析

哈希表的性能取决于负载因子(loadFactor = 元素个数 / 表长)和碰撞处理方法:

  • 负载因子越小,碰撞概率越低,查找效率越高,但空间浪费大。
  • 负载因子越大,碰撞概率越高,查找效率下降(链地址法中链表变长,开放定址法中探测次数增加)。

考研中常考不同碰撞处理方法的时间复杂度:

  • 理想情况下(无碰撞):查找、插入、删除的时间复杂度均为O(1)。
  • 实际情况下:链地址法的平均查找长度为1 + loadFactor / 2;开放定址法为-ln(1 - loadFactor) / loadFactor(线性探测)。

5.3 哈希算法的应用

考研 408 中常考哈希算法的典型应用:

  • 哈希表:用于实现字典、集合、缓存(如 LRU 缓存)等数据结构。
  • 数据校验:通过哈希值验证文件完整性(如下载文件时比对哈希值)。
  • 密码存储:存储密码的哈希值而非明文(结合盐值 Salt 增强安全性)。
  • 哈希映射:如 Java 中的HashMap、Python 中的dict等语言内置数据结构的实现原理。

5.4 与其他数据结构的对比

考研中常对比哈希表与数组、链表、二叉搜索树的性能:

数据结构

查找

插入

删除

有序性

适用场景

数组

O(n)

O(n)

O(n)

可保持

小数据量、频繁访问连续元素

链表

O(n)

O(1)

O(1)

可保持

频繁插入 / 删除头部 / 中部元素

二叉搜索树

O(log n)

O(log n)

O(log n)

有序

需要有序遍历或范围查询

哈希表

O(1)

O(1)

O(1)

无序

频繁查找、插入、删除且无需有序

总结

哈希算法通过映射函数实现高效的数据访问和处理,是计算机科学中的核心技术之一。本文从哈希算法的基本概念、思想出发,详细讲解了哈希表的实现原理、碰撞处理方法,结合 LeetCode 例题和 Java 代码展示了实际应用,并深入分析了考研 408 的考点。

学习哈希算法时,需重点掌握哈希表的构造、碰撞处理方法及性能分析,理解其在不同场景下的优缺点。对于考研 408 考生,还需对比哈希表与其他数据结构的差异,掌握典型应用场景,确保能灵活运用哈希算法解决实际问题。

希望本文能够帮助读者更深入地理解哈希算法,并在实际项目中发挥其优势。谢谢阅读!


希望这份博客能够帮助到你。如果有其他需要修改或添加的地方,请随时告诉我。

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

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

相关文章

16018.UE4+Airsim仿真环境搭建超级详细

文章目录 1 源码下载2 下载安装软件2.1 安装 UE4 软件2.2 安装visual studio 20223 编译airsim源码4 进入AirSim工程,打开工程5 UE4 工程创建5.1 下载免费场景 CityPark,并创建工程5.2 工程编译5.2.1 将airsim 插件拷贝到 UE4工程路径中5.2.2 修改工程配置文件5.2.3 创建c++类…

Python 实战:构建 Git 自动化助手

在多项目协作、企业级工程管理或开源社区维护中&#xff0c;经常面临需要同时管理数十甚至上百个 Git 仓库的场景&#xff1a;多仓库需要统一 pull 拉取更新定期向多个项目批量 commit 和 push自动备份 Git 项目批量拉取私有仓库并管理密钥为解决这类高频、重复、机械性工作&am…

【PTA数据结构 | C语言版】出栈序列的合法性

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 给定一个最大容量为 m 的堆栈&#xff0c;将 n 个数字按 1, 2, 3, …, n 的顺序入栈&#xff0c;允许按任何顺序出栈&#xff0c;则哪些数字序列是不可能得到的&#xff1f;例如给定 m5、n7&#xf…

【LangGraph】create_react_agent 方法详细解释

create_react_agent 方法详细解释 create_react_agent 方法是一个在 LangGraph 中创建 React 代理的核心函数,接下来我们将一起探讨这个函数的作用、参数、返回值以及工作原理。 @_convert_modifier_to_prompt def create_react_agent(model: Union[str, LanguageModelLike]…

【时间之外】尘封的智能套件复活记

目录 尘封的奖品 初次触网的挫败 客服只会诱导消费 意外发现的生机 真相与反思 尘封的奖品 五年前那个蝉鸣阵阵的夏日&#xff0c;我抱着创新比赛特等奖的奖品礼盒走下领奖台时&#xff0c;绝对想不到这份荣誉会衍生出如此曲折的故事。礼盒里静静躺着的智能家居套装&…

从零开始学前端html篇1

1基本结构<!DOCTYPE html> <html><head><title>this is a good website</title></head><body><h1>hello!</h1></body> </html>运行效果如下&#xff08;编辑器提示waings:"缺少所需的 lang 特性"…

Redis Cluster 手动部署(小白的“升级打怪”成长之路)

目录 一、环境规划 二、基础环境 1、创建配置目录 2、生成配置文件 3、修改监听端口 4、修改数据目录 5、修改日志目录 6、修改PID文件目录 7、修改保护模式 8、修改进程运行模式 9、修改监听地址 10、生成集群配置 11、启动服务 三、构建集群 1、将其他节点加入…

【Java入门到精通】(三)Java基础语法(下)

一、面向对象&#xff08;类和对象&#xff09;1.1 万事万物皆对象类&#xff1a;对对象向上抽取出像的部分、公共的部分以此形成类&#xff0c;类就相当于一个模板。对象&#xff1a;模板下具体的产物可以理解为具体对象&#xff0c;对象就是一个一个具体的实例&#xff0c;就…

Java文件传输要点

Java文件传输要点 一、前端 <form action"/upload" method"post" enctype"multipart/form-data"> <!--<form action"/upload" method"post">-->姓名: <input type"text" name"username…

Spring Boot 中使用 Lombok 进行依赖注入的示例

Spring Boot 中使用 Lombok 进行依赖注入的示例 下面我将展示 Spring Boot 中使用 Lombok 进行依赖注入的不同方式&#xff0c;包括构造器注入、属性注入和 setter 方法注入&#xff0c;以及相应的测试用例。 1. 构造器注入&#xff08;推荐方式&#xff09; import lombok.Req…

vue3+vit+vue-router路由,侧边栏菜单,面包屑导航设置层级结构

文章目录注意效果图目录结构代码vite.config.ts需要配置路径别名符号main.tsApp.vueBreadcrumb.vue面包屑组件menus.ts// src/router/index.ts其他文件注意 目录结构仅供参考DefaultLayout.vue 没有用到&#xff0c;我直接写在APP文件中vux-store我也没有用到&#xff0c;单独…

使用Selenium自动化获取抖音创作者平台视频数据

前言 在当今短视频盛行的时代&#xff0c;抖音作为国内领先的短视频平台&#xff0c;吸引了大量内容创作者。对于创作者而言&#xff0c;了解自己发布的视频表现&#xff08;如播放量、发布时间等&#xff09;至关重要。本文将介绍如何使用Python的Selenium库来自动化获取抖音…

SpringCloud之Eureka

SpringCloud之Eureka 推荐参考&#xff1a;https://www.springcloud.cc/spring-cloud-dalston.html#_service_discovery_eureka_clients 1. 什么是Eureka Eureka 用于简化分布式系统的服务治理&#xff0c;基于REST的服务&#xff0c;用于服务的注册与发现。通过注册发现、客户…

squash压缩合并

要将test分支的多次提交合并到dev分支并压缩为一个commit&#xff0c;核心是使用 git merge --squash 命令&#xff08;压缩合并&#xff09;&#xff0c;具体步骤如下&#xff1a; 详细步骤&#xff1a; 1. 切换到dev分支并拉取最新代码先确保本地dev分支是最新的&#xff0c;…

飞书CEO谢欣:挑战巨头,打造AI新时代的Office

引言&#xff1a;飞书要做AI时代办公协作的逐梦者与破局者。文 | 大力财经在AI浪潮席卷的当下&#xff0c;企业对AI既满怀期待又充满焦虑。“AI到底能不能用&#xff1f;AI到底怎么用&#xff1f;”成为萦绕在众多企业心头的难题。7月9日召开的飞书未来无限大会&#xff0c;飞书…

React 组件中怎么做事件代理?它的原理是什么?

在 React 组件中&#xff0c;**事件代理&#xff08;Event Delegation&#xff09;**其实是 React 内部实现的一部分&#xff0c;开发者通常无需手动实现事件代理&#xff0c;但理解它的原理和使用方式对于优化性能和掌握底层机制非常重要。一、React 中事件代理的原理React 使…

Vue 2现代模式打包:双包架构下的性能突围战

文章目录一、场景痛点&#xff1a;兼容性与性能的撕裂二、技术解析&#xff1a;Modern Mode的双引擎驱动1. 基础认知&#xff1a;什么是Modern Mode&#xff1f;2. 原理深入&#xff1a;HTML智能分发与Safari 10修复3. 性能收益对比表三、Vue 2项目实战&#xff1a;启用Modern模…

UniHttp中HttpApiProcessor生命周期钩子介绍以及公共参数填充-以百度天气接口为例

目录 引言 一、UniHttp与HttpApiProcessor简介 1、生命周期钩子的重要性 2、公共参数填充的需求 3、生命周期钩子相关介绍 二、HttpApiProcessor的实际应用 1、在Yml中定义相关参数 2、自定义HttpAPI注解 3、对接接口的定义 4、HttpApiProcessor的具体实现 5、实际调…

pytorch深度学习—RNN-循环神经网络

结合生活实例&#xff0c;先简单认识一下什么是循环神经网络先想个问题&#xff1a;为什么需要 “循环”&#xff1f;你平时看句子、听语音、看视频&#xff0c;都是 “按顺序” 来的吧&#xff1f;比如 “我吃苹果” 和 “苹果吃我”&#xff0c;字一样但顺序不同&#xff0c;…

深度学习常见名词解释、评价指标

目录 一、鲁棒性(robustness) 二、泛化能力&#xff08;Generalization Ability&#xff09; 核心含义&#xff1a; 如何衡量泛化能力&#xff1f; 三、先验信息&#xff08;Prior Information&#xff09; 四、mIoU &#xff08;Mean Intersection over Union&#xff0…