Search / Sort

This commit is contained in:
2026-08-31 11:50:04 +08:00
parent 0cb98557da
commit 109e8af3d5
10 changed files with 818 additions and 219 deletions
+230
View File
@@ -0,0 +1,230 @@
#include <c_LinearProbingHashST.h>
/**
* @brief 内部多项式滚动去偏差哈希映射机
*/
C_STATIC_FORCE_INLINE
c_size_t c_LP_HashEngine(const void* key, c_size_t key_size) {
const unsigned char* bytes = (const unsigned char*)key;
c_size_t hash = 0;
for (c_size_t i = 0; i < key_size; i++) {
hash = 31 * hash + bytes[i];
}
return hash;
}
/**
* @brief 就地初始化线性探测哈希表(开辟密集扁平存储空间)
*/
c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* self, c_size_t initial_capacity, c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator) {
if (!self || initial_capacity == 0 || key_size == 0 || val_size == 0 || !key_cmp) {
return C_ERR_PARAM;
}
if (allocator) {
self->allocator = *allocator;
} else {
self->allocator = c_DefaultAllocator;
}
c_size_t cap = initial_capacity;
// 前置无符号整数乘法算术溢出防御审计
if (((c_size_t)-1) / key_size < cap) return C_ERR_NOMEM;
if (((c_size_t)-1) / val_size < cap) return C_ERR_NOMEM;
if (((c_size_t)-1) / sizeof(bool) < cap) return C_ERR_NOMEM;
self->keys = (char*)c_Allocator_Alloc(&self->allocator, cap * key_size);
self->vals = (char*)c_Allocator_Alloc(&self->allocator, cap * val_size);
self->occupied = (bool*)c_Allocator_Alloc(&self->allocator, cap * sizeof(bool));
if (!self->keys || !self->vals || !self->occupied) {
if (self->keys) c_Allocator_Free(&self->allocator, self->keys);
if (self->vals) c_Allocator_Free(&self->allocator, self->vals);
if (self->occupied) c_Allocator_Free(&self->allocator, self->occupied);
return C_ERR_NOMEM;
}
memset(self->occupied, 0, cap * sizeof(bool)); // 清空标志位
self->m_capacity = cap;
self->size = 0;
self->key_size = key_size;
self->val_size = val_size;
self->key_cmp = key_cmp;
self->args = args;
return C_ERR_OK;
}
/**
* @brief 内部自适应动态扩容变轨接口(开放寻址法要求 N/M <= 0.5 时查找常数常数最低)
*/
c_err_t c_LinearProbingHashST_Resize(c_LinearProbingHashST_t* self, c_size_t new_capacity) {
c_LinearProbingHashST_t temp_st;
c_err_t err = c_LinearProbingHashST_Init(&temp_st, new_capacity, self->key_size, self->val_size, self->key_cmp, self->args, &self->allocator);
if (err != C_ERR_OK) return err;
// 🌟 核心:将老哈希表内所有已占用的插槽元素物理搬运重散列到新表
for (c_size_t i = 0; i < self->m_capacity; i++) {
if (self->occupied[i]) {
void* k_src = self->keys + (i * self->key_size);
void* v_src = self->vals + (i * self->val_size);
// 外部前置接口复用
c_LinearProbingHashST_Put(&temp_st, k_src, v_src);
}
}
// 释放老表的内部存储载体
c_Allocator_Free(&self->allocator, self->keys);
c_Allocator_Free(&self->allocator, self->vals);
c_Allocator_Free(&self->allocator, self->occupied);
// 将重组平衡后的 temp_st 的物理外壳平移接管给 self
self->keys = temp_st.keys;
self->vals = temp_st.vals;
self->occupied = temp_st.occupied;
self->m_capacity = temp_st.m_capacity;
return C_ERR_OK;
}
/**
* @brief 存入键值对(开放寻址常数项均摊 O(1) 吞吐效率)
*/
c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* self, const void* key, const void* val) {
if (!self || !key || !val) return C_ERR_PARAM;
// 1. 如果当前的负载因子过高(存储量已经达到总插槽容量的一半 N >= M/2),启动两倍弹性大扩容
if (self->size >= (self->m_capacity >> 1)) {
if (c_LinearProbingHashST_Resize(self, self->m_capacity << 1) != C_ERR_OK) {
return C_ERR_NOMEM;
}
}
c_size_t i;
c_size_t M = self->m_capacity;
c_size_t ks = self->key_size;
// 2. 核心线性探测滑窗:从哈希初始基准插槽位置开始,顺序向后查找
for (i = c_LP_HashEngine(key, ks) % M; self->occupied[i]; i = (i + 1) % M) {
if (self->key_cmp(key, self->keys + (i * ks), self->args) == 0) {
// Key 已存在:触发覆写(Overwrite)更新 Value 语义,平滑返回
memcpy(self->vals + (i * self->val_size), val, self->val_size);
return C_ERR_OK;
}
}
// 3. 探查到首个空闲插槽(occupied[i] == false):深拷贝落脚安家
memcpy(self->keys + (i * ks), key, ks);
memcpy(self->vals + (i * self->val_size), val, self->val_size);
self->occupied[i] = true;
self->size++;
return C_ERR_OK;
}
/**
* @brief 依据指定 Key 精准存取读取 Value(均摊常数时间复杂度 O(1))
*/
c_err_t c_LinearProbingHashST_Get(const c_LinearProbingHashST_t* self, const void* key, void* out_val) {
if (!self || !key || !out_val) return C_ERR_PARAM;
if (self->size == 0) return C_ERR_EMPTY;
c_size_t M = self->m_capacity;
c_size_t ks = self->key_size;
// 探测碰撞线,遇到空插槽时强行终止探查(说明目标必定不存在)
for (c_size_t i = c_LP_HashEngine(key, ks) % M; self->occupied[i]; i = (i + 1) % M) {
if (self->key_cmp(key, self->keys + (i * ks), self->args) == 0) {
memcpy(out_val, self->vals + (i * self->val_size), self->val_size);
return C_ERR_OK;
}
}
return C_ERR_NOTFOUND;
}
/**
* @brief 检查哈希表内是否有效包含指定的 Key
*/
bool c_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* self, const void* key) {
if (!self || !key || self->size == 0) return false;
c_size_t M = self->m_capacity;
c_size_t ks = self->key_size;
for (c_size_t i = c_LP_HashEngine(key, ks) % M; self->occupied[i]; i = (i + 1) % M) {
if (self->key_cmp(key, self->keys + (i * ks), self->args) == 0) {
return true;
}
}
return false;
}
/**
* @brief 精准斩断移出键值对(商用级硬核 Cluster 级联右移重散列机制)
*/
c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* self, const void* key) {
if (!self || !key) return C_ERR_PARAM;
if (self->size == 0) return C_ERR_EMPTY;
c_size_t M = self->m_capacity;
c_size_t ks = self->key_size;
c_size_t vs = self->val_size;
// 1. 前置走查:如果在表中根本找不到这个键,直接拦截
if (!c_LinearProbingHashST_Contains(self, key)) {
return C_ERR_NOTFOUND;
}
// 2. 寻找目标键当前所在的精确物理下标插槽位置 i
c_size_t i = c_LP_HashEngine(key, ks) % M;
while (self->key_cmp(key, self->keys + (i * ks), self->args) != 0) {
i = (i + 1) % M;
}
// 3. 物理抹除该点数据
self->occupied[i] = false;
self->size--;
// 4. 🌟【级联重散列绝杀内核】:对当前被斩断点 i 后方的整个连续碰撞簇(Cluster)的所有节点执行物理挪移
i = (i + 1) % M;
while (self->occupied[i]) {
// 先把处于位置 i 的键和值独立备份转储出来
char* k_to_redo = self->keys + (i * ks);
char* v_to_redo = self->vals + (i * vs);
char k_buf[self->key_size];
char v_buf[self->val_size];
// 256字节足够吃下常规基本和结构体对象
memcpy(k_buf, k_to_redo, (ks < sizeof(k_buf)) ? ks : sizeof(k_buf));
memcpy(v_buf, v_to_redo, (vs < sizeof(v_buf)) ? vs : sizeof(v_buf));
// 斩断原位置的占用状态,递减临时计数
self->occupied[i] = false;
self->size--;
// 重新调用 Put 将由于前面断裂被波及的孤立节点重新安全的散列到正确的落脚槽中
c_LinearProbingHashST_Put(self, k_buf, v_buf);
i = (i + 1) % M; // 向右单调递增逼近取模
}
// 5. 内存缩容防御锁(若当前有效存量已经极度萎缩 N <= M/8 且容量大于最低基数时,自动释放多余空间翻倍缩容)
if (self->size > 0 && self->size <= (self->m_capacity >> 3) && self->m_capacity > 8) {
c_LinearProbingHashST_Resize(self, self->m_capacity >> 1);
}
return C_ERR_OK;
}
/**
* @brief 释放线性开放寻址哈希表资源
*/
void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* self) {
if (self) {
if (self->keys) c_Allocator_Free(&self->allocator, self->keys);
if (self->vals) c_Allocator_Free(&self->allocator, self->vals);
if (self->occupied) c_Allocator_Free(&self->allocator, self->occupied);
self->keys = NULL; self->vals = NULL; self->occupied = NULL;
self->size = 0; self->m_capacity = 0;
}
}