mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-25 09:37:14 +00:00
build
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
---
|
||||
comments: true
|
||||
hide: [edit]
|
||||
---
|
||||
|
||||
<!-- 此檔案由 utils/exercises/publish_exercises.py 根據 exercises.yaml 自動生成,請勿直接修改。 -->
|
||||
|
||||
# 6.5 練習
|
||||
|
||||
## 6.5.1 知識鞏固
|
||||
|
||||
### 1. 發生雜湊衝突後怎樣查詢
|
||||
|
||||
一個雜湊表有 5 個桶,雜湊函式為 $h(x)=x \bmod 5$,衝突時把元素依次放進該桶的串列中。
|
||||
依次插入 `[1, 6, 11, 7]`:
|
||||
|
||||
<!-- numbered-subquestions -->
|
||||
|
||||
1. 寫出 0~4 號桶中的內容;
|
||||
2. 查詢 6 時會先進入哪個桶,並依次檢查哪些元素?
|
||||
3. 根據第 1 問寫出的桶內容,後插入的元素是否覆蓋了先插入的元素?結合這種衝突處理方式說明理由。
|
||||
|
||||
??? success "參考答案"
|
||||
|
||||
1. 因為 $1\bmod5=6\bmod5=11\bmod5=1$,而 $7\bmod5=2$,各桶為:
|
||||
|
||||
```text
|
||||
0: []
|
||||
1: [1, 6, 11]
|
||||
2: [7]
|
||||
3: []
|
||||
4: []
|
||||
```
|
||||
|
||||
2. 查詢 6 時先進入 1 號桶,再依次比較 1、6,第二次比較時找到目標。
|
||||
|
||||
3. 雜湊值相同只表示它們落入同一個桶,並不表示這些元素相等。鏈式位址把衝突元素都儲存在桶內,
|
||||
查詢時再逐個比較,因此 1、6、11 不會互相覆蓋。
|
||||
|
||||
### 2. 雜湊表擴容後元素去哪兒
|
||||
|
||||
一個使用鏈式位址的雜湊表原有 5 個桶,雜湊函式為 $h(x)=x\bmod5$。
|
||||
鍵 `[1, 6, 11]` 都在 1 號桶中。
|
||||
|
||||
現在把雜湊表擴容為 7 個桶,雜湊函式相應變為 $h(x)=x\bmod7$:
|
||||
|
||||
<!-- numbered-subquestions -->
|
||||
|
||||
1. 分別計算 1、6、11 的新桶號。
|
||||
2. 擴容後哪些桶中有元素?
|
||||
3. 擴容時能否把原來 1 號桶中的串列原樣複製到新的 1 號桶?結合第 1、2 問的結果說明理由。
|
||||
|
||||
??? success "參考答案"
|
||||
|
||||
1. 新桶號分別為:
|
||||
|
||||
- $1\bmod7=1$;
|
||||
- $6\bmod7=6$;
|
||||
- $11\bmod7=4$。
|
||||
|
||||
2. 1 號桶儲存 1,4 號桶儲存 11,6 號桶儲存 6。三個鍵不再擠在同一個桶中。
|
||||
|
||||
3. 不能原樣複製。桶號由“鍵對桶數取餘”得到。桶數從 5 變成 7 後,同一個鍵的新桶號可能改變,
|
||||
因此必須重新計算每個鍵的位置。若把舊的 1 號桶原樣複製過去,之後按新公式查詢 6 和 11 時,
|
||||
會分別前往 6 號桶和 4 號桶,從而找不到它們。
|
||||
|
||||
### 3. 刪除 6 後還能找到 11 嗎
|
||||
|
||||
一個雜湊表有 5 個位置,索引為 `0~4`,雜湊函式為 $h(x)=x\bmod5$。
|
||||
發生衝突時,從雜湊函式算出的索引開始,向右尋找第一個空位。
|
||||
|
||||
依次插入 `[1, 6, 11]`:
|
||||
|
||||
<!-- numbered-subquestions -->
|
||||
|
||||
1. 三個數最終分別放在哪個索引?
|
||||
2. 查詢 11 時,會依次檢查哪些索引?
|
||||
3. 如果刪除 6 時直接把它的位置改成“從未使用的空位”,而查詢遇到空位就停止,
|
||||
再查詢 11 時會發生什麼?這個查詢結果是否正確?如果有問題,應怎樣避免?
|
||||
|
||||
??? success "參考答案"
|
||||
|
||||
1. 1 放在索引 1。6 也對映到索引 1,發生衝突後放在索引 2。
|
||||
11 同樣從索引 1 開始,依次跳過已佔用的索引 1、2,最終放在索引 3。
|
||||
|
||||
2. 查詢 11 時依次檢查索引 `1、2、3`,在索引 3 找到它。
|
||||
|
||||
3. 如果把索引 2 改成表示“從未使用”的空位,查詢 11 時檢查索引 1 後就會在索引 2 停止,
|
||||
從而錯誤地認為 11 不存在。刪除時應留下“已刪除”標記:
|
||||
查詢遇到該標記時繼續檢查下一個索引(越過索引 4 後回到索引 0),而以後的插入仍可重新使用這個位置。
|
||||
|
||||
## 6.5.2 程式設計練習
|
||||
|
||||
### 1. 比較兩個字串的字元組成
|
||||
|
||||
給定兩個只含小寫英文字母的字串 `s` 和 `t`。
|
||||
可以任意調整 `s` 中字元的位置,但不能新增、刪除或替換字元。
|
||||
|
||||
請判斷調整後能否得到 `t`;可以則返回 `true`,否則返回 `false`。
|
||||
請使用雜湊表記錄各字母的出現次數,不對字串中的字元排序。
|
||||
|
||||
??? tip "解題提示"
|
||||
|
||||
1. 兩個字串長度不同,它們的字元組成一定不同
|
||||
2. 用雜湊表記錄每種字母的數量;掃描 s 時把對應計數加 1
|
||||
3. 掃描 t 時把對應計數減 1;所有計數最終都為 0,字元組成才相同
|
||||
|
||||
[LeetCode](https://leetcode.cn/problems/valid-anagram/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } [題目解析](https://leetcode.cn/problems/valid-anagram/solutions/2362065/242-you-xiao-de-zi-mu-yi-wei-ci-ha-xi-bi-cch7/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Executable
+1788
File diff suppressed because one or more lines are too long
@@ -0,0 +1,22 @@
|
||||
---
|
||||
comments: true
|
||||
icon: material/table-search
|
||||
---
|
||||
|
||||
# 第 6 章 雜湊表
|
||||
|
||||
{ class="cover-image" }
|
||||
|
||||
!!! abstract
|
||||
|
||||
在計算機世界中,雜湊表如同一位聰慧的圖書管理員。
|
||||
|
||||
他知道如何計算索書號,從而可以快速找到目標圖書。
|
||||
|
||||
## 本章內容
|
||||
|
||||
- [6.1 雜湊表](hash_map.md)
|
||||
- [6.2 雜湊衝突](hash_collision.md)
|
||||
- [6.3 雜湊演算法](hash_algorithm.md)
|
||||
- [6.4 小結](summary.md)
|
||||
- [6.5 練習](exercises.md)
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 6.4 小結
|
||||
|
||||
### 1. 重點回顧
|
||||
|
||||
- 輸入 `key` ,雜湊表能夠在 $O(1)$ 時間內查詢到 `value` ,效率非常高。
|
||||
- 常見的雜湊表操作包括查詢、新增鍵值對、刪除鍵值對和走訪雜湊表等。
|
||||
- 雜湊函式將 `key` 對映為陣列索引,從而訪問對應桶並獲取 `value` 。
|
||||
- 兩個不同的 `key` 可能在經過雜湊函式後得到相同的陣列索引,導致查詢結果出錯,這種現象被稱為雜湊衝突。
|
||||
- 雜湊表容量越大,雜湊衝突的機率就越低。因此可以透過擴容雜湊表來緩解雜湊衝突。與陣列擴容類似,雜湊表擴容操作的開銷很大。
|
||||
- 負載因子定義為雜湊表中元素數量除以桶數量,反映了雜湊衝突的嚴重程度,常用作觸發雜湊表擴容的條件。
|
||||
- 鏈式位址透過將單個元素轉化為鏈結串列,將所有衝突元素儲存在同一個鏈結串列中。然而,鏈結串列過長會降低查詢效率,可以透過進一步將鏈結串列轉換為紅黑樹來提高效率。
|
||||
- 開放定址透過多次探測來處理雜湊衝突。線性探查使用固定步長,缺點是不能刪除元素,且容易產生聚集。多次雜湊使用多個雜湊函式進行探測,相較線性探查更不易產生聚集,但多個雜湊函式增加了計算量。
|
||||
- 不同程式語言採取了不同的雜湊表實現。例如,Java 的 `HashMap` 使用鏈式位址,而 Python 的 `Dict` 採用開放定址。
|
||||
- 在雜湊表中,我們希望雜湊演算法具有確定性、高效率和均勻分佈的特點。在密碼學中,雜湊演算法還應該具備抗碰撞性和雪崩效應。
|
||||
- 雜湊演算法通常採用大質數作為模數,以最大化地保證雜湊值均勻分佈,減少雜湊衝突。
|
||||
- 常見的雜湊演算法包括 MD5、SHA-1、SHA-2 和 SHA-3 等。MD5 常用於校驗檔案完整性,SHA-2 常用於安全應用與協議。
|
||||
- 程式語言通常會為資料型別提供內建雜湊演算法,用於計算雜湊表中的桶索引。通常情況下,只有不可變物件是可雜湊的。
|
||||
|
||||
### 2. Q & A
|
||||
|
||||
**Q**:雜湊表的時間複雜度在什麼情況下是 $O(n)$ ?
|
||||
|
||||
當雜湊衝突比較嚴重時,雜湊表的時間複雜度會退化至 $O(n)$ 。當雜湊函式設計得比較好、容量設定比較合理、衝突比較平均時,時間複雜度是 $O(1)$ 。我們使用程式語言內建的雜湊表時,通常認為時間複雜度是 $O(1)$ 。
|
||||
|
||||
**Q**:為什麼不使用雜湊函式 $f(x) = x$ 呢?這樣就不會有衝突了。
|
||||
|
||||
在 $f(x) = x$ 雜湊函式下,每個元素對應唯一的桶索引,這與陣列等價。然而,輸入空間通常遠大於輸出空間(陣列長度),因此雜湊函式的最後一步往往是對陣列長度取模。換句話說,雜湊表的目標是將一個較大的狀態空間對映到一個較小的空間,並提供 $O(1)$ 的查詢效率。
|
||||
|
||||
**Q**:雜湊表底層實現是陣列、鏈結串列、二元樹,但為什麼效率可以比它們更高呢?
|
||||
|
||||
首先,雜湊表的時間效率變高,但空間效率變低了。雜湊表有相當一部分記憶體未使用。
|
||||
|
||||
其次,只是在特定使用場景下時間效率變高了。如果一個功能能夠在相同的時間複雜度下使用陣列或鏈結串列實現,那麼通常比雜湊表更快。這是因為雜湊函式計算需要開銷,時間複雜度的常數項更大。
|
||||
|
||||
最後,雜湊表的時間複雜度可能發生劣化。例如在鏈式位址中,我們採取在鏈結串列或紅黑樹中執行查詢操作,仍然有退化至 $O(n)$ 時間的風險。
|
||||
|
||||
**Q**:多次雜湊有不能直接刪除元素的缺陷嗎?標記為已刪除的空間還能再次使用嗎?
|
||||
|
||||
多次雜湊是開放定址的一種,開放定址法都有不能直接刪除元素的缺陷,需要透過標記刪除。標記為已刪除的空間可以再次使用。當將新元素插入雜湊表,並且透過雜湊函式找到標記為已刪除的位置時,該位置可以被新元素使用。這樣做既能保持雜湊表的探測序列不變,又能保證雜湊表的空間使用率。
|
||||
|
||||
**Q**:為什麼在線性探查中,查詢元素的時候會出現雜湊衝突呢?
|
||||
|
||||
查詢的時候透過雜湊函式找到對應的桶和鍵值對,發現 `key` 不匹配,這就代表有雜湊衝突。因此,線性探查法會根據預先設定的步長依次向下查詢,直至找到正確的鍵值對或無法找到跳出為止。
|
||||
|
||||
**Q**:為什麼雜湊表擴容能夠緩解雜湊衝突?
|
||||
|
||||
雜湊函式的最後一步往往是對陣列長度 $n$ 取模(取餘),讓輸出值落在陣列索引範圍內;在擴容後,陣列長度 $n$ 發生變化,而 `key` 對應的索引也可能發生變化。原先落在同一個桶的多個 `key` ,在擴容後可能會被分配到多個桶中,從而實現雜湊衝突的緩解。
|
||||
|
||||
**Q**:如果為了高效的存取,那麼直接使用陣列不就好了嗎?
|
||||
|
||||
當資料的 `key` 是連續的小範圍整數時,直接用陣列即可,簡單高效。但當 `key` 是其他型別(例如字串)時,就需要藉助雜湊函式將 `key` 對映為陣列索引,再透過桶陣列儲存元素,這樣的結構就是雜湊表。
|
||||
Reference in New Issue
Block a user