構造一個高效的哈希表:從基本思路到最終實現

哈希表是計算機科學中常用的數據結構之一,它提供了快速的查找、插入和刪除操作。在本篇博客中,我們將探討如何構造一個高效的哈希表,從最基本的思路逐步完善,直至最終實現。

1. 初始思路:使用布爾數組存儲

我們最初的想法是使用一個布爾數組來存儲哈希表的元素。具體來說,數組中的每個元素代表一個可能的鍵值,如果某個鍵存在于哈希表中,則將對應位置的布爾值設為true,否則設為false。這種方法的代碼如下:

public class SimpleHashSet {private boolean[] present;public SimpleHashSet() {present = new boolean[2000000000];}public void add(int key) {present[key] = true;}public boolean contains(int key) {return present[key];}
}

這種方法的問題在于,它會浪費大量的內存空間,尤其是當哈希表中只有少量元素時。此外,它只能處理整數類型的鍵,無法處理其他類型的數據。

2. 使用哈希函數解決鍵的類型問題

為了解決只能處理整數類型鍵的問題,我們可以引入哈希函數將其他類型的鍵轉換為整數。下面是一個將字符串轉換為整數的示例:

public class DataIndexedEnglishWordSet {private boolean[] present;public DataIndexedEnglishWordSet() {present = new boolean[2000000000];}public void add(String key) {present[englishToInt(key)] = true;}public boolean contains(String key) {return present[englishToInt(key)];}private static int letterNum(String s, int i) {int ithChar = s.charAt(i);if (ithChar < 'a' || ithChar > 'z') {throw new IllegalArgumentException();}return ithChar - 'a' + 1;}private static int englishToInt(String s) {int intRep = 0;for (int i = 0; i < s.length(); i++) {intRep = intRep * 26;intRep += letterNum(s, i);}return intRep;}
}

雖然現在我們可以處理字符串類型的鍵,但仍然存在兩個主要問題:內存浪費和處理哈希沖突的困難。

3. 引入開放地址法解決沖突

為了解決哈希沖突的問題,我們引入了開放地址法中的線性探查。當插入的位置已被占用時,通過順序檢查數組的下一個位置來解決沖突。

public class DataIndexedEnglishWordSet {private boolean[] present;public DataIndexedEnglishWordSet() {present = new boolean[2000000000];}public void add(String key) {int index = englishToInt(key);while (present[index] != false) {index = (index + 1) % present.length;}present[index] = true;}public boolean contains(String key) {int index = englishToInt(key);while (present[index] != false) {if (present[index]) {return true;}index = (index + 1) % present.length;}return false;}private static int letterNum(String s, int i) {int ithChar = s.charAt(i);if (ithChar < 'a' || ithChar > 'z') {throw new IllegalArgumentException();}return ithChar - 'a' + 1;}private static int englishToInt(String s) {int intRep = 0;for (int i = 0; i < s.length(); i++) {intRep = intRep * 26;intRep += letterNum(s, i);}return intRep;}
}

通過引入線性探查,我們可以更有效地處理哈希沖突,提高了哈希表的性能。

4. 使用取模操作減少空間浪費

為了減少空間浪費,我們修改了哈希函數,使用取模操作將鍵映射到數組索引上。

public class DataIndexedEnglishWordSet {private boolean[] present;public DataIndexedEnglishWordSet() {present = new boolean[101]; // 使用最小素數作為初始容量}public void add(String key) {int index = englishToInt(key);while (present[index] != false) {index = (index + 1) % present.length;}present[index] = true;}public boolean contains(String key) {int index = englishToInt(key);while (present[index] != false) {if (present[index]) {return true;}index = (index + 1) % present.length;}return false;}private static int letterNum(String s, int i) {int ithChar = s.charAt(i);if (ithChar < 'a' || ithChar > 'z') {throw new IllegalArgumentException();}return ithChar - 'a' + 1;}private int englishToInt(String s) {int intRep = 0;for (int i = 0; i < s.length(); i++) {intRep = intRep * 26;intRep += letterNum(s, i);}return intRep % present.length; // 修改為取模操作}
}

通過取模操作,我們將鍵均勻地映射到數組索引上,減少了空間浪費。

5. 動態更改哈希表大小以優化性能

為了進一步優化性能,我們添加了動態調整哈希表大小的功能。當負載因子超過閾值時,我們將重構哈希表并擴大其容量。

public class ImprovedHashSet {private boolean[] present;private int size;private static final float LOAD_FACTOR_THRESHOLD = 0.75f;public ImprovedHashSet() {present = new boolean[101];size = 0;}public void add(String key) {if (loadFactor() >= LOAD_FACTOR_THRESHOLD) {resize();}int index = englishToInt(key);while (present[index] != false) {index = (index + 1) % present.length;}present[index] = true;size++;}public boolean contains(String key) {int index = englishToInt(key);while (present[index] != false) {if (present[index]) {return true;}index = (index + 1) % present.length;}return false;}private float loadFactor() {return (float) size / present.length;}private void resize() {boolean[] oldPresent = present;int newCapacity = nextPrime(oldPresent.length * 2);present = new boolean[newCapacity];size = 0;for (boolean value : oldPresent) {if (value) {add(someKey); // 重新插入元素,假設someKey為原哈希表的所有鍵}}}private int nextPrime(int n) {while (true) {if (isPrime(n)) {return n;}n++;}}private boolean isPrime(int n) {if (n <= 1) {return false;}if (n <= 3) {return true;}if (n % 2 == 0 || n % 3 == 0) {return false;}int i = 5;while (i * i <= n) {if (n % i == 0 || n % (i + 2) == 0) {return false;}i += 6;}return true;}private static int letterNum(String s, int i) {int ithChar = s.charAt(i);if (ithChar < 'a' || ithChar > 'z') {throw new IllegalArgumentException();}return ithChar - 'a' + 1;}private int englishToInt(String s) {int intRep = 0;for (int i = 0; i < s.length(); i++) {intRep = intRep * 26;intRep += letterNum(s, i);}return intRep % present.length;}
}

通過動態調整哈希表大小,我們可以根據負載因子的變化來合理分配內存,提高了哈希表的效率和性能。

總結

在本篇博客中,我們從最基本的思路出發,逐步完善了一個高效的哈希表的實現。我們通過引入哈希函數解決鍵的類型問題,使用開放地址法解決沖突,通過取模操作減少空間浪費,并最終實現了動態更改哈希表大小以

優化性能。這些優化措施使得我們的哈希表在處理不同類型的鍵和不同規模的數據時都能保持高效運行。

希望通過本文的介紹,你對構造高效的哈希表有了更深入的理解,也能在實際應用中更好地利用哈希表這一重要的數據結構。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/21961.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21961.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/21961.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AIGC 全面介紹

隨著人工智能技術的不斷進步&#xff0c;生成式人工智能&#xff08;AI Generated Content, AIGC&#xff09;成為了一個日益熱門的話題。AIGC 指利用人工智能技術生成各類內容&#xff0c;包括文本、圖像、音頻、視頻等。與傳統的內容生成方法相比&#xff0c;AIGC 具有速度快…

谷歌創新框架:從非結構化數據,實現多模態學習

看、聽、說的多模態已成為主流大模型的重要功能之一。但在數據爆炸時代&#xff0c;大模型學習文本類的結構化數據相對還好一些&#xff0c;但要去學習視頻、音頻、圖片等非結構化數據非常困難。 目前&#xff0c;從結構化和非結構化數據實現多模態學習&#xff0c;會隨著模態…

RK3588 VOP圖層分配介紹

RK3588 VOP圖層分配介紹 RK3588圖層介紹 RK3588有8個圖層&#xff0c;分別是Custer 0/1/2/3 和Esmart 0/1/2/3&#xff0c;兩種圖層的能力不一樣&#xff0c;具體如下&#xff1a; Custer 分辨率&#xff1a;最大分辨率包括兩種合并集群和單集群&#xff0c;分別為7680x432…

QT_UI設計

mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE //命名空間 namespace Ui { class MainWindow; } //ui_MainWindow文件里定義的類&#xff0c;外部聲明 QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_O…

AccessibilityEvent的生成和處理

在 Android 框架層&#xff0c;AccessibilityEvent 的生成和處理是通過系統的 UI 框架和輔助功能服務框架密切協作來實現的。這個機制涉及幾個關鍵的部分&#xff1a;UI 組件、輔助功能服務、事件監聽和事件分發。以下是對這些部分和它們如何協同工作的詳細解釋&#xff1a; 1…

httprunner接口自動化測試框架使用說明【保姆級教程】

背景介紹&#xff1a; httprunner是國內開源的一個接口自動化框架&#xff0c;已經有部分公司開始使用這種框架來完成自己公司的接口自動化編寫&#xff0c;本文主要是從簡單的流程上去講解咋使用的&#xff08;PS&#xff1a;開發者本尊的官網教程寫的是真的爛。。。&#xf…

JVM調優實戰

如果老年代能回收掉大部分&#xff0c;說明年輕代太小了&#xff0c;放不下 OOM 1數據量一次性申請的內存過多&#xff0c;比如數據庫查詢返回值大多&#xff0c;所以做個分頁 2.并發過高的情況下&#xff0c;一些連接未釋放 3.堆內存不夠

DP-Kmaens密度峰值聚類算法

我有個問題 關于 [密度值>密度閾值] 的判定這里&#xff0c;新進來的新數據怎么確定他的密度值&#xff1f;密度閾值又是怎樣確定的呢&#xff1f;

正則表達式 0.1v

正則表達式 擴展 --> :% s/\///g //文件里面所有的 / 去掉 * 通配符 \ //轉義&#xff0c;讓字符變成原本的意思 ^ //行首 $ //行尾 [0-9] //數字 [a-z] //小寫字母 [A-Z] //大寫字母 把文件的小寫字母替換為大寫字母&#xff1f; 固定寫法 :% s/[a-…

Vscode git 插件

超好用的git記錄 軟件 安裝之后&#xff0c;鼠標在哪一行就可以看最新一次是誰提交的&#xff0c;真的超好用&#xff01;&#xff01;&#xff01;

43頁 | 2024年企業級BI平臺白皮書(免費下載)

【1】關注本公眾號&#xff0c;轉發當前文章到微信朋友圈 【2】私信發送 2024年企業級BI平臺白皮書 【3】獲取本方案PDF下載鏈接&#xff0c;直接下載即可。 誠摯邀請您微信掃碼加入以下方案驛站知識星球&#xff0c;獲取上萬份PPT/WORD解決方案&#xff01;&#xff01;&…

【NOI】C++程序結構入門之循環結構二-for循環

文章目錄 前言一、for循環1.導入2.語法3.使用場景4.條件控制5.小結 二、例題講解問題&#xff1a;1264 - 4位反序數問題&#xff1a;1085 - 尋找雷劈數問題&#xff1a;1057 - 能被5整除且至少有一位數字是5的所有整數的個數問題&#xff1a;1392 - 回文偶數&#xff1f;問題&a…

Linux命令 netstat -anp | grep 的用法

文章目錄 1、第一種解釋2、第二種解釋3、第三種解釋4、第四種解釋5、第五種解釋6、netstat --help 在Windows中&#xff0c;殺死端口占用的博客鏈接 1、第一種解釋 在Unix和Linux系統中&#xff0c;netstat -anp 命令用于顯示所有的網絡連接&#xff08; -a 表示所有&#xff…

文件md5加密

使用場景&#xff1a;為了避免上傳資源空間的浪費&#xff0c;通過對文件進行md5摘要加密獲取唯一的值&#xff0c;從數據庫中查詢是否已有該md5碼存在&#xff0c;不存在的就上傳&#xff0c;存在的話使用之前已存儲的文件信息。 如何加密 下載插件browser-md5-file 【之前有…

maridb10.4.30數據庫數據遷移

1.新建數據存儲文件夾&#xff0c;例如E:\maridb_data 2.修改原數據所在目錄的my.ini文件&#xff0c;例如D:\Program Files\MariaDB 10.4\data\my.ini 3.剪切除my.ini文件外的其他所有文件到遷移目的地文件(E:\maridb_data) 結果如下&#xff1a; 原數據文件目錄&#xff1a…

聊聊限流的一些事兒

一、背景 最近幾年&#xff0c;隨著微服務的流行&#xff0c;服務與服務之間依賴越來越強&#xff0c;調用也越來越復雜&#xff0c;服務間的穩定性變突顯出來。特別是在遇到突發請求時&#xff0c;常常需要通過緩存、限流、熔斷降級、負載均衡等多種方式保證服務的穩定性。其…

C++命名空間(詳解)

C基礎語法 C基于C語言的改進&#xff1a;c在C語言的基礎上引入并擴充了面向對象的概念 C基礎概念&#xff1a;C是基于C語言而產生的,它即可以進行C語言的過程化程序設計,又可以進行以抽象數據類型為特點的基于對象的程序設計,還可以進行面向對象的程序設計 在1998年 出現C98…

愛普生差分晶振在光模塊中的重要角色

光模塊是現代通信設備中的重要組成部分&#xff0c;主要用于實現光電轉換和信號傳輸&#xff0c;它是一種將光信號轉換為電信號&#xff0c;或者將電信號轉換為光信號的設備。在光纖通信中&#xff0c;光模塊扮演著至關重要的角色。 光模塊的主要組成部分包括光源、光接收器、…

OSPF學習筆記(狀態機)

1、鄰居關系 OSPF設備啟動后&#xff0c;會通過OSPF接口向外發送Hello報文&#xff0c;收到Hello報文的OSPF設備會檢查報文中所定義的參數&#xff0c;如果雙方一致就會形成鄰居關系&#xff0c;兩端設備互為鄰居 2、鄰接關系 形成鄰居關系后&#xff0c;如果兩端設備成功交…

【代碼隨想錄】【算法訓練營】【第27天】 [39]組合總和 [40] 組合總和II [131]分割回文串

前言 思路及算法思維&#xff0c;指路 代碼隨想錄。 題目來自 LeetCode。 day26&#xff0c; 休息的周末~ day 27&#xff0c;周一&#xff0c;庫存沒了&#xff0c;哭死~ 題目詳情 [39] 組合總和 題目描述 39 組合總和 解題思路 前提&#xff1a;組合的子集問題&…