算法學習筆記:16.哈希算法 ——從原理到實戰,涵蓋 LeetCode 與考研 408 例題

在計算機科學中,哈希算法(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…