Search / Sort
This commit is contained in:
+215
@@ -0,0 +1,215 @@
|
||||
#include <c_BST.h>
|
||||
|
||||
c_err_t c_BST_Init(c_BST_t* self, c_size_t key_size, c_size_t val_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator) {
|
||||
if (!self || key_size == 0 || val_size == 0 || !cmp) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
// 自适应分配器降级缺省安全播种
|
||||
if (allocator) {
|
||||
self->allocator = *allocator;
|
||||
} else {
|
||||
self->allocator = c_DefaultAllocator;
|
||||
}
|
||||
|
||||
self->root = NULL;
|
||||
self->size = 0;
|
||||
self->key_size = key_size;
|
||||
self->val_size = val_size;
|
||||
self->cmp = cmp;
|
||||
self->args = args;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据指定的 Key 检索关联的 Value 值(平均时间复杂度 O(log N))
|
||||
*/
|
||||
c_err_t c_BST_Get(const c_BST_t* self, const void* key, void* out_val) {
|
||||
if (!self || !key || !out_val) return C_ERR_PARAM;
|
||||
|
||||
c_BSTNode* curr = self->root;
|
||||
while (curr != NULL) {
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
if (cmp_res < 0) {
|
||||
curr = curr->left; // 目标比当前小,滑向左子树
|
||||
} else if (cmp_res > 0) {
|
||||
curr = curr->right; // 目标比当前大,滑向右子树
|
||||
} else {
|
||||
// 精确命中,将内部深度存储的数据镜像复制给外部
|
||||
memcpy(out_val, curr->val, self->val_size);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return C_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查树中是否包含指定的 Key 键值
|
||||
*/
|
||||
bool c_BST_Contains(const c_BST_t* self, const void* key) {
|
||||
if (!self || !key) return false;
|
||||
c_BSTNode* curr = self->root;
|
||||
while (curr != NULL) {
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
if (cmp_res < 0) curr = curr->left;
|
||||
else if (cmp_res > 0) curr = curr->right;
|
||||
else return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部递归插入/覆写辅助(基于二级指针代理,完美消除悬空控制流)
|
||||
*/
|
||||
static c_err_t c_BST_InternalPut(c_BST_t* self, c_BSTNode** node_ptr, const void* key, const void* val, bool* is_new_inserted) {
|
||||
c_BSTNode* curr = *node_ptr;
|
||||
|
||||
// 递归基:如果当前插槽为空,在此物理位置开辟并挂载新节点
|
||||
if (curr == NULL) {
|
||||
c_BSTNode* new_node = (c_BSTNode*)c_Allocator_Alloc(&self->allocator, sizeof(c_BSTNode));
|
||||
void* new_key = c_Allocator_Alloc(&self->allocator, self->key_size);
|
||||
void* new_val = c_Allocator_Alloc(&self->allocator, self->val_size);
|
||||
|
||||
if (!new_node || !new_key || !new_val) {
|
||||
if (new_node) c_Allocator_Free(&self->allocator, new_node);
|
||||
if (new_key) c_Allocator_Free(&self->allocator, new_key);
|
||||
if (new_val) c_Allocator_Free(&self->allocator, new_val);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
memcpy(new_key, key, self->key_size);
|
||||
memcpy(new_val, val, self->val_size);
|
||||
new_node->key = new_key;
|
||||
new_node->val = new_val;
|
||||
new_node->left = NULL;
|
||||
new_node->right = NULL;
|
||||
|
||||
*node_ptr = new_node; // 代理写入,父节点指针自动对齐
|
||||
*is_new_inserted = true;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
if (cmp_res < 0) {
|
||||
return c_BST_InternalPut(self, &(curr->left), key, val, is_new_inserted);
|
||||
} else if (cmp_res > 0) {
|
||||
return c_BST_InternalPut(self, &(curr->right), key, val, is_new_inserted);
|
||||
} else {
|
||||
// 键已存在,执行覆写(Overwrite)语义
|
||||
memcpy(curr->val, val, self->val_size);
|
||||
*is_new_inserted = false;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 存入键值对。若 Key 已存在则覆写更新;若不存在则开辟新节点维护树拓扑
|
||||
*/
|
||||
c_err_t c_BST_Put(c_BST_t* self, const void* key, const void* val) {
|
||||
if (!self || !key || !val) return C_ERR_PARAM;
|
||||
|
||||
bool is_new = false;
|
||||
c_err_t err = c_BST_InternalPut(self, &(self->root), key, val, &is_new);
|
||||
if (err == C_ERR_OK && is_new) {
|
||||
self->size++;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部辅助:寻找并剥离指定子树的绝对最小值节点(用于 Hibbard 删除替换)
|
||||
*/
|
||||
static c_BSTNode* c_BST_DeleteMin(c_BST_t* self, c_BSTNode** node_ptr) {
|
||||
c_BSTNode* curr = *node_ptr;
|
||||
if (curr->left == NULL) {
|
||||
// 找到最小值,将右子树代理向上对接,将当前节点断开剥离并返回
|
||||
*node_ptr = curr->right;
|
||||
return curr;
|
||||
}
|
||||
return c_BST_DeleteMin(self, &(curr->left));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部递归删除控制流(基于二级指针代理的 Hibbard 经典删除算法)
|
||||
*/
|
||||
static c_err_t c_BST_InternalDelete(c_BST_t* self, c_BSTNode** node_ptr, const void* key) {
|
||||
c_BSTNode* curr = *node_ptr;
|
||||
if (curr == NULL) {
|
||||
return C_ERR_NOTFOUND; // 节点不存在
|
||||
}
|
||||
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
if (cmp_res < 0) {
|
||||
return c_BST_InternalDelete(self, &(curr->left), key);
|
||||
} else if (cmp_res > 0) {
|
||||
return c_BST_InternalDelete(self, &(curr->right), key);
|
||||
} else {
|
||||
// 精确命中当前要删除的节点 curr,开启 Hibbard 拆解合并
|
||||
c_BSTNode* old_node = curr;
|
||||
|
||||
if (curr->right == NULL) {
|
||||
// 情况 1:无右子树,直接将左子树整体顶替上来
|
||||
*node_ptr = curr->left;
|
||||
} else if (curr->left == NULL) {
|
||||
// 情况 2:无左子树,直接将右子树整体顶替上来
|
||||
*node_ptr = curr->right;
|
||||
} else {
|
||||
// 情况 3:左右子树均完好。寻找右子树的绝对最小值充当继承后继者 (Successor)
|
||||
c_BSTNode* successor = c_BST_DeleteMin(self, &(curr->right));
|
||||
|
||||
// 后继者完美接管原节点的双向拓扑路由
|
||||
successor->left = old_node->left;
|
||||
successor->right = *node_ptr; // 此时 *node_ptr 已经是处理过 deleteMin 后的右子树根
|
||||
|
||||
*node_ptr = successor; // 代理顶替
|
||||
}
|
||||
|
||||
// 释放被移出树的旧节点物理内存
|
||||
c_Allocator_Free(&self->allocator, old_node->key);
|
||||
c_Allocator_Free(&self->allocator, old_node->val);
|
||||
c_Allocator_Free(&self->allocator, old_node);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据指定 Key 彻底从树中移出其关联的键值对节点
|
||||
*/
|
||||
c_err_t c_BST_Delete(c_BST_t* self, const void* key) {
|
||||
if (!self || !key) return C_ERR_PARAM;
|
||||
if (self->size == 0) return C_ERR_EMPTY;
|
||||
|
||||
c_err_t err = c_BST_InternalDelete(self, &(self->root), key);
|
||||
if (err == C_ERR_OK) {
|
||||
self->size--;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部递归反初始化解构辅助
|
||||
*/
|
||||
static void c_BST_InternalDeinit(c_Allocator_t* alloc, c_BSTNode* node) {
|
||||
if (node == NULL) return;
|
||||
|
||||
// 递归后序遍历:先解构左右子树,再回收当前节点
|
||||
c_BST_InternalDeinit(alloc, node->left);
|
||||
c_BST_InternalDeinit(alloc, node->right);
|
||||
|
||||
c_Allocator_Free(alloc, node->key);
|
||||
c_Allocator_Free(alloc, node->val);
|
||||
c_Allocator_Free(alloc, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 二叉搜索树反初始化彻底释放
|
||||
*/
|
||||
void c_BST_Destroy(c_BST_t* self) {
|
||||
if (self && self->root) {
|
||||
c_BST_InternalDeinit(&self->allocator, self->root);
|
||||
self->root = NULL;
|
||||
self->size = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef INCLUDED_C_BST_H
|
||||
#define INCLUDED_C_BST_H
|
||||
|
||||
#ifndef INCLUDED_C_SORTCOMPARE_H
|
||||
#include <c_SortCompare.h>
|
||||
#endif /*INCLUDED_C_SORTCOMPARE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct c_BSTNode {
|
||||
void* key; // 独立分配存储的 Key 物理地址
|
||||
void* val; // 独立分配存储的 Value 物理地址
|
||||
struct c_BSTNode* left; // 左子节点指针 (指向更小键的子树)
|
||||
struct c_BSTNode* right; // 右子节点指针 (指向更大键的子树)
|
||||
} c_BSTNode;
|
||||
|
||||
/**
|
||||
* @brief 工业级泛型二叉搜索树结构体(分配器内联组合版)
|
||||
*/
|
||||
typedef struct {
|
||||
c_BSTNode* root; // 树的根节点指针
|
||||
c_size_t size; // 当前树内有效驻留的键值对总个数
|
||||
c_size_t key_size; // 键对象占用的物理字节大小 (sizeof)
|
||||
c_size_t val_size; // 值对象占用的物理字节大小 (sizeof)
|
||||
c_SortCompare_t cmp; // 键对象专用的动态回调比对器
|
||||
void* args; // 自定义上下文参数指针
|
||||
c_Allocator_t allocator; // 内联组合分配器实例与默认 Fallback 缺省机制
|
||||
} c_BST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_BST_Init(c_BST_t* self, c_size_t key_size, c_size_t val_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator);
|
||||
|
||||
c_err_t c_BST_Get(const c_BST_t* self, const void* key, void* out_val);
|
||||
|
||||
bool c_BST_Contains(const c_BST_t* self, const void* key);
|
||||
|
||||
c_err_t c_BST_Put(c_BST_t* self, const void* key, const void* val) ;
|
||||
|
||||
c_err_t c_BST_Delete(c_BST_t* self, const void* key);
|
||||
|
||||
void c_BST_Destroy(c_BST_t* self);
|
||||
|
||||
#endif /*INCLUDED_C_BST_H*/
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "c_BST.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int bst_compare_chars(const void* a, const void* b, void* args) {
|
||||
(void)args;
|
||||
char char1 = *(const char*)a;
|
||||
char char2 = *(const char*)b;
|
||||
return char1 - char2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE(test_c_BST_Dynamic_CRUD) {
|
||||
c_BST_t tree;
|
||||
// 就地初始化:将 char 作为 Key,int 作为 Value
|
||||
c_err_t err = c_BST_Init(&tree, sizeof(char), sizeof(int), bst_compare_chars, NULL, &c_DefaultAllocator);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
// 故意以颠簸顺序插入,构造一个经典的非退化二叉平衡树拓扑
|
||||
char key_M = 'M'; int val_M = 40;
|
||||
char key_E = 'E'; int val_E = 20;
|
||||
char key_S = 'S'; int val_S = 50;
|
||||
char key_A = 'A'; int val_A = 10;
|
||||
char key_R = 'R'; int val_R = 45;
|
||||
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Put(&tree, &key_M, &val_M));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Put(&tree, &key_E, &val_E));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Put(&tree, &key_S, &val_S));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Put(&tree, &key_A, &val_A));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Put(&tree, &key_R, &val_R));
|
||||
ASSERT_INT_EQ(5, (int)tree.size);
|
||||
|
||||
// 1. Get 状态命中读取验证
|
||||
int get_val = 0;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Get(&tree, &key_S, &get_val));
|
||||
ASSERT_INT_EQ(50, get_val);
|
||||
|
||||
ASSERT_TRUE(c_BST_Contains(&tree, &key_R));
|
||||
char key_X = 'X';
|
||||
ASSERT_TRUE(!c_BST_Contains(&tree, &key_X));
|
||||
|
||||
// 2. Overwrite 相同键覆写更新审计
|
||||
int update_val_M = 999;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Put(&tree, &key_M, &update_val_M));
|
||||
ASSERT_INT_EQ(5, (int)tree.size); // 大小锁死不能变
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Get(&tree, &key_M, &get_val));
|
||||
ASSERT_INT_EQ(999, get_val);
|
||||
|
||||
// 3. Delete 深度极限压测:删除拥有双向子树的复杂中段根节点 'S'
|
||||
// 修复后的 Hibbard 机制应当自动将 'R' 节点提升顶替上来,并原路释放 S 的内存
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Delete(&tree, &key_S));
|
||||
ASSERT_INT_EQ(4, (int)tree.size);
|
||||
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_BST_Get(&tree, &key_S, &get_val));
|
||||
|
||||
// 确保与 S 共同历经剧变的邻居节点 R 依然在新树中完好驻留
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BST_Get(&tree, &key_R, &get_val));
|
||||
ASSERT_INT_EQ(45, get_val);
|
||||
|
||||
// 深度级联注销反初始化
|
||||
c_BST_Destroy(&tree);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_BST_ParamConstraints) {
|
||||
c_BST_t local_tree;
|
||||
c_BST_Init(&local_tree, sizeof(char), sizeof(int), bst_compare_chars, NULL, NULL); // 降格 Fallback
|
||||
|
||||
char k = 'Q';
|
||||
int v = 77;
|
||||
// 验证各类非法调用的拦截返回
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_BST_Init(NULL, sizeof(char), sizeof(int), bst_compare_chars, NULL, NULL));
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_BST_Put(NULL, &k, &v));
|
||||
ASSERT_INT_EQ(C_ERR_EMPTY, c_BST_Delete(&local_tree, &k)); // 空树删除直接拦截抛出 C_ERR_EMPTY
|
||||
|
||||
c_BST_Destroy(&local_tree);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 5. 主集成入口
|
||||
// ==========================================
|
||||
int main(void) {
|
||||
TEST_START(C_BinarySearchTreeST_Isolated_TestSuite);
|
||||
RUN_TEST(test_c_BST_Dynamic_CRUD);
|
||||
RUN_TEST(test_c_BST_ParamConstraints);
|
||||
TEST_REPORT();
|
||||
return (g_test_registry.failed_count > 0 ? 1 : 0);
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
#include <c_BinarySearchST.h>
|
||||
|
||||
|
||||
/**
|
||||
* @brief 核心内部操作:基于无符号安全的左闭右开二分 Rank 检索
|
||||
*
|
||||
* @return c_size_t 返回小于指定 key 的键的总个数。该位置就是精确命中点或完美插入点
|
||||
*/
|
||||
static c_size_t c_BinarySearchST_Rank(const c_BinarySearchST_t* self, const void* key) {
|
||||
c_size_t low = 0;
|
||||
c_size_t high = self->size; // 右边界设为 size(开区间),这样 high 永远不会因 mid-1 下溢
|
||||
|
||||
while (low < high) { // 🌟 开区间控制条件为 low < high,彻底杜绝死循环
|
||||
c_size_t mid = low + ((high - low) >> 1);
|
||||
char* mid_key = self->keys + (mid * self->key_size);
|
||||
|
||||
int cmp_res = self->cmp(key, mid_key, self->args);
|
||||
if (cmp_res < 0) {
|
||||
high = mid; // 🌟 目标比 mid 小,安全收缩右开边界,完全斩断减法下溢
|
||||
} else if (cmp_res > 0) {
|
||||
low = mid + 1; // 目标比 mid 大,安全收缩左闭边界
|
||||
} else {
|
||||
return mid; // 精确命中,返回对应的物理下标位置
|
||||
}
|
||||
}
|
||||
return low; // 未命中,返回当前最精准的插入点插槽索引
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* self, c_size_t initial_capacity, c_size_t key_size, c_size_t val_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator) {
|
||||
if (!self || key_size == 0 || val_size == 0 || !cmp) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
// 自适应分配器降级缺省安全播种
|
||||
if (allocator) {
|
||||
self->allocator = *allocator;
|
||||
} else {
|
||||
self->allocator = c_DefaultAllocator;
|
||||
}
|
||||
|
||||
c_size_t cap = (initial_capacity > 0) ? initial_capacity : 4;
|
||||
|
||||
// 前置逆向除法溢出安全审计
|
||||
if (((c_size_t)-1) / key_size < cap) return C_ERR_NOMEM;
|
||||
if (((c_size_t)-1) / val_size < 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);
|
||||
|
||||
if (!self->keys || !self->vals) {
|
||||
if (self->keys) c_Allocator_Free(&self->allocator, self->keys);
|
||||
if (self->vals) c_Allocator_Free(&self->allocator, self->vals);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
self->capacity = cap;
|
||||
self->size = 0;
|
||||
self->key_size = key_size;
|
||||
self->val_size = val_size;
|
||||
self->cmp = cmp;
|
||||
self->args = args;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 根据指定的 Key 检索关联的 Value 值(时间复杂度 O(log N))
|
||||
*/
|
||||
c_err_t c_BinarySearchST_Get(const c_BinarySearchST_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 i = c_BinarySearchST_Rank(self, key);
|
||||
|
||||
// 如果检索出的位置合法,且对应的键与其等价,说明精确命中
|
||||
if (i < self->size && self->cmp(key, self->keys + (i * self->key_size), 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_BinarySearchST_Contains(const c_BinarySearchST_t* self, const void* key) {
|
||||
if (!self || !key || self->size == 0) return false;
|
||||
c_size_t i = c_BinarySearchST_Rank(self, key);
|
||||
return (i < self->size && self->cmp(key, self->keys + (i * self->key_size), self->args) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 存入键值对。若已存在则覆写更新;若不存在则对后方数据滑窗后移,插入并维持有序性
|
||||
*/
|
||||
c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* self, const void* key, const void* val) {
|
||||
if (!self || !key || !val) return C_ERR_PARAM;
|
||||
|
||||
c_size_t i = c_BinarySearchST_Rank(self, key);
|
||||
|
||||
// 1. 如果键值已经存在,直接更新它的值(覆写语义)
|
||||
if (i < self->size && self->cmp(key, self->keys + (i * self->key_size), self->args) == 0) {
|
||||
memcpy(self->vals + (i * self->val_size), val, self->val_size);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
// 2. 键值不存在,准备执行新元素插入。前置判定自适应弹性动态扩容
|
||||
if (self->size >= self->capacity) {
|
||||
c_size_t old_cap = self->capacity;
|
||||
|
||||
// 算术乘法整数溢出除法拦截防护
|
||||
if (((c_size_t)-1) / self->key_size < (old_cap << 1)) return C_ERR_NOMEM;
|
||||
if (((c_size_t)-1) / self->val_size < (old_cap << 1)) return C_ERR_NOMEM;
|
||||
|
||||
c_size_t new_cap = old_cap << 1;
|
||||
c_size_t old_key_bytes = old_cap * self->key_size;
|
||||
c_size_t new_key_bytes = new_cap * self->key_size;
|
||||
c_size_t old_val_bytes = old_cap * self->val_size;
|
||||
c_size_t new_val_bytes = new_cap * self->val_size;
|
||||
|
||||
char* new_keys = (char*)c_Allocator_Realloc(&self->allocator, self->keys, old_key_bytes, new_key_bytes);
|
||||
char* new_vals = (char*)c_Allocator_Realloc(&self->allocator, self->vals, old_val_bytes, new_val_bytes);
|
||||
if (!new_keys || !new_vals) {
|
||||
// 部分成功安全回退
|
||||
if (new_keys) self->keys = new_keys;
|
||||
if (new_vals) self->vals = new_vals;
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
self->keys = new_keys;
|
||||
self->vals = new_vals;
|
||||
self->capacity = new_cap;
|
||||
}
|
||||
|
||||
// 3. 核心数据平移:将区间 [i, size-1] 内的所有键值对数据,单向全部后移一位给新插入留出位置
|
||||
// 控制条件为 j > i。即便 i 为最左侧的 0,j 最小减到 1 就会强制退出,彻底封死无符号自减下溢
|
||||
for (c_size_t j = self->size; j > i; j--) {
|
||||
memcpy(self->keys + (j * self->key_size), self->keys + ((j - 1) * self->key_size), self->key_size);
|
||||
memcpy(self->vals + (j * self->val_size), self->vals + ((j - 1) * self->val_size), self->val_size);
|
||||
}
|
||||
|
||||
// 4. 将新元素深拷贝写入空出来的有序安全插槽中
|
||||
memcpy(self->keys + (i * self->key_size), key, self->key_size);
|
||||
memcpy(self->vals + (i * self->val_size), val, self->val_size);
|
||||
self->size++;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据指定 Key 彻底从有序表中斩断移出指定对,并对其后方数据滑窗前移覆盖
|
||||
*/
|
||||
c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* self, const void* key) {
|
||||
if (!self || !key) return C_ERR_PARAM;
|
||||
if (self->size == 0) return C_ERR_EMPTY;
|
||||
|
||||
c_size_t i = c_BinarySearchST_Rank(self, key);
|
||||
|
||||
// 键值未找到拦截
|
||||
if (i >= self->size || self->cmp(key, self->keys + (i * self->key_size), self->args) != 0) {
|
||||
return C_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
// 核心数据平移:将区间 [i+1, size-1] 内的元素统一向前推进覆盖一位
|
||||
// 纯单调递增控制,num 边界有效拦截
|
||||
c_size_t limit = self->size - 1;
|
||||
for (c_size_t j = i; j < limit; j++) {
|
||||
memcpy(self->keys + (j * self->key_size), self->keys + ((j + 1) * self->key_size), self->key_size);
|
||||
memcpy(self->vals + (j * self->val_size), self->vals + ((j + 1) * self->val_size), self->val_size);
|
||||
}
|
||||
|
||||
self->size--;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 有序表反初始化数据销毁
|
||||
*/
|
||||
void c_BinarySearchST_Destroy(c_BinarySearchST_t* self) {
|
||||
if (self) {
|
||||
if (self->keys) c_Allocator_Free(&self->allocator, self->keys);
|
||||
if (self->vals) c_Allocator_Free(&self->allocator, self->vals);
|
||||
self->keys = NULL;
|
||||
self->vals = NULL;
|
||||
self->size = 0;
|
||||
self->capacity = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef INCLUDED_C_BINARYSEARCHST_H
|
||||
#define INCLUDED_C_BINARYSEARCHST_H
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_SORTCOMPARE_H
|
||||
#include <c_SortCompare.h>
|
||||
#endif /*INCLUDED_C_SORTCOMPARE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
char* keys; // 密集排布的有序键数组载体 (保持严格升序排列)
|
||||
char* vals; // 与键数组物理下标一一对应的值数组载体
|
||||
c_size_t capacity; // 当前容器的最大可容纳插槽数
|
||||
c_size_t size; // 当前已存储的有效键值对总个数
|
||||
c_size_t key_size; // 单个键对象占用的物理字节大小 (sizeof)
|
||||
c_size_t val_size; // 单个值对象占用的物理字节大小 (sizeof)
|
||||
c_SortCompare_t cmp; // 键对象专用的动态回调比对器
|
||||
void* args; // 自定义上下文参数指针
|
||||
c_Allocator_t allocator;// 内联组合分配器实例与默认 Fallback 缺省机制
|
||||
} c_BinarySearchST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* self, c_size_t initial_capacity, c_size_t key_size, c_size_t val_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator);
|
||||
|
||||
c_err_t c_BinarySearchST_Get(const c_BinarySearchST_t* self, const void* key, void* out_val);
|
||||
|
||||
bool c_BinarySearchST_Contains(const c_BinarySearchST_t* self, const void* key);
|
||||
|
||||
c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* self, const void* key, const void* val);
|
||||
|
||||
c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* self, const void* key);
|
||||
|
||||
void c_BinarySearchST_Destroy(c_BinarySearchST_t* self);
|
||||
|
||||
#endif /*INCLUDED_C_BINARYSEARCHST_H*/
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "c_BinarySearchST.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int bst_compare_chars(const void* a, const void* b, void* args) {
|
||||
(void)args;
|
||||
char char1 = *(const char*)a;
|
||||
char char2 = *(const char*)b;
|
||||
return char1 - char2;
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_BinarySearchST_Sorted_CRUD) {
|
||||
c_BinarySearchST_t st;
|
||||
// 初始化一个仅有 2 容量的有序列,强制触发自增扩容
|
||||
c_err_t err = c_BinarySearchST_Init(&st, 2, sizeof(char), sizeof(int), bst_compare_chars, NULL, &c_DefaultAllocator);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
char key_M = 'M'; int val_M = 400;
|
||||
char key_B = 'B'; int val_B = 100;
|
||||
char key_R = 'R'; int val_R = 500;
|
||||
char key_G = 'G'; int val_G = 200;
|
||||
|
||||
// 1. 乱序 Put 入队,内部数据平移必须能够将其安全纠正排序为严格的: B -> G -> M -> R 升序
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BinarySearchST_Put(&st, &key_M, &val_M));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BinarySearchST_Put(&st, &key_B, &val_B));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BinarySearchST_Put(&st, &key_R, &val_R));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BinarySearchST_Put(&st, &key_G, &val_G));
|
||||
ASSERT_INT_EQ(4, (int)st.size);
|
||||
|
||||
// 2. 验证底层有序性排列的插槽指向物理存储
|
||||
ASSERT_INT_EQ('B', st.keys[0]);
|
||||
ASSERT_INT_EQ('G', st.keys[1]);
|
||||
ASSERT_INT_EQ('M', st.keys[2]);
|
||||
ASSERT_INT_EQ('R', st.keys[3]);
|
||||
|
||||
// 3. Get 二分高速检索断言
|
||||
int get_result = 0;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BinarySearchST_Get(&st, &key_M, &get_result));
|
||||
ASSERT_INT_EQ(400, get_result);
|
||||
|
||||
// 4. Overwrite 覆写测试
|
||||
int update_val_M = 8888;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BinarySearchST_Put(&st, &key_M, &update_val_M));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BinarySearchST_Get(&st, &key_M, &get_result));
|
||||
ASSERT_INT_EQ(8888, get_result);
|
||||
|
||||
// 5. 极低值溢出核验:检索一个比全队首项还小的 Key ('A' < 'B')
|
||||
// 修复后的 Rank 在无符号数下应当将 high 收缩到 0 号位置而不是下溢到最大值爆发死循环
|
||||
char toxic_key = 'A';
|
||||
ASSERT_TRUE(!c_BinarySearchST_Contains(&st, &toxic_key));
|
||||
|
||||
// 6. Delete 滑窗前移覆盖移出测试
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_BinarySearchST_Delete(&st, &key_G));
|
||||
ASSERT_INT_EQ(3, (int)st.size);
|
||||
// 移出后原属于 G 的 下标 1 应该被后面的 M(keys='M') 顺理成章顶替
|
||||
ASSERT_INT_EQ('M', st.keys[1]);
|
||||
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_BinarySearchST_Get(&st, &key_G, &get_result));
|
||||
|
||||
c_BinarySearchST_Destroy(&st);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_BinarySearchST_EmptyAndToxicity) {
|
||||
c_BinarySearchST_t local_st;
|
||||
c_BinarySearchST_Init(&local_st, 4, sizeof(char), sizeof(int), bst_compare_chars, NULL, NULL);
|
||||
|
||||
char k = 'X';
|
||||
int v = 99;
|
||||
// 验证拦截防御
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_BinarySearchST_Init(NULL, 4, sizeof(char), sizeof(int), bst_compare_chars, NULL, NULL));
|
||||
ASSERT_INT_EQ(C_ERR_EMPTY, c_BinarySearchST_Delete(&local_st, &k)); // 空仓删除安全抛出 C_ERR_EMPTY
|
||||
ASSERT_INT_EQ(C_ERR_EMPTY, c_BinarySearchST_Get(&local_st, &k, &v));
|
||||
|
||||
c_BinarySearchST_Destroy(&local_st);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 5. 主集成入口
|
||||
// ==========================================
|
||||
int main(void) {
|
||||
TEST_START(C_BinarySearchST_UnsignedSafe_TestSuite);
|
||||
RUN_TEST(test_c_BinarySearchST_Sorted_CRUD);
|
||||
RUN_TEST(test_c_BinarySearchST_EmptyAndToxicity);
|
||||
TEST_REPORT();
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#include <c_HashMap.h>
|
||||
|
||||
/**
|
||||
* @brief 工业级无符号泛型去偏差多项式哈希映射机
|
||||
*
|
||||
* 采用经典常数乘子 31 逐字节滚动翻滚,完美离散任何变长扁平结构体或内置基础类型
|
||||
*/
|
||||
static c_size_t c_HashMap_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_STATIC_FORCE_INLINE
|
||||
c_size_t c_HashMap_GetBucketSlot(const c_HashMap_t* self, const void* key) {
|
||||
c_size_t code = c_HashMap_HashEngine(key, self->key_size);
|
||||
// 纯无符号按位安全取模,物理屏蔽符号位回绕风险
|
||||
return code % self->m_buckets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 就地初始化哈希映射表
|
||||
*
|
||||
* @param initial_buckets 哈希桶(拉链容量)的总基数 M。建议传入质数(如 97, 997, 8191)以获得最佳离散度
|
||||
*/
|
||||
c_err_t c_HashMap_Init(c_HashMap_t* self, c_size_t initial_buckets, c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator) {
|
||||
if (!self || initial_buckets == 0 || key_size == 0 || val_size == 0 || !key_cmp) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
if (allocator) {
|
||||
self->allocator = *allocator;
|
||||
} else {
|
||||
self->allocator = c_DefaultAllocator;
|
||||
}
|
||||
|
||||
self->m_buckets = initial_buckets;
|
||||
self->size = 0;
|
||||
self->key_size = key_size;
|
||||
self->val_size = val_size;
|
||||
self->key_cmp = key_cmp;
|
||||
self->args = args;
|
||||
|
||||
// 前置无符号乘法整数溢出防御审计
|
||||
if (((c_size_t)-1) / sizeof(c_SeqSearchST_t) < initial_buckets) {
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// 一次性静态分配 M 个桶的符号表控制头空间
|
||||
self->buckets = (c_SeqSearchST_t*)c_Allocator_Alloc(&self->allocator, initial_buckets * sizeof(c_SeqSearchST_t));
|
||||
if (!self->buckets) {
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// 逐个串联并初始化每个桶内部的单链表控制流,强力绑定统一的组合分配器
|
||||
for (c_size_t i = 0; i < initial_buckets; i++) {
|
||||
c_SeqSearchST_Init(&(self->buckets[i]), key_size, val_size, key_cmp, args, &self->allocator);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 存入键值对(均摊常数项时间复杂度 O(1))
|
||||
*
|
||||
* 如果键已存在于特定拉链桶中则覆写更新;若不存在则头插法压入新节点并刷新全局计数
|
||||
*/
|
||||
c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* val) {
|
||||
if (!self || !key || !val) return C_ERR_PARAM;
|
||||
|
||||
// 1. 利用哈希映射机瞬间定位到目标桶
|
||||
c_size_t slot = c_HashMap_GetBucketSlot(self, key);
|
||||
c_SeqSearchST_t* bucket_st = &(self->buckets[slot]);
|
||||
|
||||
// 2. 顺序探查该拉链。前置提取原拉链大小,用来判别本次操作是“新增”还是“修改覆写”
|
||||
c_size_t old_bucket_size = bucket_st->size;
|
||||
|
||||
c_err_t err = c_SeqSearchST_Put(bucket_st, key, val);
|
||||
if (err == C_ERR_OK) {
|
||||
// 如果引发了当前单链桶节点的空间膨胀,说明是新键插入,递增全局计数
|
||||
if (bucket_st->size > old_bucket_size) {
|
||||
self->size++;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 依据指定 Key 精准存取读取关联的 Value(均摊常数时间复杂度 O(1))
|
||||
*/
|
||||
c_err_t c_HashMap_Get(const c_HashMap_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 slot = c_HashMap_GetBucketSlot(self, key);
|
||||
// 穿透调用单向顺序表的 Get 接口
|
||||
return c_SeqSearchST_Get(&(self->buckets[slot]), key, out_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查哈希表内是否有效包含指定的 Key
|
||||
*/
|
||||
bool c_HashMap_Contains(const c_HashMap_t* self, const void* key) {
|
||||
if (!self || !key || self->size == 0) return false;
|
||||
c_size_t slot = c_HashMap_GetBucketSlot(self, key);
|
||||
return c_SeqSearchST_Contains(&(self->buckets[slot]), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从指定的哈希桶拉链中斩断并彻底移出其关联的符号对
|
||||
*/
|
||||
c_err_t c_HashMap_Delete(c_HashMap_t* self, const void* key) {
|
||||
if (!self || !key) return C_ERR_PARAM;
|
||||
if (self->size == 0) return C_ERR_EMPTY;
|
||||
|
||||
c_size_t slot = c_HashMap_GetBucketSlot(self, key);
|
||||
c_SeqSearchST_t* bucket_st = &(self->buckets[slot]);
|
||||
|
||||
c_err_t err = c_SeqSearchST_Delete(bucket_st, key);
|
||||
if (err == C_ERR_OK) {
|
||||
self->size--; // 递减总容量计数
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 反初始化:级联彻底清理销毁全部哈希桶拉链,原路逆向回收堆空间
|
||||
*/
|
||||
void c_HashMap_Destroy(c_HashMap_t* self) {
|
||||
if (!self || !self->buckets) return;
|
||||
|
||||
// 1. 迫使每个哈希拉链桶先链式释放其内部的单链物理节点
|
||||
for (c_size_t i = 0; i < self->m_buckets; i++) {
|
||||
c_SeqSearchST_Destroy(&(self->buckets[i]));
|
||||
}
|
||||
|
||||
// 2. 回收哈希桶外壳控制头数组本身
|
||||
c_Allocator_Free(&self->allocator, self->buckets);
|
||||
self->buckets = NULL;
|
||||
self->size = 0;
|
||||
self->m_buckets = 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef INCLUDED_C_HASHMAP_H
|
||||
#define INCLUDED_C_HASHMAP_H
|
||||
|
||||
#ifndef INCLUDED_C_SEQSEARCHST_H
|
||||
#include <c_SeqSearchST.h>
|
||||
#endif /*INCLUDED_C_SEQSEARCHST_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_size_t m_buckets; // 哈希表内的桶(拉链数量)总基数 (M)
|
||||
c_size_t size; // 当前整个哈希映射表内有效驻留的键值对总数 (N)
|
||||
c_SeqSearchST_t* buckets; // 密集排布的拉链桶符号表动态数组:buckets[0 ... M-1]
|
||||
c_size_t key_size; // 键对象的字节大小 (sizeof)
|
||||
c_size_t val_size; // 值对象的字节大小 (sizeof)
|
||||
c_SortCompare_t key_cmp; // 键对象的全等判定器
|
||||
void* args; // 自定义上下文
|
||||
c_Allocator_t allocator; // 内联组合分配器实例与自适应 Fallback 缺省
|
||||
} c_HashMap_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_HashMap_Init(c_HashMap_t* self, c_size_t initial_buckets, c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator);
|
||||
|
||||
c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* val);
|
||||
|
||||
c_err_t c_HashMap_Get(const c_HashMap_t* self, const void* key, void* out_val);
|
||||
|
||||
bool c_HashMap_Contains(const c_HashMap_t* self, const void* key);
|
||||
|
||||
c_err_t c_HashMap_Delete(c_HashMap_t* self, const void* key);
|
||||
|
||||
void c_HashMap_Destroy(c_HashMap_t* self);
|
||||
|
||||
#endif /*INCLUDED_C_HASHMAP_H*/
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "c_HashMap.h"
|
||||
#include "c_Test.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int hash_compare_chars(const void* a, const void* b, void* args) {
|
||||
(void)args;
|
||||
char char1 = *(const char*)a;
|
||||
char char2 = *(const char*)b;
|
||||
return char1 - char2;
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_HashMap_O1_StandardFlow) {
|
||||
c_HashMap_t map;
|
||||
|
||||
// 初始化具有 5 个拉链桶的泛型哈希映射表 (M=5),测试默认分配器 Fallback 降级机制
|
||||
c_err_t err = c_HashMap_Init(&map, 5, sizeof(char), sizeof(int), hash_compare_chars, NULL, NULL);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
char key_P = 'P'; int val_P = 90;
|
||||
char key_H = 'H'; int val_H = 80;
|
||||
char key_Q = 'Q'; int val_Q = 70;
|
||||
|
||||
// 1. Put 基础常数级高速录入验证
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashMap_Put(&map, &key_P, &val_P));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashMap_Put(&map, &key_H, &val_H));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashMap_Put(&map, &key_Q, &val_Q));
|
||||
ASSERT_INT_EQ(3, (int)map.size);
|
||||
|
||||
// 状态包含判定
|
||||
ASSERT_TRUE(c_HashMap_Contains(&map, &key_H));
|
||||
char key_NotExit = 'X';
|
||||
ASSERT_TRUE(!c_HashMap_Contains(&map, &key_NotExit));
|
||||
|
||||
// 2. Get 常数级读取断言
|
||||
int get_result = 0;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashMap_Get(&map, &key_H, &get_result));
|
||||
ASSERT_INT_EQ(80, get_result); // 精确提取
|
||||
|
||||
// 3. Put 相同键覆写更新(Overwrite)特性核验
|
||||
int overwrite_val_H = 9999;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashMap_Put(&map, &key_H, &overwrite_val_H));
|
||||
ASSERT_INT_EQ(3, (int)map.size); // 覆写后,全局哈希大小必须守恒,依旧为 3
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashMap_Get(&map, &key_H, &get_result));
|
||||
ASSERT_INT_EQ(9999, get_result);
|
||||
|
||||
// 4. Delete 常数级拉链断开删除测试
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashMap_Delete(&map, &key_H));
|
||||
ASSERT_INT_EQ(2, (int)map.size); // 递减确凿
|
||||
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_HashMap_Get(&map, &key_H, &get_result));
|
||||
|
||||
// 彻底级联反初始化释放桶空间
|
||||
c_HashMap_Destroy(&map);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_HashMap_ToxicityDefenses) {
|
||||
c_HashMap_t local_map;
|
||||
c_HashMap_Init(&local_map, 4, sizeof(char), sizeof(int), hash_compare_chars, NULL, &c_DefaultAllocator);
|
||||
|
||||
char k = 'K'; int v = 11;
|
||||
// 5. 验证关键入参异常状态码强拦截
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_HashMap_Init(NULL, 5, sizeof(char), sizeof(int), hash_compare_chars, NULL, NULL));
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_HashMap_Put(NULL, &k, &v));
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_HashMap_Delete(NULL, &k));
|
||||
ASSERT_INT_EQ(C_ERR_EMPTY, c_HashMap_Delete(&local_map, &k)); // 空仓删除安全拦截抛出 C_ERR_EMPTY
|
||||
|
||||
c_HashMap_Destroy(&local_map);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 5. 主集成入口
|
||||
// ==========================================
|
||||
int main(void) {
|
||||
TEST_START(C_HashMap_Isolated_TestSuite);
|
||||
RUN_TEST(test_c_HashMap_O1_StandardFlow);
|
||||
RUN_TEST(test_c_HashMap_ToxicityDefenses);
|
||||
TEST_REPORT();
|
||||
return (g_test_registry.failed_count > 0 ? 1 : 0);
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
#include <c_HashST.h>
|
||||
|
||||
#define DEFAULT_INITIAL_CAPACITY (1024*8)
|
||||
#define GROWTH_FACTOR 1
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
static c_HashSTNode_t* GetNode(c_HashST_t* self, const void* key) {
|
||||
c_HashSTNode_t* result = NULL;
|
||||
c_HashSTNode_t* node = NULL;
|
||||
c_PtrBag_t* bucket = NULL;
|
||||
|
||||
uint32_t hash=0;
|
||||
c_size_t bucket_idx = 0;
|
||||
|
||||
hash = self->key_ops.hash(key, self->key_ops.arg);
|
||||
bucket_idx = hash % self->capacity;
|
||||
bucket = self->buckets[bucket_idx];
|
||||
|
||||
if (!bucket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (c_size_t i=0; i<bucket->size; i++) {
|
||||
node = c_PtrBag_Get(bucket, i);
|
||||
if (!node) continue;
|
||||
if (node->hash == hash) {
|
||||
if (self->key_ops.eq(node->key, key, self->key_ops.arg)) {
|
||||
result = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_size_t NumCol(c_PtrBag_t* self) {
|
||||
if (!self) return 0;
|
||||
return (self->size==0)?0:self->size-1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
c_err_t c_HashST_Init(c_HashST_t* self, c_size_t capacity, c_HashKeyOps_t key_ops, c_HashValOps_t val_ops, c_Allocator_t* allocator) {
|
||||
if (!self ) return C_ERR_PARAM;
|
||||
|
||||
self->capacity = capacity==0?DEFAULT_INITIAL_CAPACITY:capacity;
|
||||
self->key_ops = key_ops;
|
||||
self->val_ops = val_ops;
|
||||
self->allocator = allocator!=NULL?*allocator:c_DefaultAllocator;
|
||||
self->size = 0;
|
||||
|
||||
// key_ops.arg = &self->allocator;
|
||||
// val_ops.arg = &self->allocator;
|
||||
|
||||
self->buckets = c_Allocator_Alloc(&self->allocator, self->capacity * sizeof(*(self->buckets)));
|
||||
if (!self->buckets) return C_ERR_NOMEM;
|
||||
|
||||
for (c_size_t i=0; i<self->capacity; i++) {
|
||||
self->buckets[i] = NULL;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_HashST_Destroy(c_HashST_t* self) {
|
||||
if (!self ) return;
|
||||
c_PtrBag_t* bucket=0;
|
||||
c_HashSTNode_t* node=0;
|
||||
|
||||
for (c_size_t i=0; i<self->capacity; i++) {
|
||||
bucket = self->buckets[i];
|
||||
if (!bucket) continue;
|
||||
for (c_size_t j=0; j<bucket->size; j++) {
|
||||
node = c_PtrBag_Get(bucket, j);
|
||||
if (!node) continue;
|
||||
self->key_ops.free(node->key, self->key_ops.arg);
|
||||
self->val_ops.free(node->val, self->val_ops.arg);
|
||||
c_Allocator_Free(&self->allocator, node);
|
||||
}
|
||||
c_PtrBag_Destroy(bucket);
|
||||
c_Allocator_Free(&self->allocator, bucket);
|
||||
}
|
||||
|
||||
c_Allocator_Free(&self->allocator, self->buckets);
|
||||
}
|
||||
|
||||
void* c_HashST_Get(c_HashST_t* self, const void* key) {
|
||||
c_HashSTNode_t* node = GetNode(self, key);
|
||||
if (!node) return NULL;
|
||||
return node->val;
|
||||
}
|
||||
|
||||
c_err_t c_HashST_Resize(c_HashST_t* self, c_size_t new_capacity) {
|
||||
if (!self || new_capacity < self->capacity) return C_ERR_PARAM;
|
||||
if (new_capacity == self->capacity) {
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_PtrBag_t** new_buckets = c_Allocator_Alloc(&self->allocator, new_capacity * sizeof(*(self->buckets)));
|
||||
if (!new_buckets) return C_ERR_NOMEM;
|
||||
for (c_size_t i=0; i<new_capacity; i++) {
|
||||
new_buckets[i] = NULL;
|
||||
}
|
||||
|
||||
for (c_size_t i=0; i<self->capacity; i++) {
|
||||
c_PtrBag_t* bucket = self->buckets[i];
|
||||
if (!bucket) continue;
|
||||
for (c_size_t j=0; j<bucket->size; j++) {
|
||||
c_HashSTNode_t* node = c_PtrBag_Get(bucket, j);
|
||||
if (!node) continue;
|
||||
const c_size_t idx = node->hash % new_capacity;
|
||||
if (!new_buckets[idx]) {
|
||||
new_buckets[idx] = c_Allocator_Alloc(&self->allocator, sizeof(*bucket));
|
||||
if (!new_buckets[idx]) return C_ERR_NOMEM;
|
||||
c_PtrBag_Init(new_buckets[idx], 0, &self->allocator);
|
||||
}
|
||||
c_PtrBag_Add(new_buckets[idx], node);
|
||||
}
|
||||
c_PtrBag_Destroy(bucket);
|
||||
c_Allocator_Free(&self->allocator, bucket);
|
||||
}
|
||||
|
||||
self->capacity = new_capacity;
|
||||
c_Allocator_Free(&self->allocator, self->buckets);
|
||||
self->buckets = new_buckets;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_HashST_Put(c_HashST_t* self, const void* key, const void* val) {
|
||||
if (!self || key==NULL || val==NULL) return C_ERR_PARAM;
|
||||
c_HashSTNode_t* node = GetNode(self, key);
|
||||
|
||||
// 情况1: key 已经存在
|
||||
if (node!=NULL) {
|
||||
self->val_ops.free(node->val, self->val_ops.arg);
|
||||
node->val = val?self->val_ops.cp(val, self->val_ops.arg):NULL;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
node = c_Allocator_Alloc(&self->allocator, sizeof(*node));
|
||||
if (!node) return C_ERR_NOMEM;
|
||||
node->hash = self->key_ops.hash(key, self->key_ops.arg);
|
||||
node->key = self->key_ops.cp(key, self->key_ops.arg);
|
||||
node->val = self->val_ops.cp(val, self->val_ops.arg);
|
||||
|
||||
const c_size_t idx = node->hash % self->capacity;
|
||||
c_PtrBag_t* bucket = self->buckets[idx];
|
||||
if (bucket==NULL) {
|
||||
bucket = c_Allocator_Alloc(&self->allocator, sizeof(*bucket));
|
||||
if (!bucket) return C_ERR_NOMEM;
|
||||
c_PtrBag_Init(bucket, 0, &self->allocator);
|
||||
self->buckets[idx] = bucket;
|
||||
}
|
||||
|
||||
c_PtrBag_Add(bucket, node);
|
||||
|
||||
self->size++;
|
||||
|
||||
// if (self->size > self->capacity * GROWTH_FACTOR) {
|
||||
// return c_HashST_Resize(self, self->capacity * 2);
|
||||
// }
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
c_err_t c_HashST_Remove(c_HashST_t* self, const void* key) {
|
||||
if (!self || key==NULL) return C_ERR_PARAM;
|
||||
|
||||
c_HashSTNode_t* node = NULL;
|
||||
c_PtrBag_t* bucket = NULL;
|
||||
|
||||
uint32_t hash=0;
|
||||
c_size_t bucket_idx = 0;
|
||||
|
||||
hash = self->key_ops.hash(key, self->key_ops.arg);
|
||||
bucket_idx = hash % self->capacity;
|
||||
bucket = self->buckets[bucket_idx];
|
||||
|
||||
if (!bucket) {
|
||||
return C_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
for (c_size_t i=0; i<bucket->size; i++) {
|
||||
node = c_PtrBag_Get(bucket, i);
|
||||
if (!node) continue;
|
||||
if (node->hash == hash) {
|
||||
if (self->key_ops.eq(node->key, key, self->key_ops.arg)) {
|
||||
self->key_ops.free(node->key, self->key_ops.arg);
|
||||
self->val_ops.free(node->val, self->val_ops.arg);
|
||||
c_Allocator_Free(&self->allocator, node);
|
||||
c_PtrBag_RemoveAt(bucket, i, 0);
|
||||
self->size--;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return C_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
bool c_HashST_Contains(c_HashST_t* self, const void* key) {
|
||||
if (!self || key==NULL) return false;
|
||||
const c_HashSTNode_t* node = GetNode(self, key);
|
||||
if (node==NULL) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
c_size_t c_HashST_NumCol(c_HashST_t* self) {
|
||||
if (!self) return 0;
|
||||
c_size_t result = 0;
|
||||
for (c_size_t i=0; i<self->capacity; i++) {
|
||||
result+= NumCol(self->buckets[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void c_HashST_ForEach(c_HashST_t* self, void (*apply)(c_HashSTNode_t* node, void* args), void* args) {
|
||||
if (!self || !apply) return;
|
||||
if (self->size==0) return;
|
||||
|
||||
for (c_size_t i=0; i<self->capacity; i++) {
|
||||
c_PtrBag_t* bucket = self->buckets[i];
|
||||
if (!bucket) continue;
|
||||
for (c_size_t j=0; j<bucket->size; j++) {
|
||||
c_HashSTNode_t* node = c_PtrBag_Get(bucket, j);
|
||||
if (!node) continue;
|
||||
apply(node, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef INCLUDED_C_HASHST_H
|
||||
#define INCLUDED_C_HASHST_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_PTRBAG_H
|
||||
#include <c_PtrBag.h>
|
||||
#endif /*INCLUDED_C_PTRBAG_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
typedef struct c_HashKeyOps_t {
|
||||
uint32_t (*hash)(const void *data, void *arg);
|
||||
void* (*cp)(const void *data, void *arg);
|
||||
void (*free)(void *data, void *arg);
|
||||
bool (*eq)(const void *data1, const void *data2, void *arg);
|
||||
void *arg;
|
||||
} c_HashKeyOps_t;
|
||||
|
||||
typedef struct c_HashValOps_t {
|
||||
void* (*cp)(const void *data, void *arg);
|
||||
void (*free)(void *data, void *arg);
|
||||
bool (*eq)(const void *data1, const void *data2, void *arg);
|
||||
void *arg;
|
||||
} c_HashValOps_t;
|
||||
|
||||
typedef struct c_HashSTNode_t {
|
||||
uint32_t hash;
|
||||
void* key;
|
||||
void* val;
|
||||
}c_HashSTNode_t;
|
||||
|
||||
typedef struct {
|
||||
c_PtrBag_t** buckets;
|
||||
c_size_t capacity;
|
||||
c_size_t size;
|
||||
c_HashKeyOps_t key_ops;
|
||||
c_HashValOps_t val_ops;
|
||||
c_Allocator_t allocator;
|
||||
}c_HashST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_HashST_Init(c_HashST_t* self, c_size_t capacity, c_HashKeyOps_t key_ops, c_HashValOps_t val_ops, c_Allocator_t* allocator);
|
||||
|
||||
void c_HashST_Destroy(c_HashST_t* self);
|
||||
|
||||
c_err_t c_HashST_Resize(c_HashST_t* self, c_size_t new_capacity);
|
||||
|
||||
c_err_t c_HashST_Put(c_HashST_t* self, const void* key, const void* val);
|
||||
|
||||
void* c_HashST_Get(c_HashST_t* self, const void* key);
|
||||
|
||||
c_err_t c_HashST_Remove(c_HashST_t* self, const void* key);
|
||||
|
||||
bool c_HashST_Contains(c_HashST_t* self, const void* key);
|
||||
|
||||
c_size_t c_HashST_NumCol(c_HashST_t* self);
|
||||
|
||||
void c_HashST_ForEach(c_HashST_t* self, void (*apply)(c_HashSTNode_t* node, void* args), void* args);
|
||||
|
||||
#endif /*INCLUDED_C_HASHST_H*/
|
||||
@@ -0,0 +1,216 @@
|
||||
#include "c_HashST.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// ==========================================
|
||||
// 1. 测试框架配套:虚操作函数模拟实现
|
||||
// ==========================================
|
||||
|
||||
// --- Key 虚操作实现:以动态独立分配内存、长度未知的“深层堆字符串指针 (char**)”作为键 ---
|
||||
static uint32_t test_key_string_hash(const void* data, void* arg) {
|
||||
(void)arg;
|
||||
const char* str = *(const char* const*)data;
|
||||
uint32_t hash = 0;
|
||||
while (*str) {
|
||||
hash = 31 * hash + (unsigned char)(*str);
|
||||
str++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void* test_key_string_cp(const void* data, void* arg) {
|
||||
(void)arg;
|
||||
const char* original = *(const char* const*)data;
|
||||
char* clone = (char*)malloc(strlen(original) + 1);
|
||||
if (clone) strcpy(clone, original);
|
||||
|
||||
char** storage = (char**)malloc(sizeof(char*));
|
||||
if (storage) *storage = clone;
|
||||
return (void*)storage;
|
||||
}
|
||||
|
||||
static void test_key_string_free(void* data, void* arg) {
|
||||
(void)arg;
|
||||
if (data) {
|
||||
char** storage = (char**)data;
|
||||
free(*storage);
|
||||
free(storage);
|
||||
}
|
||||
}
|
||||
|
||||
static bool test_key_string_eq(const void* data1, const void* data2, void* arg) {
|
||||
(void)arg;
|
||||
const char* s1 = *(const char* const*)data1;
|
||||
const char* s2 = *(const char* const*)data2;
|
||||
return strcmp(s1, s2) == 0;
|
||||
}
|
||||
|
||||
// --- Value 虚操作实现:以动态独立分配内存的“结构体对象指针 (Task_t**)”作为值 ---
|
||||
typedef struct {
|
||||
int id;
|
||||
int payload;
|
||||
} TestTask_t;
|
||||
|
||||
static void* test_val_task_cp(const void* data, void* arg) {
|
||||
(void)arg;
|
||||
const TestTask_t* original = *(const TestTask_t* const*)data;
|
||||
TestTask_t* clone = (TestTask_t*)malloc(sizeof(TestTask_t));
|
||||
if (clone) {
|
||||
clone->id = original->id;
|
||||
clone->payload = original->payload;
|
||||
}
|
||||
|
||||
TestTask_t** storage = (TestTask_t**)malloc(sizeof(TestTask_t*));
|
||||
if (storage) *storage = clone;
|
||||
return (void*)storage;
|
||||
}
|
||||
|
||||
static void test_val_task_free(void* data, void* arg) {
|
||||
(void)arg;
|
||||
if (data) {
|
||||
TestTask_t** storage = (TestTask_t**)data;
|
||||
free(*storage);
|
||||
free(storage);
|
||||
}
|
||||
}
|
||||
|
||||
static bool test_val_task_eq(const void* data1, const void* data2, void* arg) {
|
||||
(void)arg;
|
||||
const TestTask_t* t1 = *(const TestTask_t* const*)data1;
|
||||
const TestTask_t* t2 = *(const TestTask_t* const*)data2;
|
||||
return (t1->id == t2->id && t1->payload == t2->payload);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 2. 自动化单元测试集
|
||||
// ==========================================
|
||||
|
||||
TEST_CASE(test_c_HashST_Base_CRUD_Flow) {
|
||||
c_HashST_t map;
|
||||
|
||||
// 1. 初始化多态虚操作集(Key 采用堆字符串,Value 采用堆任务结构体)
|
||||
c_HashKeyOps_t key_ops = { test_key_string_hash, test_key_string_cp, test_key_string_free, test_key_string_eq, NULL };
|
||||
c_HashValOps_t val_ops = { test_val_task_cp, test_val_task_free, test_val_task_eq, NULL };
|
||||
|
||||
// 2. 初始化初始容量为 4 的哈希符号表
|
||||
c_err_t err = c_HashST_Init(&map, 4, key_ops, val_ops, &c_DefaultAllocator);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
// 声明待插入的临时浅数据线
|
||||
const char* k1 = "Task_Alpha"; TestTask_t t1_raw = { 101, 555 }; const TestTask_t* v1 = &t1_raw;
|
||||
const char* k2 = "Task_Beta"; TestTask_t t2_raw = { 102, 666 }; const TestTask_t* v2 = &t2_raw;
|
||||
const char* k3 = "Task_Gamma"; TestTask_t t3_raw = { 103, 777 }; const TestTask_t* v3 = &t3_raw;
|
||||
|
||||
// 3. Put 添加行为断言
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k1, &v1));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k2, &v2));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k3, &v3));
|
||||
ASSERT_INT_EQ(3, (int)map.size);
|
||||
|
||||
// 4. Contains 包含性状态断言(使用物理地址不同的副本 Key 进行全等检索)
|
||||
const char* search_k1 = "Task_Alpha";
|
||||
const char* search_fake = "Task_Unknown";
|
||||
ASSERT_TRUE(c_HashST_Contains(&map, &search_k1));
|
||||
ASSERT_TRUE(!c_HashST_Contains(&map, &search_fake));
|
||||
|
||||
// 5. Get 数据读取与物理结构体字段穿透比对
|
||||
void* get_res_ptr = c_HashST_Get(&map, &search_k1);
|
||||
ASSERT_TRUE(get_res_ptr != NULL);
|
||||
|
||||
// 穿透二级指针,精确检验深拷贝出来的值的成员变量
|
||||
TestTask_t* matched_task = *(TestTask_t**)get_res_ptr;
|
||||
ASSERT_INT_EQ(101, matched_task->id);
|
||||
ASSERT_INT_EQ(555, matched_task->payload);
|
||||
|
||||
// 6. Put 相同键下的覆写更新(Overwrite)语义核验
|
||||
TestTask_t t1_update_raw = { 101, 9999 }; const TestTask_t* v1_update = &t1_update_raw;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k1, &v1_update));
|
||||
ASSERT_INT_EQ(3, (int)map.size); // 覆写后,全局哈希大小必须守恒
|
||||
|
||||
get_res_ptr = c_HashST_Get(&map, &search_k1);
|
||||
ASSERT_TRUE(get_res_ptr != NULL);
|
||||
ASSERT_INT_EQ(9999, (*(TestTask_t**)get_res_ptr)->payload); // 配额必须已被更新覆盖
|
||||
|
||||
// 7. Remove 单路摘除断言
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Remove(&map, &search_k1));
|
||||
ASSERT_INT_EQ(2, (int)map.size); // 递减正确
|
||||
ASSERT_TRUE(c_HashST_Get(&map, &search_k1) == NULL); // 再拿应该彻底变空
|
||||
|
||||
// 8. 全清理注销销毁线,多态自由解构,阻断任何残留
|
||||
c_HashST_Destroy(&map);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_HashST_Resize_And_Collision) {
|
||||
c_HashST_t map;
|
||||
c_HashKeyOps_t key_ops = { test_key_string_hash, test_key_string_cp, test_key_string_free, test_key_string_eq, NULL };
|
||||
c_HashValOps_t val_ops = { test_val_task_cp, test_val_task_free, test_val_task_eq, NULL };
|
||||
|
||||
// 1. 初始化一个极小容量的哈希表(故意设为 1,强迫后面插入的所有节点落入同一个桶中,构成 100% 碰撞拉链链条)
|
||||
c_err_t err = c_HashST_Init(&map, 1, key_ops, val_ops, &c_DefaultAllocator);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
const char* k1 = "Key_Coll_1"; TestTask_t t1 = { 1, 10 }; const TestTask_t* v1 = &t1;
|
||||
const char* k2 = "Key_Coll_2"; TestTask_t t2 = { 2, 20 }; const TestTask_t* v2 = &t2;
|
||||
const char* k3 = "Key_Coll_3"; TestTask_t t3 = { 3, 30 }; const TestTask_t* v3 = &t3;
|
||||
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k1, &v1));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k2, &v2));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k3, &v3));
|
||||
|
||||
// 2. NumCol 碰撞指标验证:在容量为 1 且塞入 3 个数据时,产生的物理碰撞线数必然为 2 次
|
||||
ASSERT_INT_EQ(2, (int)c_HashST_NumCol(&map));
|
||||
|
||||
// 3. 核心压测:触发大跨度扩容变轨(容量从 1 直接倍增重塑至 16)
|
||||
// 扩容后,原先挤在 0 号桶拉链中的 3 个冲突节点必须被重新洗牌、散列分流到各个新桶中
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Resize(&map, 16));
|
||||
ASSERT_INT_EQ(16, (int)map.capacity);
|
||||
ASSERT_INT_EQ(3, (int)map.size); // 全局大小守恒不变
|
||||
|
||||
// 4. 扩容分流后的碰撞指标必须自适应下降(通常随机散列到 16 个桶中后,碰撞数会锐减为 0)
|
||||
ASSERT_TRUE(c_HashST_NumCol(&map) < 2);
|
||||
|
||||
// 5. 扩容变轨后,再次使用 Get 穿透读取,验证多态路由未丢失
|
||||
const char* search_k3 = "Key_Coll_3";
|
||||
void* get_res_ptr = c_HashST_Get(&map, &search_k3);
|
||||
ASSERT_TRUE(get_res_ptr != NULL);
|
||||
ASSERT_INT_EQ(30, (*(TestTask_t**)get_res_ptr)->payload);
|
||||
|
||||
c_HashST_Destroy(&map);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_HashST_Empty_And_Toxicity_Defenses) {
|
||||
c_HashST_t map;
|
||||
c_HashKeyOps_t key_ops = { test_key_string_hash, test_key_string_cp, test_key_string_free, test_key_string_eq, NULL };
|
||||
c_HashValOps_t val_ops = { test_val_task_cp, test_val_task_free, test_val_task_eq, NULL };
|
||||
|
||||
c_HashST_Init(&map, 4, key_ops, val_ops, NULL); // 降级测试
|
||||
|
||||
const char* k = "Toxic_Key";
|
||||
|
||||
// 1. 空仓返回与毒入参前置拦截防御
|
||||
ASSERT_TRUE(c_HashST_Get(&map, &k) == NULL);
|
||||
ASSERT_TRUE(!c_HashST_Contains(&map, &k));
|
||||
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_HashST_Remove(&map, &k));
|
||||
ASSERT_INT_EQ(0, (int)c_HashST_NumCol(&map));
|
||||
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_HashST_Init(NULL, 4, key_ops, val_ops, NULL));
|
||||
|
||||
c_HashST_Destroy(&map);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 3. 独立测试运行入口
|
||||
// ==========================================
|
||||
int main(void) {
|
||||
TEST_START(C_HashST_PolymorphicObject_TestSuite);
|
||||
|
||||
// 顺序调度高级多态哈希表的各项极限业务场景
|
||||
RUN_TEST(test_c_HashST_Base_CRUD_Flow);
|
||||
RUN_TEST(test_c_HashST_Resize_And_Collision);
|
||||
RUN_TEST(test_c_HashST_Empty_And_Toxicity_Defenses);
|
||||
|
||||
TEST_REPORT();
|
||||
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
#include <c_RBTreeSet.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
bool c_RBSet_IsRed(const c_RBSetNode_t* node) {
|
||||
if (node == NULL) return false; // 空链接恒为隐式黑色链接 'B'
|
||||
return node->color == C_RB_RED;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBSet_RotateLeft(c_RBSetNode_t** node_ptr) {
|
||||
c_RBSetNode_t* h = *node_ptr;
|
||||
c_RBSetNode_t* x = h->right;
|
||||
h->right = x->left;
|
||||
x->left = h;
|
||||
x->color = h->color;
|
||||
h->color = C_RB_RED;
|
||||
*node_ptr = x;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBSet_RotateRight(c_RBSetNode_t** node_ptr) {
|
||||
c_RBSetNode_t* h = *node_ptr;
|
||||
c_RBSetNode_t* x = h->left;
|
||||
h->left = x->right;
|
||||
x->right = h;
|
||||
x->color = h->color;
|
||||
h->color = C_RB_RED;
|
||||
*node_ptr = x;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBSet_FlipColors(c_RBSetNode_t* h) {
|
||||
#if 0
|
||||
h->color = (h->color == C_RB_RED) ? C_RB_BLACK : C_RB_RED;
|
||||
if (h->left) h->left->color = (h->left->color == C_RB_RED) ? C_RB_BLACK : C_RB_RED;
|
||||
if (h->right) h->right->color = (h->right->color == C_RB_RED) ? C_RB_BLACK : C_RB_RED;
|
||||
#endif
|
||||
|
||||
h->color = !h->color;
|
||||
if (h->left) h->left->color = !h->left->color;
|
||||
if (h->right) h->right->color = !h->right->color;
|
||||
}
|
||||
|
||||
static void c_RBSet_Balance(c_RBSetNode_t** node_ptr) {
|
||||
if (*node_ptr == NULL) return;
|
||||
if (c_RBSet_IsRed((*node_ptr)->right) && !c_RBSet_IsRed((*node_ptr)->left)) {
|
||||
c_RBSet_RotateLeft(node_ptr);
|
||||
}
|
||||
if (c_RBSet_IsRed((*node_ptr)->left) && c_RBSet_IsRed((*node_ptr)->left->left)) {
|
||||
c_RBSet_RotateRight(node_ptr);
|
||||
}
|
||||
if (c_RBSet_IsRed((*node_ptr)->left) && c_RBSet_IsRed((*node_ptr)->right)) {
|
||||
c_RBSet_FlipColors(*node_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void c_RBSet_MoveRedLeft(c_RBSetNode_t** node_ptr) {
|
||||
c_RBSetNode_t* h = *node_ptr;
|
||||
c_RBSet_FlipColors(h);
|
||||
if (c_RBSet_IsRed(h->right->left)) {
|
||||
c_RBSet_RotateRight(&(h->right));
|
||||
c_RBSet_RotateLeft(node_ptr);
|
||||
c_RBSet_FlipColors(*node_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void c_RBSet_MoveRedRight(c_RBSetNode_t** node_ptr) {
|
||||
c_RBSetNode_t* h = *node_ptr;
|
||||
c_RBSet_FlipColors(h);
|
||||
if (c_RBSet_IsRed(h->left->left)) {
|
||||
c_RBSet_RotateRight(node_ptr);
|
||||
c_RBSet_FlipColors(*node_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 核心接口层实现
|
||||
// ==================================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 就地初始化红黑树集合
|
||||
*/
|
||||
c_err_t c_RBTreeSet_Init(c_RBTreeSet_t* self, c_size_t key_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator) {
|
||||
if (!self || key_size == 0 || !cmp) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
if (allocator) {
|
||||
self->allocator = *allocator;
|
||||
} else {
|
||||
self->allocator = c_DefaultAllocator;
|
||||
}
|
||||
|
||||
self->root = NULL;
|
||||
self->size = 0;
|
||||
self->key_size = key_size;
|
||||
self->cmp = cmp;
|
||||
self->args = args;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部递归插入自平衡状态机(内置去重机制)
|
||||
*/
|
||||
static c_err_t c_RBTreeSet_InternalAdd(c_RBTreeSet_t* self, c_RBSetNode_t** node_ptr, const void* key, bool* is_new_inserted) {
|
||||
c_RBSetNode_t* curr = *node_ptr;
|
||||
|
||||
if (curr == NULL) {
|
||||
c_RBSetNode_t* new_node = (c_RBSetNode_t*)c_Allocator_Alloc(&self->allocator, sizeof(c_RBSetNode_t));
|
||||
void* new_key = c_Allocator_Alloc(&self->allocator, self->key_size);
|
||||
|
||||
if (!new_node || !new_key) {
|
||||
if (new_node) c_Allocator_Free(&self->allocator, new_node);
|
||||
if (new_key) c_Allocator_Free(&self->allocator, new_key);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
memcpy(new_key, key, self->key_size);
|
||||
new_node->key = new_key;
|
||||
new_node->left = NULL;
|
||||
new_node->right = NULL;
|
||||
new_node->color = C_RB_RED; // 新生成的链接赋红
|
||||
|
||||
*node_ptr = new_node;
|
||||
*is_new_inserted = true;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
c_err_t err = C_ERR_OK;
|
||||
|
||||
if (cmp_res < 0) {
|
||||
err = c_RBTreeSet_InternalAdd(self, &(curr->left), key, is_new_inserted);
|
||||
} else if (cmp_res > 0) {
|
||||
err = c_RBTreeSet_InternalAdd(self, &(curr->right), key, is_new_inserted);
|
||||
} else {
|
||||
// 🌟【集合核心去重约束】:若元素已在树中存在,果断拦截,抛出 C_ERR_EXIST 阻止其向下流转
|
||||
*is_new_inserted = false;
|
||||
return C_ERR_EXIST;
|
||||
}
|
||||
|
||||
if (err != C_ERR_OK && err != C_ERR_EXIST) return err;
|
||||
|
||||
// 自底向上 Sedgewick 三部曲修复
|
||||
c_RBSet_Balance(node_ptr);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向集合中注入添加一个元素(自动去重,时间复杂度 O(log N))
|
||||
*/
|
||||
c_err_t c_RBTreeSet_Add(c_RBTreeSet_t* self, const void* key) {
|
||||
if (!self || !key) return C_ERR_PARAM;
|
||||
|
||||
bool is_new = false;
|
||||
c_err_t err = c_RBTreeSet_InternalAdd(self, &(self->root), key, &is_new);
|
||||
if (err == C_ERR_OK && is_new) {
|
||||
self->size++;
|
||||
self->root->color = C_RB_BLACK; // 根节点链接刷黑
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 包含性检索判定(时间复杂度稳定的 O(log N))
|
||||
*/
|
||||
bool c_RBTreeSet_Contains(const c_RBTreeSet_t* self, const void* key) {
|
||||
if (!self || !key) return false;
|
||||
c_RBSetNode_t* curr = self->root;
|
||||
while (curr != NULL) {
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
if (cmp_res < 0) curr = curr->left;
|
||||
else if (cmp_res > 0) curr = curr->right;
|
||||
else return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部辅助:寻找并断开右子树绝对最小值节点
|
||||
*/
|
||||
static c_RBSetNode_t* c_RBTreeSet_InternalDeleteMin(c_RBTreeSet_t* self, c_RBSetNode_t** node_ptr) {
|
||||
c_RBSetNode_t* curr = *node_ptr;
|
||||
if (curr->left == NULL) {
|
||||
*node_ptr = NULL;
|
||||
return curr;
|
||||
}
|
||||
if (!c_RBSet_IsRed(curr->left) && !c_RBSet_IsRed(curr->left->left)) {
|
||||
c_RBSet_MoveRedLeft(node_ptr);
|
||||
}
|
||||
c_RBSetNode_t* min_node = c_RBTreeSet_InternalDeleteMin(self, &((*node_ptr)->left));
|
||||
c_RBSet_Balance(node_ptr);
|
||||
return min_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部递归自适应删除控制流(Hibbard 替换策略)
|
||||
*/
|
||||
static c_err_t c_RBTreeSet_InternalDelete(c_RBTreeSet_t* self, c_RBSetNode_t** node_ptr, const void* key) {
|
||||
c_RBSetNode_t* curr = *node_ptr;
|
||||
if (curr == NULL) return C_ERR_NOTFOUND;
|
||||
|
||||
if (self->cmp(key, curr->key, self->args) < 0) {
|
||||
if (!c_RBSet_IsRed(curr->left) && !c_RBSet_IsRed(curr->left->left)) {
|
||||
c_RBSet_MoveRedLeft(node_ptr);
|
||||
}
|
||||
c_err_t err = c_RBTreeSet_InternalDelete(self, &((*node_ptr)->left), key);
|
||||
c_RBSet_Balance(node_ptr);
|
||||
return err;
|
||||
}
|
||||
else {
|
||||
if (c_RBSet_IsRed(curr->left)) {
|
||||
c_RBSet_RotateRight(node_ptr);
|
||||
curr = *node_ptr;
|
||||
}
|
||||
|
||||
if (self->cmp(key, curr->key, self->args) == 0 && (curr->right == NULL)) {
|
||||
c_RBSetNode_t* old_node = curr;
|
||||
*node_ptr = curr->left;
|
||||
c_Allocator_Free(&self->allocator, old_node->key);
|
||||
c_Allocator_Free(&self->allocator, old_node);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
if (!c_RBSet_IsRed(curr->right) && !c_RBSet_IsRed(curr->right->left)) {
|
||||
c_RBSet_MoveRedRight(node_ptr);
|
||||
curr = *node_ptr;
|
||||
}
|
||||
|
||||
if (self->cmp(key, curr->key, self->args) == 0) {
|
||||
c_RBSetNode_t* old_node = curr;
|
||||
c_RBSetNode_t* successor = c_RBTreeSet_InternalDeleteMin(self, &(curr->right));
|
||||
|
||||
successor->left = old_node->left;
|
||||
successor->right = (*node_ptr)->right;
|
||||
successor->color = old_node->color;
|
||||
*node_ptr = successor;
|
||||
|
||||
c_Allocator_Free(&self->allocator, old_node->key);
|
||||
c_Allocator_Free(&self->allocator, old_node);
|
||||
c_RBSet_Balance(node_ptr);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
else {
|
||||
c_err_t err = c_RBTreeSet_InternalDelete(self, &((*node_ptr)->right), key);
|
||||
c_RBSet_Balance(node_ptr);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从有序集合中精准剔除一个指定元素
|
||||
*/
|
||||
c_err_t c_RBTreeSet_Remove(c_RBTreeSet_t* self, const void* key) {
|
||||
if (!self || !key) return C_ERR_PARAM;
|
||||
if (self->size == 0 || self->root == NULL) return C_ERR_EMPTY;
|
||||
|
||||
c_err_t err = c_RBTreeSet_InternalDelete(self, &(self->root), key);
|
||||
if (err == C_ERR_OK) {
|
||||
self->size--;
|
||||
if (self->root != NULL) self->root->color = C_RB_BLACK;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void c_RBTreeSet_InternalDeinit(c_Allocator_t* alloc, c_RBSetNode_t* node) {
|
||||
if (node == NULL) return;
|
||||
c_RBTreeSet_InternalDeinit(alloc, node->left);
|
||||
c_RBTreeSet_InternalDeinit(alloc, node->right);
|
||||
c_Allocator_Free(alloc, node->key);
|
||||
c_Allocator_Free(alloc, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 集合彻底反初始化销毁释放
|
||||
*/
|
||||
void c_RBTreeSet_Destroy(c_RBTreeSet_t* self) {
|
||||
if (self && self->root) {
|
||||
c_RBTreeSet_InternalDeinit(&self->allocator, self->root);
|
||||
self->root = NULL;
|
||||
self->size = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef INCLUDED_C_RBTREESET_H
|
||||
#define INCLUDED_C_RBTREESET_H
|
||||
|
||||
#ifndef INCLUDED_C_SORTCOMPARE_H
|
||||
#include <c_SortCompare.h>
|
||||
#endif /*INCLUDED_C_SORTCOMPARE_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
#define C_RB_RED true
|
||||
#define C_RB_BLACK false
|
||||
|
||||
/**
|
||||
* @brief 红黑树集合内部单节点物理封装
|
||||
*/
|
||||
typedef struct c_RBSetNode_t {
|
||||
void* key; // 独立分配存储的元素键物理地址
|
||||
struct c_RBSetNode_t* left; // 左子节点指针
|
||||
struct c_RBSetNode_t* right; // 右子节点指针
|
||||
bool color; // 指向该节点的父链接颜色 (R为红,B为黑)
|
||||
} c_RBSetNode_t;
|
||||
|
||||
/**
|
||||
* @brief 工业级泛型红黑树集合结构体(分配器内联组合版)
|
||||
*/
|
||||
typedef struct {
|
||||
c_RBSetNode_t* root; // 集合树根节点指针
|
||||
c_size_t size; // 当前集合内有效驻留的唯一元素总个数
|
||||
c_size_t key_size; // 元素键占用的物理字节大小 (sizeof)
|
||||
c_SortCompare_t cmp; // 元素专用动态回调比对器
|
||||
void* args; // 自定义上下文参数指针
|
||||
c_Allocator_t allocator; // 内联组合分配器实例与自适应 Fallback 缺省
|
||||
} c_RBTreeSet_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_RBTreeSet_Init(c_RBTreeSet_t* self, c_size_t key_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator);
|
||||
|
||||
c_err_t c_RBTreeSet_Add(c_RBTreeSet_t* self, const void* key);
|
||||
|
||||
bool c_RBTreeSet_Contains(const c_RBTreeSet_t* self, const void* key);
|
||||
|
||||
c_err_t c_RBTreeSet_Remove(c_RBTreeSet_t* self, const void* key);
|
||||
|
||||
void c_RBTreeSet_Destroy(c_RBTreeSet_t* self);
|
||||
|
||||
#endif /*INCLUDED_C_RBTREESET_H*/
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "c_RBTreeSet.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int rbset_compare_ints(const void* a, const void* b, void* args) {
|
||||
(void)args;
|
||||
int arg1 = *(const int*)a;
|
||||
int arg2 = *(const int*)b;
|
||||
if (arg1 < arg2) return -1;
|
||||
if (arg1 > arg2) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE(test_c_RBTreeSet_Unique_CRUD) {
|
||||
c_RBTreeSet_t set;
|
||||
c_err_t err = c_RBTreeSet_Init(&set, sizeof(int), rbset_compare_ints, NULL, &c_DefaultAllocator);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
int n1 = 50; int n2 = 20; int n3 = 80;
|
||||
|
||||
// 1. 基础 Add 压入与 Contains 检测断言
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTreeSet_Add(&set, &n1));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTreeSet_Add(&set, &n2));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTreeSet_Add(&set, &n3));
|
||||
ASSERT_INT_EQ(3, (int)set.size);
|
||||
ASSERT_TRUE(c_RBTreeSet_Contains(&set, &n2));
|
||||
|
||||
// 2. 🌟【绝杀点 1:去重防御】:再次尝试注入相同的数字 20
|
||||
// 修复后的代码必须果断返回 C_ERR_EXIST 报错拦截,且全局大小坚守为 3 守恒!
|
||||
ASSERT_INT_EQ(C_ERR_EXIST, c_RBTreeSet_Add(&set, &n2));
|
||||
ASSERT_INT_EQ(3, (int)set.size);
|
||||
|
||||
// 3. 🌟【绝杀点 2:平衡级联删除】:移出处于中段的核心枢纽元素 50
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTreeSet_Remove(&set, &n1));
|
||||
ASSERT_INT_EQ(2, (int)set.size);
|
||||
ASSERT_TRUE(!c_RBTreeSet_Contains(&set, &n1));
|
||||
|
||||
// 确保大震荡洗牌后,树根的字符颜色依然被恢复洗回黑链接 'B'
|
||||
ASSERT_INT_EQ(C_RB_BLACK, (int)set.root->color);
|
||||
|
||||
c_RBTreeSet_Destroy(&set);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_RBTreeSet_EmptyDefenses) {
|
||||
c_RBTreeSet_t local_set;
|
||||
c_RBTreeSet_Init(&local_set, sizeof(int), rbset_compare_ints, NULL, NULL);
|
||||
|
||||
int item = 777;
|
||||
// 4. 空仓及入参不合法拦截校验
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_RBTreeSet_Init(NULL, sizeof(int), rbset_compare_ints, NULL, NULL));
|
||||
ASSERT_INT_EQ(C_ERR_EMPTY, c_RBTreeSet_Remove(&local_set, &item)); // 空仓删除安全返回 C_ERR_EMPTY
|
||||
|
||||
c_RBTreeSet_Destroy(&local_set);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 5. 主集成入口
|
||||
// ==========================================
|
||||
int main(void) {
|
||||
TEST_START(C_RBTreeSet_UniqueUnsigned_TestSuite);
|
||||
RUN_TEST(test_c_RBTreeSet_Unique_CRUD);
|
||||
RUN_TEST(test_c_RBTreeSet_EmptyDefenses);
|
||||
TEST_REPORT();
|
||||
return (g_test_registry.failed_count > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
#include <c_RedBlackBST.h>
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/**
|
||||
* @brief 内部辅助:安全检测某个节点的父链接是否为红
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
bool c_RBBST_IsRed(const c_RBBSTNode_t* node) {
|
||||
if (node == NULL) return C_RB_BLACK; // 空链接恒为黑色链接
|
||||
return node->color == C_RB_RED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部平衡机制 1:左旋操作
|
||||
*
|
||||
* 将任意临时的右倾红链接,通过局部拓扑对调,安全置换为标准合规的左倾红链接
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBBST_RotateLeft(c_RBBSTNode_t** node_ptr) {
|
||||
c_RBBSTNode_t* h = *node_ptr;
|
||||
c_RBBSTNode_t* x = h->right;
|
||||
|
||||
h->right = x->left;
|
||||
x->left = h;
|
||||
|
||||
x->color = h->color;
|
||||
h->color = C_RB_RED;
|
||||
|
||||
*node_ptr = x; // 代理写回父节点
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部平衡机制 2:右旋操作
|
||||
*
|
||||
* 临时放宽左侧的连续红链接,为后续的 4-node 拆分做前置拓扑对调准备
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBBST_RotateRight(c_RBBSTNode_t** node_ptr) {
|
||||
c_RBBSTNode_t* h = *node_ptr;
|
||||
c_RBBSTNode_t* x = h->left;
|
||||
|
||||
h->left = x->right;
|
||||
x->right = h;
|
||||
|
||||
x->color = h->color;
|
||||
h->color = C_RB_RED;
|
||||
|
||||
*node_ptr = x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 内部平衡机制 3:颜色翻转(分解临时的 4-node 节点)
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBBST_FlipColors(c_RBBSTNode_t* h) {
|
||||
h->color = !h->color;
|
||||
if (h->left) h->left->color = !h->left->color;
|
||||
if (h->right) h->right->color = !h->right->color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部递归插入与自适应动态平衡状态机
|
||||
*/
|
||||
static c_err_t c_RedBlackBST_InternalPut(c_RedBlackBST_t* self, c_RBBSTNode_t** node_ptr, const void* key, const void* val, bool* is_new_inserted) {
|
||||
c_RBBSTNode_t* curr = *node_ptr;
|
||||
|
||||
// 递归基:开辟挂载新节点,新生成的链接默认为极其活跃的【红链接】
|
||||
if (curr == NULL) {
|
||||
c_RBBSTNode_t* new_node = (c_RBBSTNode_t*)c_Allocator_Alloc(&self->allocator, sizeof(c_RBBSTNode_t));
|
||||
void* new_key = c_Allocator_Alloc(&self->allocator, self->key_size);
|
||||
void* new_val = c_Allocator_Alloc(&self->allocator, self->val_size);
|
||||
|
||||
if (!new_node || !new_key || !new_val) {
|
||||
if (new_node) c_Allocator_Free(&self->allocator, new_node);
|
||||
if (new_key) c_Allocator_Free(&self->allocator, new_key);
|
||||
if (new_val) c_Allocator_Free(&self->allocator, new_val);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
memcpy(new_key, key, self->key_size);
|
||||
memcpy(new_val, val, self->val_size);
|
||||
new_node->key = new_key;
|
||||
new_node->val = new_val;
|
||||
new_node->left = NULL;
|
||||
new_node->right = NULL;
|
||||
new_node->color = C_RB_RED; // 🌟 核心:新节点一律为红链接
|
||||
|
||||
*node_ptr = new_node;
|
||||
*is_new_inserted = true;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
c_err_t err = C_ERR_OK;
|
||||
|
||||
if (cmp_res < 0) {
|
||||
err = c_RedBlackBST_InternalPut(self, &(curr->left), key, val, is_new_inserted);
|
||||
} else if (cmp_res > 0) {
|
||||
err = c_RedBlackBST_InternalPut(self, &(curr->right), key, val, is_new_inserted);
|
||||
} else {
|
||||
// 键已存在,执行覆写
|
||||
memcpy(curr->val, val, self->val_size);
|
||||
*is_new_inserted = false;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
if (err != C_ERR_OK) return err;
|
||||
|
||||
// 🌟🌟🌟【左倾红黑树自适应自平衡标准控制链(Sedgewick 经典三部曲)】🌟🌟🌟
|
||||
// 指针可能随着旋转被改写,故直接对当前二级指针接管的实体执行自底向上回溯刷新
|
||||
|
||||
// 步骤 1:若右链接为红且左链接为黑,强制执行左旋使其左倾
|
||||
if (c_RBBST_IsRed((*node_ptr)->right) && !c_RBBST_IsRed((*node_ptr)->left)) {
|
||||
c_RBBST_RotateLeft(node_ptr);
|
||||
}
|
||||
// 步骤 2:若左链接为红,且左子节点的左链接也是红(连续两条红链接出现),强制执行右旋平衡
|
||||
if (c_RBBST_IsRed((*node_ptr)->left) && c_RBBST_IsRed((*node_ptr)->left->left)) {
|
||||
c_RBBST_RotateRight(node_ptr);
|
||||
}
|
||||
// 步骤 3:若左右两条子链接同为红,强制翻转颜色,将红链接推向更高的父层级
|
||||
if (c_RBBST_IsRed((*node_ptr)->left) && c_RBBST_IsRed((*node_ptr)->right)) {
|
||||
c_RBBST_FlipColors(*node_ptr);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_RedBlackBST_Init(c_RedBlackBST_t* self, c_size_t key_size, c_size_t val_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator) {
|
||||
if (!self || key_size == 0 || val_size == 0 || !cmp) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
if (allocator) {
|
||||
self->allocator = *allocator;
|
||||
} else {
|
||||
self->allocator = c_DefaultAllocator;
|
||||
}
|
||||
|
||||
self->root = NULL;
|
||||
self->size = 0;
|
||||
self->key_size = key_size;
|
||||
self->val_size = val_size;
|
||||
self->cmp = cmp;
|
||||
self->args = args;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 存入键值对(始终维持树的对数级绝对平衡,时间复杂度 O(log N))
|
||||
*/
|
||||
c_err_t c_RedBlackBST_Put(c_RedBlackBST_t* self, const void* key, const void* val) {
|
||||
if (!self || !key || !val) return C_ERR_PARAM;
|
||||
|
||||
bool is_new = false;
|
||||
c_err_t err = c_RedBlackBST_InternalPut(self, &(self->root), key, val, &is_new);
|
||||
if (err == C_ERR_OK) {
|
||||
if (is_new) self->size++;
|
||||
// 根节点的父链接在扩容和旋转后必须强制恢复回稳定严肃的【黑链接】
|
||||
self->root->color = C_RB_BLACK;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 精准二叉有序检索(时间复杂度稳定为 O(log N))
|
||||
*/
|
||||
c_err_t c_RedBlackBST_Get(const c_RedBlackBST_t* self, const void* key, void* out_val) {
|
||||
if (!self || !key || !out_val) return C_ERR_PARAM;
|
||||
|
||||
c_RBBSTNode_t* curr = self->root;
|
||||
while (curr != NULL) {
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
if (cmp_res < 0) curr = curr->left;
|
||||
else if (cmp_res > 0) curr = curr->right;
|
||||
else {
|
||||
memcpy(out_val, curr->val, self->val_size);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
}
|
||||
return C_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查树中是否有效包含指定的 Key
|
||||
*/
|
||||
bool c_RedBlackBST_Contains(const c_RedBlackBST_t* self, const void* key) {
|
||||
if (!self || !key) return false;
|
||||
c_RBBSTNode_t* curr = self->root;
|
||||
while (curr != NULL) {
|
||||
int cmp_res = self->cmp(key, curr->key, self->args);
|
||||
if (cmp_res < 0) curr = curr->left;
|
||||
else if (cmp_res > 0) curr = curr->right;
|
||||
else return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void c_RedBlackBST_InternalDeinit(c_Allocator_t* alloc, c_RBBSTNode_t* node) {
|
||||
if (node == NULL) return;
|
||||
c_RedBlackBST_InternalDeinit(alloc, node->left);
|
||||
c_RedBlackBST_InternalDeinit(alloc, node->right);
|
||||
c_Allocator_Free(alloc, node->key);
|
||||
c_Allocator_Free(alloc, node->val);
|
||||
c_Allocator_Free(alloc, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 彻底销毁红黑树并逆向释放全部堆资源
|
||||
*/
|
||||
void c_RedBlackBST_Destroy(c_RedBlackBST_t* self) {
|
||||
if (self && self->root) {
|
||||
c_RedBlackBST_InternalDeinit(&self->allocator, self->root);
|
||||
self->root = NULL;
|
||||
self->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/**
|
||||
* @brief 自底向上沿途修复平衡(Fix-up)
|
||||
*/
|
||||
static void c_RBBST_Balance(c_RBBSTNode_t** node_ptr) {
|
||||
if (*node_ptr == NULL) return;
|
||||
|
||||
// 1. 纠正右倾红链接
|
||||
if (c_RBBST_IsRed((*node_ptr)->right) && !c_RBBST_IsRed((*node_ptr)->left)) {
|
||||
c_RBBST_RotateLeft(node_ptr);
|
||||
}
|
||||
// 2. 纠正连续红链接
|
||||
if (c_RBBST_IsRed((*node_ptr)->left) && c_RBBST_IsRed((*node_ptr)->left->left)) {
|
||||
c_RBBST_RotateRight(node_ptr);
|
||||
}
|
||||
// 3. 分解临时的 4-node
|
||||
if (c_RBBST_IsRed((*node_ptr)->left) && c_RBBST_IsRed((*node_ptr)->right)) {
|
||||
c_RBBST_FlipColors(*node_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 假设当前节点 h 为红且 h->left 和 h->left->left 都为黑,将红链接强制向左移动
|
||||
*/
|
||||
static void c_RBBST_MoveRedLeft(c_RBBSTNode_t** node_ptr) {
|
||||
c_RBBSTNode_t* h = *node_ptr;
|
||||
c_RBBST_FlipColors(h);
|
||||
|
||||
// 如果亲兄弟节点的左子节点是红链接,说明可以向右边“借”一个红链接过来
|
||||
if (c_RBBST_IsRed(h->right->left)) {
|
||||
c_RBBST_RotateRight(&(h->right));
|
||||
c_RBBST_RotateLeft(node_ptr);
|
||||
// 旋转后由于指针载体换成新根,同步对其刷新颜色翻转
|
||||
c_RBBST_FlipColors(*node_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 假设当前节点 h 为红且 h->right 和 h->right->left 都为黑,将红链接强制向右移动
|
||||
*/
|
||||
static void c_RBBST_MoveRedRight(c_RBBSTNode_t** node_ptr) {
|
||||
c_RBBSTNode_t* h = *node_ptr;
|
||||
c_RBBST_FlipColors(h);
|
||||
|
||||
// 如果亲兄弟节点的左子节点是红链接,说明可以向左边“借”一个红链接过来
|
||||
if (c_RBBST_IsRed(h->left->left)) {
|
||||
c_RBBST_RotateRight(node_ptr);
|
||||
c_RBBST_FlipColors(*node_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部辅助:寻找指定子树的绝对最小值节点(内部删除并解绑提取)
|
||||
*/
|
||||
static c_RBBSTNode_t* c_RedBlackBST_InternalDeleteMin(c_RedBlackBST_t* self, c_RBBSTNode_t** node_ptr) {
|
||||
c_RBBSTNode_t* curr = *node_ptr;
|
||||
|
||||
if (curr->left == NULL) {
|
||||
*node_ptr = NULL; // 断开断裂
|
||||
return curr;
|
||||
}
|
||||
|
||||
// 核心推进:如果当前左子链和左子的左子都是黑色链接,为了防止删除 2-node 崩溃,强行将红链接左推
|
||||
if (!c_RBBST_IsRed(curr->left) && !c_RBBST_IsRed(curr->left->left)) {
|
||||
c_RBBST_MoveRedLeft(node_ptr);
|
||||
}
|
||||
|
||||
c_RBBSTNode_t* min_node = c_RedBlackBST_InternalDeleteMin(self, &((*node_ptr)->left));
|
||||
|
||||
// 自底向上逐级退栈平衡修复
|
||||
c_RBBST_Balance(node_ptr);
|
||||
return min_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 内部递归自适应删除核心逻辑
|
||||
*/
|
||||
static c_err_t c_RedBlackBST_InternalDelete(c_RedBlackBST_t* self, c_RBBSTNode_t** node_ptr, const void* key) {
|
||||
c_RBBSTNode_t* curr = *node_ptr;
|
||||
if (curr == NULL) {
|
||||
return C_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
// 1. 如果目标键小于当前节点,向左子树探查
|
||||
if (self->cmp(key, curr->key, self->args) < 0) {
|
||||
// 如果左边深度不足,前置借调红链接向左推
|
||||
if (!c_RBBST_IsRed(curr->left) && !c_RBBST_IsRed(curr->left->left)) {
|
||||
c_RBBST_MoveRedLeft(node_ptr);
|
||||
}
|
||||
c_err_t err = c_RedBlackBST_InternalDelete(self, &((*node_ptr)->left), key);
|
||||
c_RBBST_Balance(node_ptr); // 退栈修复
|
||||
return err;
|
||||
}
|
||||
else {
|
||||
// 2. 如果当前左子链接是红的,强制右旋。
|
||||
// 这可以让待比较的较大或相等的元素被顺利挪动并暴露到右侧
|
||||
if (c_RBBST_IsRed(curr->left)) {
|
||||
c_RBBST_RotateRight(node_ptr);
|
||||
curr = *node_ptr; // 刷新本地缓存指针
|
||||
}
|
||||
|
||||
// 3. 精确命中检查条件:如果完全相等,且已经走到了树的叶子底部(无右子树)
|
||||
// 此时由于自顶向下红链接移位的保护,curr 必然含有红链接,可以直接抹除并释放
|
||||
if (self->cmp(key, curr->key, self->args) == 0 && (curr->right == NULL)) {
|
||||
c_RBBSTNode_t* old_node = curr;
|
||||
*node_ptr = curr->left; // 让左子树托管
|
||||
|
||||
c_Allocator_Free(&self->allocator, old_node->key);
|
||||
c_Allocator_Free(&self->allocator, old_node->val);
|
||||
c_Allocator_Free(&self->allocator, old_node);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
// 4. 继续向右子树探查(目标比当前大,或者相等但处于中间层级)
|
||||
if (!c_RBBST_IsRed(curr->right) && !c_RBBST_IsRed(curr->right->left)) {
|
||||
c_RBBST_MoveRedRight(node_ptr);
|
||||
curr = *node_ptr;
|
||||
}
|
||||
|
||||
// 5. 中间层级精确命中:执行 Hibbard 后继者替换策略
|
||||
if (self->cmp(key, curr->key, self->args) == 0) {
|
||||
c_RBBSTNode_t* old_node = curr;
|
||||
|
||||
// 剥离并索取右子树的绝对最小值节点作为继承人
|
||||
c_RBBSTNode_t* successor = c_RedBlackBST_InternalDeleteMin(self, &(curr->right));
|
||||
|
||||
// 继承人完美承接原有乱序节点的所有双向指针拓扑以及红黑颜色
|
||||
successor->left = old_node->left;
|
||||
successor->right = (*node_ptr)->right;
|
||||
successor->color = old_node->color;
|
||||
|
||||
*node_ptr = successor; // 代理顶替
|
||||
|
||||
// 彻底释放旧节点占用的内存
|
||||
c_Allocator_Free(&self->allocator, old_node->key);
|
||||
c_Allocator_Free(&self->allocator, old_node->val);
|
||||
c_Allocator_Free(&self->allocator, old_node);
|
||||
|
||||
c_RBBST_Balance(node_ptr);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
else {
|
||||
// 只是普通的向右边路探查
|
||||
c_err_t err = c_RedBlackBST_InternalDelete(self, &((*node_ptr)->right), key);
|
||||
c_RBBST_Balance(node_ptr);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据指定 Key 彻底从左倾红黑树中斩断删除该节点(时间复杂度卡死在完美的 O(log N) 上限)
|
||||
*/
|
||||
c_err_t c_RedBlackBST_Delete(c_RedBlackBST_t* self, const void* key) {
|
||||
if (!self || !key) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
// 状态码规范链条:空仓异常前置拦截
|
||||
if (self->size == 0 || self->root == NULL) {
|
||||
return C_ERR_EMPTY;
|
||||
}
|
||||
|
||||
c_err_t err = c_RedBlackBST_InternalDelete(self, &(self->root), key);
|
||||
if (err == C_ERR_OK) {
|
||||
self->size--;
|
||||
// 如果树还没被删空,必须对重组后的最终新树根强制刷新并恢复黑高性质 `'B'`
|
||||
if (self->root != NULL) {
|
||||
self->root->color = C_RB_BLACK;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef INCLUDED_C_REDBLACKBST_H
|
||||
#define INCLUDED_C_REDBLACKBST_H
|
||||
|
||||
#ifndef INCLUDED_C_SORTCOMPARE_H
|
||||
#include <c_SortCompare.h>
|
||||
#endif /*INCLUDED_C_SORTCOMPARE_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
#define C_RB_RED true
|
||||
#define C_RB_BLACK false
|
||||
|
||||
typedef struct c_RBBSTNode_t {
|
||||
void* key; // 独立分配存储的 Key 物理地址
|
||||
void* val; // 独立分配存储的 Value 物理地址
|
||||
struct c_RBBSTNode_t* left; // 左子节点指针
|
||||
struct c_RBBSTNode_t* right; // 右子节点指针
|
||||
bool color; // 指向该节点的父链接颜色 (R 为红链接,B 为黑链接)
|
||||
} c_RBBSTNode_t;
|
||||
|
||||
typedef struct {
|
||||
c_RBBSTNode_t* root; // 树根节点指针
|
||||
c_size_t size; // 当前有效键值对总个数
|
||||
c_size_t key_size; // 键对象占用的物理字节大小 (sizeof)
|
||||
c_size_t val_size; // 值对象占用的物理字节大小 (sizeof)
|
||||
c_SortCompare_t cmp; // 键专用的动态回调比对器
|
||||
void* args; // 自定义上下文参数指针
|
||||
c_Allocator_t allocator; // 内联组合分配器实例与默认 Fallback 缺省机制
|
||||
} c_RedBlackBST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_RedBlackBST_Init(c_RedBlackBST_t* self, c_size_t key_size, c_size_t val_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator);
|
||||
|
||||
c_err_t c_RedBlackBST_Put(c_RedBlackBST_t* self, const void* key, const void* val);
|
||||
|
||||
c_err_t c_RedBlackBST_Get(const c_RedBlackBST_t* self, const void* key, void* out_val);
|
||||
|
||||
bool c_RedBlackBST_Contains(const c_RedBlackBST_t* self, const void* key);
|
||||
|
||||
c_err_t c_RedBlackBST_Delete(c_RedBlackBST_t* self, const void* key);
|
||||
|
||||
void c_RedBlackBST_Destroy(c_RedBlackBST_t* self);
|
||||
|
||||
#endif /*INCLUDED_C_REDBLACKBST_H*/
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "c_RedBlackBST.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int rbbst_compare_chars(const void* a, const void* b, void* args) {
|
||||
(void)args;
|
||||
char char1 = *(const char*)a;
|
||||
char char2 = *(const char*)b;
|
||||
return char1 - char2;
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_RedBlackBST_CharColorFlow) {
|
||||
c_RedBlackBST_t tree;
|
||||
c_err_t err = c_RedBlackBST_Init(&tree, sizeof(char), sizeof(int), rbbst_compare_chars, NULL, &c_DefaultAllocator);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
// 🌟【高强度测试】:连续输入偏斜升序数据
|
||||
char keys[] = { 'A', 'B', 'C', 'D', 'E' };
|
||||
int vals[] = { 10, 20, 30, 40, 50 };
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RedBlackBST_Put(&tree, &keys[i], &vals[i]));
|
||||
}
|
||||
ASSERT_INT_EQ(5, (int)tree.size);
|
||||
|
||||
// 验证经过旋转后的树根节点确实符合 2-3 树的中位数上提,而不是退化链表
|
||||
char root_key = *(char*)(tree.root->key);
|
||||
ASSERT_TRUE(root_key != 'A');
|
||||
|
||||
// 🌟 核心断言:根据 LLRB 契约,最终留在树最顶端的根节点的父链接颜色必须为黑色字符 'B'!
|
||||
ASSERT_INT_EQ(C_RB_BLACK, tree.root->color);
|
||||
|
||||
// 验证 Get 检索的完好命中
|
||||
int extracted_val = 0;
|
||||
char target_key_D = 'D';
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RedBlackBST_Get(&tree, &target_key_D, &extracted_val));
|
||||
ASSERT_INT_EQ(40, extracted_val);
|
||||
|
||||
ASSERT_TRUE(c_RedBlackBST_Contains(&tree, &target_key_D));
|
||||
|
||||
c_RedBlackBST_Destroy(&tree);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_RedBlackBST_EdgeToxicity) {
|
||||
c_RedBlackBST_t local_tree;
|
||||
c_RedBlackBST_Init(&local_tree, sizeof(char), sizeof(int), rbbst_compare_chars, NULL, NULL);
|
||||
|
||||
char k = 'X'; int v = 99;
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_RedBlackBST_Init(NULL, sizeof(char), sizeof(int), rbbst_compare_chars, NULL, NULL));
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_RedBlackBST_Put(NULL, &k, &v));
|
||||
|
||||
c_RedBlackBST_Destroy(&local_tree);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_RedBlackBST_CascadeDeleteFlow) {
|
||||
c_RedBlackBST_t tree;
|
||||
c_err_t err = c_RedBlackBST_Init(&tree, sizeof(char), sizeof(int), rbbst_compare_chars, NULL, &c_DefaultAllocator);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
// 1. 先填充空表,验证初次空表下的 Delete 状态码契约是否为标准的 C_ERR_EMPTY
|
||||
char key_X = 'X';
|
||||
ASSERT_INT_EQ(C_ERR_EMPTY, c_RedBlackBST_Delete(&tree, &key_X));
|
||||
|
||||
// 2. 密集录入数据,触发多层级左旋、右旋自平衡,构建标准 2-3 树拓扑
|
||||
char keys[] = { 'M', 'E', 'S', 'A', 'R', 'C', 'W' };
|
||||
int vals[] = { 10, 20, 30, 40, 50, 60, 70 };
|
||||
c_size_t num = sizeof(keys) / sizeof(keys[0]);
|
||||
|
||||
for (c_size_t i = 0; i < num; i++) {
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RedBlackBST_Put(&tree, &keys[i], &vals[i]));
|
||||
}
|
||||
ASSERT_INT_EQ(7, (int)tree.size);
|
||||
|
||||
// 3. 【第一轮绝杀】:删除处于树底部的叶子边缘节点 'A'
|
||||
char key_A = 'A';
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RedBlackBST_Delete(&tree, &key_A));
|
||||
ASSERT_INT_EQ(6, (int)tree.size); // 有效递减
|
||||
int get_verify = 0;
|
||||
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_RedBlackBST_Get(&tree, &key_A, &get_verify));
|
||||
|
||||
// 4. 【第二轮绝杀】:删除拥有双向完好子树、处于核心中间分水岭的枢纽根节点 'M'
|
||||
// 修复后的控制流应当极其完美地调用 InternalDeleteMin 从右子树剥离后继者,无伤缝合红黑黑高
|
||||
char key_M = 'M';
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RedBlackBST_Delete(&tree, &key_M));
|
||||
ASSERT_INT_EQ(5, (int)tree.size);
|
||||
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_RedBlackBST_Get(&tree, &key_M, &get_verify));
|
||||
|
||||
// 5. 验证大动荡过后,其余未被删除的邻居兄弟节点依旧稳固且可被 O(log N) 正常 Get
|
||||
char key_C = 'C';
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RedBlackBST_Get(&tree, &key_C, &get_verify));
|
||||
ASSERT_INT_EQ(60, get_verify);
|
||||
|
||||
char key_W = 'W';
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RedBlackBST_Get(&tree, &key_W, &get_verify));
|
||||
ASSERT_INT_EQ(70, get_verify);
|
||||
|
||||
// 🌟【终极颜色完整性断言】:数据大洗牌后,驻留在最顶层的新树根节点颜色字符,必须依然被强行洗回完美的黑色 `'B'`!
|
||||
ASSERT_INT_EQ(C_RB_BLACK, (int)tree.root->color);
|
||||
|
||||
c_RedBlackBST_Destroy(&tree);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_RedBlackBST_DeleteDefenses) {
|
||||
c_RedBlackBST_t local_tree;
|
||||
c_RedBlackBST_Init(&local_tree, sizeof(char), sizeof(int), rbbst_compare_chars, NULL, NULL);
|
||||
|
||||
char k = 'Q';
|
||||
// 6. 验证入参非法的强参数过滤
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_RedBlackBST_Delete(NULL, &k));
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_RedBlackBST_Delete(&local_tree, NULL));
|
||||
|
||||
c_RedBlackBST_Destroy(&local_tree);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 5. 主集成入口
|
||||
// ==========================================
|
||||
int main(void) {
|
||||
TEST_START(C_RedBlackBST_CharColor_TestSuite);
|
||||
RUN_TEST(test_c_RedBlackBST_CharColorFlow);
|
||||
RUN_TEST(test_c_RedBlackBST_EdgeToxicity);
|
||||
|
||||
RUN_TEST(test_c_RedBlackBST_CascadeDeleteFlow);
|
||||
RUN_TEST(test_c_RedBlackBST_DeleteDefenses);
|
||||
|
||||
TEST_REPORT();
|
||||
return (g_test_registry.failed_count > 0 ? 1 : 0);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#include <c_SeqSearchST.h>
|
||||
|
||||
|
||||
c_err_t c_SeqSearchST_Init(c_SeqSearchST_t* self, c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator) {
|
||||
if (!self || key_size == 0 || val_size == 0 || !key_cmp) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
// 自适应分配器降级缺省安全播种
|
||||
if (allocator) {
|
||||
self->allocator = *allocator;
|
||||
} else {
|
||||
self->allocator = c_DefaultAllocator;
|
||||
}
|
||||
|
||||
self->first = NULL;
|
||||
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 存入键值对。若 Key 已存在则覆写更新其 Value;若不存在则使用头插法压入新节点
|
||||
*/
|
||||
c_err_t c_SeqSearchST_Put(c_SeqSearchST_t* self, const void* key, const void* val) {
|
||||
if (!self || !key || !val) return C_ERR_PARAM;
|
||||
|
||||
// 1. 前置顺序走查:如果键值已经存在,直接更新它的值(覆写语义)
|
||||
for (c_SeqSearchSTNode* x = self->first; x != NULL; x = x->next) {
|
||||
if (self->key_cmp(key, x->key, self->args) == 0) {
|
||||
memcpy(x->val, val, self->val_size);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 键值不存在,启动托管分配:开辟新节点以及键和值的独立存储块
|
||||
c_SeqSearchSTNode* new_node = (c_SeqSearchSTNode*)c_Allocator_Alloc(&self->allocator, sizeof(c_SeqSearchSTNode));
|
||||
void* new_key = c_Allocator_Alloc(&self->allocator, self->key_size);
|
||||
void* new_val = c_Allocator_Alloc(&self->allocator, self->val_size);
|
||||
|
||||
if (!new_node || !new_key || !new_val) {
|
||||
// 部分失败安全回收拦截,杜绝内存泄漏
|
||||
if (new_node) c_Allocator_Free(&self->allocator, new_node);
|
||||
if (new_key) c_Allocator_Free(&self->allocator, new_key);
|
||||
if (new_val) c_Allocator_Free(&self->allocator, new_val);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// 执行内存数据安全转储
|
||||
memcpy(new_key, key, self->key_size);
|
||||
memcpy(new_val, val, self->val_size);
|
||||
|
||||
new_node->key = new_key;
|
||||
new_node->val = new_val;
|
||||
|
||||
// 3. 头插法挂载新节点
|
||||
new_node->next = self->first;
|
||||
self->first = new_node;
|
||||
self->size++;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据指定的 Key 检索关联的 Value 值
|
||||
*/
|
||||
c_err_t c_SeqSearchST_Get(const c_SeqSearchST_t* self, const void* key, void* out_val) {
|
||||
if (!self || !key || !out_val) return C_ERR_PARAM;
|
||||
|
||||
for (const c_SeqSearchSTNode* x = self->first; x != NULL; x = x->next) {
|
||||
if (self->key_cmp(key, x->key, self->args) == 0) {
|
||||
memcpy(out_val, x->val, self->val_size);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return C_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查符号表内是否包含指定的 Key 键值
|
||||
*/
|
||||
bool c_SeqSearchST_Contains(const c_SeqSearchST_t* self, const void* key) {
|
||||
if (!self || !key) return false;
|
||||
for (const c_SeqSearchSTNode* x = self->first; x != NULL; x = x->next) {
|
||||
if (self->key_cmp(key, x->key, self->args) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据指定 Key 彻底从符号表中移出其关联的键值对节点
|
||||
*/
|
||||
c_err_t c_SeqSearchST_Delete(c_SeqSearchST_t* self, const void* key) {
|
||||
if (!self || !key) return C_ERR_PARAM;
|
||||
if (self->size == 0) return C_ERR_EMPTY;
|
||||
|
||||
c_SeqSearchSTNode* prev = NULL;
|
||||
c_SeqSearchSTNode* curr = self->first;
|
||||
|
||||
while (curr != NULL) {
|
||||
if (self->key_cmp(key, curr->key, self->args) == 0) {
|
||||
if (prev == NULL) {
|
||||
self->first = curr->next;
|
||||
} else {
|
||||
prev->next = curr->next;
|
||||
}
|
||||
|
||||
// 原路回收释放单节点下深开辟的所有子内存块
|
||||
c_Allocator_Free(&self->allocator, curr->key);
|
||||
c_Allocator_Free(&self->allocator, curr->val);
|
||||
c_Allocator_Free(&self->allocator, curr);
|
||||
|
||||
self->size--;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
return C_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 反初始化:深度级联释放符号表内的所有节点、键和值的全部堆空间
|
||||
*/
|
||||
void c_SeqSearchST_Destroy(c_SeqSearchST_t* self) {
|
||||
if (!self) return;
|
||||
|
||||
c_SeqSearchSTNode* curr = self->first;
|
||||
while (curr != NULL) {
|
||||
c_SeqSearchSTNode* next_tmp = curr->next;
|
||||
|
||||
c_Allocator_Free(&self->allocator, curr->key);
|
||||
c_Allocator_Free(&self->allocator, curr->val);
|
||||
c_Allocator_Free(&self->allocator, curr);
|
||||
|
||||
curr = next_tmp;
|
||||
}
|
||||
|
||||
self->first = NULL;
|
||||
self->size = 0;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef INCLUDED_C_SEQSEARCHST_H
|
||||
#define INCLUDED_C_SEQSEARCHST_H
|
||||
|
||||
#ifndef INCLUDED_C_SORTCOMPARE_H
|
||||
#include <c_SortCompare.h>
|
||||
#endif /*INCLUDED_C_SORTCOMPARE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct c_SeqSearchSTNode {
|
||||
void* key; // 独立分配存储的 Key 物理地址
|
||||
void* val; // 独立分配存储的 Value 物理地址
|
||||
struct c_SeqSearchSTNode* next; // 指向下一个符号节点的指针
|
||||
} c_SeqSearchSTNode;
|
||||
|
||||
typedef struct {
|
||||
c_SeqSearchSTNode* first; // 链表头节点指针
|
||||
c_size_t size; // 当前符号表内有效驻留的键值对总个数
|
||||
c_size_t key_size; // 键对象占用的物理字节大小 (sizeof)
|
||||
c_size_t val_size; // 值对象占用的物理字节大小 (sizeof)
|
||||
c_SortCompare_t key_cmp; // 键值专用的动态回调比对器
|
||||
void* args; // 自定义上下文参数指针
|
||||
c_Allocator_t allocator; // 内联组合分配器实例与默认 Fallback 缺省机制
|
||||
} c_SeqSearchST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_SeqSearchST_Init(c_SeqSearchST_t* self, c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator);
|
||||
|
||||
c_err_t c_SeqSearchST_Put(c_SeqSearchST_t* self, const void* key, const void* val);
|
||||
|
||||
c_err_t c_SeqSearchST_Get(const c_SeqSearchST_t* self, const void* key, void* out_val);
|
||||
|
||||
bool c_SeqSearchST_Contains(const c_SeqSearchST_t* self, const void* key);
|
||||
|
||||
c_err_t c_SeqSearchST_Delete(c_SeqSearchST_t* self, const void* key);
|
||||
|
||||
void c_SeqSearchST_Destroy(c_SeqSearchST_t* self);
|
||||
|
||||
#endif /*INCLUDED_C_SEQSEARCHST_H*/
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "c_SeqSearchST.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "c_Test.h"
|
||||
|
||||
static int st_compare_chars(const void* a, const void* b, void* args) {
|
||||
(void)args;
|
||||
char char1 = *(const char*)a;
|
||||
char char2 = *(const char*)b;
|
||||
return char1 - char2;
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_SeqSearchST_CRUD_Flow) {
|
||||
c_SeqSearchST_t st;
|
||||
c_err_t err = c_SeqSearchST_Init(&st, sizeof(char), sizeof(int), st_compare_chars, NULL, &c_DefaultAllocator);
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
|
||||
char key_S = 'S'; int val_S = 100;
|
||||
char key_E = 'E'; int val_E = 200;
|
||||
char key_A = 'A'; int val_A = 300;
|
||||
|
||||
// 1. Put 添加行为断言
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SeqSearchST_Put(&st, &key_S, &val_S));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SeqSearchST_Put(&st, &key_E, &val_E));
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SeqSearchST_Put(&st, &key_A, &val_A));
|
||||
ASSERT_INT_EQ(3, (int)st.size);
|
||||
|
||||
// 包含性状态核验
|
||||
ASSERT_TRUE(c_SeqSearchST_Contains(&st, &key_E));
|
||||
char key_X = 'X';
|
||||
ASSERT_TRUE(!c_SeqSearchST_Contains(&st, &key_X));
|
||||
|
||||
// 2. Get 读取断言
|
||||
int get_result = 0;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SeqSearchST_Get(&st, &key_E, &get_result));
|
||||
ASSERT_INT_EQ(200, get_result);
|
||||
|
||||
// 3. Put 覆写更新(Overwrite)断言
|
||||
int update_val_E = 999;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SeqSearchST_Put(&st, &key_E, &update_val_E));
|
||||
ASSERT_INT_EQ(3, (int)st.size);
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SeqSearchST_Get(&st, &key_E, &get_result));
|
||||
ASSERT_INT_EQ(999, get_result);
|
||||
|
||||
// 4. Delete 节点移出断言
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SeqSearchST_Delete(&st, &key_E));
|
||||
ASSERT_INT_EQ(2, (int)st.size);
|
||||
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_SeqSearchST_Get(&st, &key_E, &get_result));
|
||||
|
||||
c_SeqSearchST_Destroy(&st);
|
||||
}
|
||||
|
||||
TEST_CASE(test_c_SeqSearchST_ParamDefenses) {
|
||||
c_SeqSearchST_t local_st;
|
||||
c_SeqSearchST_Init(&local_st, sizeof(char), sizeof(int), st_compare_chars, NULL, NULL);
|
||||
|
||||
char k = 'W';
|
||||
int v = 88;
|
||||
// 5. 验证拦截线
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_SeqSearchST_Init(NULL, sizeof(char), sizeof(int), st_compare_chars, NULL, NULL));
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_SeqSearchST_Put(NULL, &k, &v));
|
||||
ASSERT_INT_EQ(C_ERR_EMPTY, c_SeqSearchST_Delete(&local_st, &k));
|
||||
|
||||
c_SeqSearchST_Destroy(&local_st);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 5. 主集成入口
|
||||
// ==========================================
|
||||
int main(void) {
|
||||
TEST_START(C_SeqSearchST_Isolated_TestSuite);
|
||||
RUN_TEST(test_c_SeqSearchST_CRUD_Flow);
|
||||
RUN_TEST(test_c_SeqSearchST_ParamDefenses);
|
||||
TEST_REPORT();
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <c_StrHashOps.h>
|
||||
|
||||
#include "c_Allocator.h"
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
uint32_t hash_fmix32(uint32_t h) {
|
||||
h ^= h >> 16;
|
||||
h *= 0x3243f6a9U;
|
||||
h ^= h >> 16;
|
||||
return h;
|
||||
}
|
||||
|
||||
// Hashing: Using djb2 for string data
|
||||
uint32_t c_StrHashOps_Hash(const void *data, void *arg) {
|
||||
uint32_t hash = 5381;
|
||||
const char *str = (const char*) data;
|
||||
char c;
|
||||
while((c = *str++)) {
|
||||
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||
}
|
||||
return hash_fmix32(hash);
|
||||
}
|
||||
|
||||
|
||||
// Deep Copy: Duplicating the string in memory
|
||||
void* c_StrHashOps_Cp(const void *data, void *arg) {
|
||||
if (!data || !arg) {
|
||||
return NULL;
|
||||
}
|
||||
c_Allocator_t* allocator = arg;
|
||||
const char *input = (const char*) data;
|
||||
c_size_t len = strlen(input);
|
||||
c_size_t size = len + 1;
|
||||
char *result = c_Allocator_Alloc(allocator, size);
|
||||
if (!result) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(result, input, len);
|
||||
result[len] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Equality: Comparing string contents
|
||||
bool c_StrHashOps_Eq(const void *data1, const void *data2, void *arg) {
|
||||
return strcmp((const char*)data1, (const char*)data2) == 0;
|
||||
}
|
||||
|
||||
// Memory Cleanup
|
||||
void c_StrHashOps_Free(void *data, void *arg) {
|
||||
if (!data || !arg) {
|
||||
return;
|
||||
}
|
||||
c_Allocator_t* allocator = arg;
|
||||
c_Allocator_Free(allocator, data);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_HashKeyOps_t c_StrKeyOps={.hash = c_StrHashOps_Hash, .cp = c_StrHashOps_Cp, .free = c_StrHashOps_Free, .eq = c_StrHashOps_Eq, .arg = &c_DefaultAllocator};
|
||||
|
||||
c_HashValOps_t c_StrValOps={.cp = c_StrHashOps_Cp, .free = c_StrHashOps_Free, .eq = c_StrHashOps_Eq, .arg = &c_DefaultAllocator};
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef INCLUDED_C_STRHASHOPS_H
|
||||
#define INCLUDED_C_STRHASHOPS_H
|
||||
|
||||
#ifndef INCLUDED_C_HASHST_H
|
||||
#include <c_HashST.h>
|
||||
#endif /*INCLUDED_C_HASHST_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
uint32_t c_StrHashOps_Hash(const void *data, void *arg);
|
||||
|
||||
void* c_StrHashOps_Cp(const void *data, void *arg);
|
||||
|
||||
bool c_StrHashOps_Eq(const void *data1, const void *data2, void *arg);
|
||||
|
||||
void c_StrHashOps_Free(void *data, void *arg);
|
||||
|
||||
extern c_HashKeyOps_t c_StrKeyOps;
|
||||
extern c_HashValOps_t c_StrValOps;
|
||||
|
||||
#endif /*INCLUDED_C_STRHASHOPS_H*/
|
||||
Reference in New Issue
Block a user