哈希表(Hash Table)
哈希表是一種高效率的數據結構,能夠以接近 O(1) 的時間複雜度進行數據查找、插入和刪除操作。它廣泛應用於數據庫索引、快取系統、符號表等場景,是現代編程中最重要的數據結構之一。
基本原理
哈希表基於「鍵值對」(key-value pair)的概念,它使用一個特殊的函數(哈希函數)將鍵轉換為數組索引,然後將值存儲在該索引位置。
核心組件
- 哈希函數:將鍵轉換為數組索引的函數
- 數組:存儲數據的物理結構
- 衝突解決策略:處理不同鍵產生相同索引的情況
哈希函數
好的哈希函數應當滿足以下特性:
- 計算速度快
- 分布均勻,最小化衝突
- 確定性,相同輸入產生相同輸出
一個簡單的哈希函數示例(針對字符串):
int hash(char* key, int table_size) {
unsigned int hash_value = 0;
while (*key) {
hash_value = (hash_value * 31) + *key++;
}
return hash_value % table_size;
}
衝突解決
當兩個不同的鍵產生相同的哈希值(稱為「衝突」)時,我們需要有策略來解決這個問題:
1. 鏈接法(Chaining)
每個數組位置存儲一個鏈表,所有哈希到同一位置的元素都放入這個鏈表中。
[0] -> (key1, value1) -> (key4, value4)
[1] -> (key2, value2)
[2] -> (key3, value3) -> (key5, value5) -> (key6, value6)
2. 開放尋址法(Open Addressing)
當發生衝突時,查找數組中的另一個位置來存儲元素。常見的方法有:
- 線性探測:檢查下一個位置,再下一個,依此類推
- 二次探測:使用二次函數計算探測序列
- 雙重哈希:使用第二個哈希函數計算步長
哈希表操作的時間複雜度
操作 | 平均情況 | 最壞情況 |
---|---|---|
查找 | O(1) | O(n) |
插入 | O(1) | O(n) |
刪除 | O(1) | O(n) |
最壞情況發生在所有鍵都哈希到同一位置時。
實現示例(JavaScript)
以下是一個簡單的哈希表實現:
class HashTable {
constructor(size = 53) {
this.keyMap = new Array(size);
}
_hash(key) {
let total = 0;
let PRIME = 31;
for (let i = 0; i < Math.min(key.length, 100); i++) {
let char = key[i];
let value = char.charCodeAt(0) - 96;
total = (total * PRIME + value) % this.keyMap.length;
}
return total;
}
set(key, value) {
let index = this._hash(key);
if (!this.keyMap[index]) {
this.keyMap[index] = [];
}
this.keyMap[index].push([key, value]);
}
get(key) {
let index = this._hash(key);
if (this.keyMap[index]) {
for (let i = 0; i < this.keyMap[index].length; i++) {
if (this.keyMap[index][i][0] === key) {
return this.keyMap[index][i][1];
}
}
}
return undefined;
}
keys() {
let keysArr = [];
for (let i = 0; i < this.keyMap.length; i++) {
if (this.keyMap[i]) {
for (let j = 0; j < this.keyMap[i].length; j++) {
if (!keysArr.includes(this.keyMap[i][j][0])) {
keysArr.push(this.keyMap[i][j][0]);
}
}
}
}
return keysArr;
}
values() {
let valuesArr = [];
for (let i = 0; i < this.keyMap.length; i++) {
if (this.keyMap[i]) {
for (let j = 0; j < this.keyMap[i].length; j++) {
if (!valuesArr.includes(this.keyMap[i][j][1])) {
valuesArr.push(this.keyMap[i][j][1]);
}
}
}
}
return valuesArr;
}
}
// 使用示例
const ht = new HashTable();
ht.set("红色", "#FF0000");
ht.set("藍色", "#0000FF");
ht.set("綠色", "#00FF00");
ht.get("藍色"); // 返回 "#0000FF"
哈希表優化策略
- 負載因子控制:當元素數量與表大小的比值(負載因子)超過閾值時,需要擴容並重新哈希
- 好的哈希函數:選擇分布均勻的哈希函數
- 適當的初始容量:根據預計數據量設置初始容量
常見應用
- 數據庫索引:快速查找記錄
- 快取系統:如 Redis、Memcached
- 集合(Set)的實現:檢查成員資格
- 計數器:例如統計單詞頻率
- 關聯數組/字典:如 JavaScript 的物件、Python 的字典
總結
哈希表通過空間換時間的方式,實現了極高效率的數據存取。理解哈希表的工作原理和實現細節,對於開發高性能應用至關重要。在選擇數據結構時,如果需要快速查找、插入、刪除操作,且數據具有鍵值對特性,哈希表通常是最佳選擇。