哈希表實現(1):

1. 哈希:

之前我們的紅黑數的查找是由于左邊小右邊大的原則可以快速的查找,我們這里的哈希表呢?

這里是用過哈希函數把關鍵字key和存儲位置建立一個關聯的映射。

直接定址法(函數函數定義的其中一種):

直接定址法是我們設置哈希函數的第一種方法:

直接定址法的限制就是適用范圍比較集中的時候,如果范圍不集中的話,我們就不能使用直接定址法。

負載因子:

負載因子其實表示的哈希表的空間的利用率,負載因子越大的時候,空間利用率就越大,哈希沖突的概率就越大,負載因子越小的時候,空間利用率越小,哈希沖突的概率就越小。

要注意這里是概率,不是一定的沖突就大或者小,有可能這批數據剛好比較合適就沒有啥沖突。

哈希沖突:

直接定址法是不會有哈希沖突的,直接定址法適用于數據范圍比較集中,直接定址法是會給每個值都分配一個空間的。所以不會有哈希沖突。

但是直接定址法的話,我們當然不能一直使用這個,當我們的其他的環境下,我們的數據并不集中的話,我們其實常常使用的是其他的構建哈希函數的方法,他可能會導致幾個數據映射到同一個位置上去,導致哈希沖突。

哈希函數:

我們接下來看定義我們的哈希函數的方法:

除法散列法/除留余數法:

除留余數法,我們要讓所有的值都映射到M個空間里面,M是哈希表的大小,我們就讓關鍵值key取模M,那么他取模得到的值一定是M-1這個范圍里面的某一個,這就映射出了一個位置。

我們看上面的圖片,我們看第二點,他說我們的哈希表的大小M要盡量避免2的冪和10的冪。因為我們要計算數據映射的位置的時候,我們是要使用數據來除以哈希表的大小M來得到的。如果使用他們的話就會導致沖突比較大。

除留余數法的話就比直接定址法好多了,我們不需要管數據的范圍的大小,我們只要開比數據個數要大的空間就可以。

這就是除留余數法(M是哈希表的大小),19%M得到8,19就映射到8這個位置,然后其他的數據也是一樣的,取模M得到一個映射的位置,但是我們的看到30取模M的結果得到的也是8,這個就是我們的哈希沖突。

哈希沖突是不可避免的,我們接下來要講解如果處理哈希沖突;

我們還有其他的構建哈希函數的方法,乘法散列法和全域散列法,這兩個的話我們知道了解一下就行,不進行細講。

處理哈希沖突:

開放定址法:

當?個關鍵字key?哈希函數計算出的位置沖突了,則按照某種規則找到?個沒有存儲數據的位置進?存儲。簡單的說就是我們的這個數據映射出的位置被別人占了,我們就找一個新的位置占上。

這個找新位置的規則我們分成三種:線性探測,二次探測,雙重探測。

我們主要學的是線性探測。

線性探測就是映射到這個位置但是這個位置被占了,我們就從這個發生沖突的位置開始,我們就依次的往后走,直到遇到一個空的位置,我們就占到這個位置上(如果走到哈希表的尾了,我們就繞到頭上去)。

當我們的映射的這個位置發生沖突被別的數據占據了之后,我們的公式就是給哈希這個位置+1然后繼續取模往后走,走到哈希表的尾了,但是我們是取模進行移動的,比如圖中的,hash0+i從10變成11的時候,他取模的到的結果就可以跳轉回到頭部。

開放定址法代碼實現:
insert():

Find():

Erase():

素數表:

我們之前說我們要使用素數來設置哈希表的大小,但是如果這個哈希表滿了以后,我們需要擴容的時候,我們一般都會給這個哈希表*2來進行擴容,這時候的到的就不是一個素數,可能會導致哈希沖突變大。

我們的庫里面就給出了一個不太接近2的素數表。

你看下面的那個是一個lower_bound的函數,這個就是找大于等于的,我們在下面的擴容里面實現的話,我們就可以調用這個素數表的函數,他每次擴容的話,就走到了下一個素數的位置。

當前的size()+1的話,里面的lower_bound是會給你找比這個大或者相等的數據的,假如現在的size()是53,你給他+1,得到54,那么他就會找比54大的數據來進行的。

key不能取模的問題:

我們這里還有一個問題那就是key如果不是我們的int類型的時候,我們建立哈希函數的話,我們是必須要使用除留余數法的,但是如果key不是int類型的,那怎么取模呢?

我們看這個函數,他是絕對插入不到我們的哈希表里面去的,我們的insert函數,我們是要使用到dict.first進行判斷的。(這里編譯就會報錯);

那我們這里怎么解決呢?

我們這里就要走兩層映射,首先把string映射成int類型的,然后把int映射到哈希表的位置上。

那怎么把string類型的映射成int類型的數據呢?我們可以把string里面的每一個字母的ASCII值加起來,這個邏輯還比較合適。

那實現怎么來實現呢?

我們就實現一個仿函數來進行我們的轉化,

我們先看下面的這個圖片:

我們給我們的模板的第三個參數是我們的Hash仿函數,我們給他傳一個默認的缺省函數HashFunc函數,這個函數的話,我們會把傳進來的類型轉為size_t類型的數據,但是有的類型他是轉換不成size_t類型的數據的,這時候我們就要自己來手動的實現一個仿函數來進行轉換。

這個就是我們實現的仿函數,我們把這個仿函數傳進去。

還記得我們之前的仿函數是怎樣使用的呢?

我們的仿函數實例化出的對象我們可以直接當作函數來進行使用,看上面Hash仿函數實例化對象hs以后,hs(key)這個就是直接調用仿函數,把key數據轉換成int類型的可以取模的數據。

這時候看我們的pair鍵值對的類型是string類型的,這個類型轉成int類型的話,我們就要傳我們的自己的仿函數進去才行。

你傳其他類型的key也能用,但是的話,你要配一個仿函數類幫助他可以進行取模(必須要能取模,這是構建哈希函數的必要途徑)。

我們繼續往下看:

我們看,我們剛才自己實現的string轉換為int的仿函數,我們是讓所有的字母的ASCII加起來,但是這樣的話,我們看上面的圖片,這三個string的順序不一樣,但是他們的ASCII是一樣的,最后導致他們映射的int是同一個,這就導致了沖突,那我們的這個仿函數是不是就顯得沒有那么好呢?

那有沒有剛好的方法來實現這個仿函數,有的,有人提出了BKDRHash方法來進行:

這樣我們加起來的ASCII相同的不同順序的字符串,除非是這兩個ASCII值相等的字符串順序都一樣,不然最后計算得到的結果不可能一樣。

我們繼續往下看:

當我們的容器是我們的unordered_map,這個容器的底層是哈希表實現的,我們給他的key傳上string的時候,它不需要仿函數就可以通過運行,但是我們的HashTables我們就要加上仿函數才可以。

要知道我們的string是要經常使用的,經常使用的話,我們想辦法讓key默認的支持string轉化,我們可以使用一個特化來實現。

這個就是特化的實現,當我們的key是string類型的時候,他就是走特化,就不需要仿函數,我們的unordered_map使用的是庫里面的,他的底層是由哈希表封裝的,庫里面已經把這個string的特化實現過了,我們這里調用unordered_map給key傳string的話,他就不需要仿函數。

但是我們的這里的哈希表是我們自己在進行實現,我們沒有實現特化,我們就要仿函數。

我們看這個特化,特化的上面是我們的仿函數,我們的哈希表可以傳各種類型的數據進來,然后我們傳仿函數,把各種類型轉換為size_t類型的。也可以不傳仿函數,把string的特化出來,我們傳string類型的數據進來后就直接調用特化的模板了。

現在我們這樣就沒事,就可以了,我們已經特化了string類型的數據,可以不傳仿函數。

我們繼續往下看:

我們看這個:

當我們的調用庫里面的unordered_map的時候,我們傳K傳pair<>鍵值對,這時候也是有問題的,庫里面沒有實現pair<>鍵值對的特化,如果想要這種的話,還是需要你手動的實現仿函數。

看這個特化,為了防止你1,3和3,1算出來的值是一樣的,減少哈希沖突的發生,使用BKDRHash來實現。

然后接著我們傳仿函數進去,然后我們的鍵值對存1,3和3,1兩個數據,實現哈希表的話,這兩個數據分別進行存儲,我們當然不想讓他產生哈希沖突,我們上面的仿函數就使用BKDRHash來實現。

我們看我們的f第二種解決哈希沖突的方法:

鏈地址法:

這個叫作拉鏈法,鏈地址法,這個是非常重要的解決哈希沖突的方法。

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

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

相關文章

泰迪杯特等獎案例深度解析:基于多級二值化與CNN回歸的車牌識別系統設計

(第八屆泰迪杯數據挖掘挑戰賽特等獎案例全流程拆解) 一、案例背景與核心挑戰 1.1 行業痛點與場景需求 在智慧交通與無感支付場景中,車牌識別是核心環節。傳統車牌識別系統在復雜光照、污損車牌、多角度傾斜等場景下存在顯著缺陷。根據某智慧油站2024年運營數據顯示,高峰期…

光學變焦和數字變倍模塊不同點概述!

一、光學變焦與數字變倍模塊的不同點 1. 物理基礎 光學變焦&#xff1a;通過調整鏡頭組中鏡片的物理位置改變焦距&#xff0c;實現無損放大。例如&#xff0c;上海墨揚的MF-STAR吊艙采用30倍光學變焦鏡頭&#xff0c;焦距范圍6~180mm&#xff0c;等效焦距可達997mm。 數字…

ECMAScript標準:JavaScript的核心

什么是ECMAScript&#xff1f; ECMAScript&#xff08;簡稱ES&#xff09;是一個由ECMA國際&#xff08;歐洲計算機制造商協會&#xff09;制定的腳本語言標準&#xff0c;它為JavaScript、JScript和ActionScript等腳本語言提供了基礎規范。JavaScript 可以視為 ECMAScript 的…

小白學AI DeepSeep 部署中的常見問題及解決方法

在部署 DeepSeek(或類似的大模型/AI 系統)時,可能會遇到多種技術或環境相關的問題。以下是常見問題及對應的解決方案,結合實際部署經驗總結: 文章目錄 前言一、 硬件資源不足二、環境配置問題三、模型加載或推理失敗四、網絡或分布式訓練問題五、數據加載或預處理問題六、…

redis數據結構-11(了解 Redis 持久性選項:RDB 和 AOF)

了解 Redis 持久性選項&#xff1a;RDB 和 AOF Redis 提供了多個持久性選項&#xff0c;以確保數據持久性并防止在服務器發生故障或重啟時丟失數據。了解這些選項對于為您的特定使用案例選擇正確的策略、平衡性能和數據安全至關重要。本章節將深入探討 Redis 中的兩種主要持久…

LLaMA-Factory:環境準備

一、硬件和系統 操作系統: Ubuntu 24.04.2 LTS&#xff08;64位&#xff09;GPU: NVIDIA RTX 4090 筆記本 GPU&#xff0c;16GB顯存CPU: 建議高性能多核 CPU&#xff08;如 Intel i7/i9 或 AMD Ryzen 7/9&#xff09;以支持數據預處理&#xff0c;我的是32核。RAM: 至少 32GB&…

2025 uniapp的請求封裝工具類以及使用【拿來就用】

一、創建一個http請求封裝的js文件&#xff0c;名字自定義&#xff1a;my_http.js /*** 基礎API請求地址&#xff08;常量&#xff0c;全大寫命名規范&#xff09;* type {string}* constant*/ let BASE_URL //通過環境來判斷基礎路徑 if (process.env.NODE_ENV development…

Qt應用程序啟動時的一些思路:從單實例到性能優化的處理方案

程序啟動時優化的價值 在桌面軟件開發領域,應用程序的啟動過程就像音樂的序曲,決定了用戶對軟件品質的第一印象。比如首次啟動等待超過3秒時,會讓大多數用戶產生負面看法,而專業工具軟件的容忍閾值甚至更低。Qt框架作為跨平臺開發的利器,其啟動過程的優化不僅關乎用戶體驗…

Node.js入門指南:開啟JavaScript全棧開發之旅

Hi&#xff0c;我是布蘭妮甜 &#xff01;Node.js讓JavaScript突破了瀏覽器的限制&#xff0c;成為全棧開發的利器。作為基于V8引擎的高性能運行時&#xff0c;它徹底改變了JavaScript只能做前端開發的局面。本文將帶你快速掌握Node.js的核心用法&#xff1a;環境搭建與模塊系統…

MySQL MCP 使用案例

## 概述 MySQL MCP&#xff08;MySQL Multi-Channel Protocol&#xff09;是MySQL的多通道協議實現&#xff0c;提供了高效的數據庫連接池和負載均衡功能。本文檔將介紹MySQL MCP的基本使用方法和常見案例。 ## 環境準備 ### 安裝MySQL MCP bash pip install mysql-mcp ### 基…

基于 React Hook 封裝 Store 的三種方案

基于 React Hook 封裝 Store 的三種方案 方案一&#xff1a;基于 useSyncExternalStore 的輕量級 Store&#xff08;推薦&#xff09; import { useSyncExternalStore } from react;type Store<T> {state: T;listeners: Set<() > void>; };function createSt…

MySQL 8.0 OCP 1Z0-908 131-140題

Q131.You have upgraded the MySQL binaries from 5.7.28 to 8.0.18 by using an in-place upgrade. Examine the message sequence generated during the first start of MySQL 8.0.18: 。。。[System]。。。/usx/sbin/mysqld (mysqld 8.0.18-commercial) starting as process…

正向代理和反向代理的區別?

前言 在現代網絡架構中&#xff0c;代理服務器扮演著至關重要的角色。無論是企業網絡還是互聯網服務&#xff0c;代理技術都廣泛應用以提高性能、安全性和可管理性。正向代理和反向代理是兩種最常見的代理類型&#xff0c;雖然它們都作為中間人處理客戶端和服務器之間的通信&am…

技術融資:概念與形式、步驟與案例、挑戰與應對、發展趨勢

一、技術融資概述 技術融資是指通過外部資金支持技術研發、產品開發或市場擴展的過程。它通常涉及風險投資、天使投資、私募股權、眾籌等多種形式。技術融資的核心目標是為技術創新提供資金保障&#xff0c;推動技術從概念到市場的轉化。 技術融資的主要形式包括以下幾種&…

從硬件角度理解“Linux下一切皆文件“,詳解用戶級緩沖區

目錄 前言 一、從硬件角度理解"Linux下一切皆文件" 從理解硬件是種“文件”到其他系統資源的抽象 二、緩沖區 1.緩沖區介紹 2.緩沖區的刷新策略 3.用戶級緩沖區 這個用戶級緩沖區在哪呢&#xff1f; 解釋關于fork再加重定向“>”后數據會打印兩份的原因 4.內核緩沖…

車道線檢測----CLRERNet

CLRerNet&#xff1a;利用LaneIoU提升車道檢測置信度 摘要 車道標檢測在自動駕駛和駕駛輔助系統中至關重要。現代深度車道檢測方法在車道檢測基準測試中表現出色。通過初步的預言機實驗&#xff0c;我們首次拆解車道表示組件以確定研究方向。我們表明&#xff0c;正確的車道位…

ML307R 的 USB Vendor ID (VID):0x2ECC ML307R 的 USB Product ID (PID):0x3012

可以的&#xff0c;在文檔的「Table 3. VID、PID查詢表」中明確指出&#xff1a; ML307R 的 USB Vendor ID (VID)&#xff1a;0x2ECCML307R 的 USB Product ID (PID)&#xff1a;0x3012 你可以將這對 VID/PID 加到 Linux 的 option 驅動中&#xff0c;比如&#xff1a; ech…

論信息系統項目的范圍管理

論信息系統項目的范圍管理 前言一、規劃范圍管理&#xff0c;收集需求二、定義范圍三、創建工作分解結構四、確認范圍五、控制范圍 前言 為了應對煙草零售客戶數量大幅度增長所帶來的問題&#xff0c;切實履行控煙履約的相關要求&#xff0c;同時也為了響應國務院“放管服”政策…

MongoDB與PostgreSQL兩個數據庫的特點詳細對比

MongoDB 和 PostgreSQL 是兩種不同類型的數據庫&#xff0c;分別屬于 ??NoSQL&#xff08;文檔型&#xff09;?? 和 ??關系型&#xff08;SQL&#xff09;?? 數據庫。它們在數據模型、查詢語言、擴展性、事務支持等方面有顯著差異。以下是詳細對比&#xff1a; ??1. …

計算機網絡:什么是電磁波以及有什么危害?

電磁波詳解 電磁波(Electromagnetic Wave)是由電場和磁場相互激發、在空間中傳播的能量形式。它既是現代通信的基石(如手機、Wi-Fi、衛星信號),也是自然界中光、熱輻射等現象的本質。以下從定義、產生、特性、分類及應用全面解析: 一、電磁波的本質 1. 核心定義 電場與…