本文梳理哈希表核心概念,並給出基於開放地址法(線性探測)的完整 C++ 實現代碼,包含動態擴容、衝突統計、查找/插入/遍歷操作。
Hash(key) = key % TableSize)。α = N / M:當 α 超過閾值(如 0.75)觸發擴容重雜湊。#ifndef MYHASH_H
#define MYHASH_H
#include
#include
using namespace std;
typedef int KeyType;
const int NULLKEY = -1; // 標記空槽,避免 0 為合法鍵值
struct ElemType {
KeyType key;
int ord; // 附加數據(如記錄序號)
ElemType(KeyType k = NULLKEY, int o = 0) : key(k), ord(o) {}
bool isNull() const { return key == NULLKEY; }
};
class HashTable {
vector elem; // 動態數組存儲
int count; // 當前元素數
int sizeIdx; // hashsize 索引
static const int hashsize[]; // 容量遞增表
int HashLength() const { return hashsize[sizeIdx]; }
int Hash(KeyType key) const { return key % HashLength(); }
void Rehash(); // 擴容重雜湊
public:
HashTable() : count(0), sizeIdx(0) { elem.assign(HashLength(), ElemType()); }
bool Insert(const ElemType& e); // 插入,返回是否成功
bool Search(KeyType key, ElemType& out) const; // 查找
void Traverse() const; // 遍歷輸出
int CollisionCount() const; // 統計衝突次數
};
#endif
#include "myhash.h"
const int HashTable::hashsize[] = { 11, 19, 29, 37, 47, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869 };
void HashTable::Rehash() {
vector old = elem;
sizeIdx++;
elem.assign(HashLength(), ElemType());
count = 0;
for (const auto& e : old) if (!e.isNull()) Insert(e);
}
bool HashTable::Insert(const ElemType& e) {
if (count * 4 >= HashLength() * 3) Rehash(); // α > 0.75 擴容
int addr = Hash(e.key);
int start = addr;
do {
if (elem[addr].isNull()) {
elem[addr] = e;
++count;
return true;
}
if (elem[addr].key == e.key) return false; // 鍵值重複
addr = (addr + 1) % HashLength(); // 線性探測
} while (addr != start);
return false; // 理論上不會滿(已擴容)
}
bool HashTable::Search(KeyType key, ElemType& out) const {
int addr = Hash(key);
int start = addr;
do {
if (elem[addr].isNull()) return false;
if (elem[addr].key == key) { out = elem[addr]; return true; }
addr = (addr + 1) % HashLength();
} while (addr != start);
return false;
}
void HashTable::Traverse() const {
cout << "Index Key Ord\n";
for (int i = 0; i < HashLength(); ++i)
if (!elem[i].isNull()) cout << i << " " << elem[i].key << " " << elem[i].ord << "\n";
}
int HashTable::CollisionCount() const {
// 簡易統計:非空槽中 key%len != 索引 的數量
int c = 0;
for (int i = 0; i < HashLength(); ++i)
if (!elem[i].isNull() && Hash(elem[i].key) != i) ++c;
return c;
}
#include "myhash.h"
int main() {
HashTable H;
ElemType data[] = { {17,1}, {60,2}, {29,3}, {38,4}, {1,5}, {2,6}, {3,7}, {4,8}, {5,9}, {6,10}, {7,11}, {8,12} };
for (auto& e : data) {
if (!H.Insert(e)) cout << "Duplicate key: " << e.key << "\n";
}
H.Traverse();
cout << "Collisions: " << H.CollisionCount() << "\n";
ElemType found;
if (H.Search(29, found)) cout << "Found: " << found.key << " ord=" << found.ord << "\n";
return 0;
}
Index Key Ord 0 1 5 1 60 2 2 38 4 3 17 1 4 4 8 5 5 9 6 6 10 7 7 11 8 8 12 9 2 6 10 3 7 Collisions: 5 Found: 29 ord=3
KeyType 模板化,支援字符串、自定義類型(需提供 hash 特化)。Erase(key):開放地址法刪除需標記 DELETED 狀態,避免斷開探測鏈。vector>
),觀察不同負載因子下的性能差異。std::unordered_map 作為生產級替代參考。