Search / Sort

This commit is contained in:
2026-08-30 22:24:45 +08:00
parent 3a4717a9d8
commit 0cb98557da
75 changed files with 7570 additions and 7 deletions
+215
View File
@@ -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;
}
}
+51
View File
@@ -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*/
+88
View File
@@ -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 作为 Keyint 作为 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);
}
+192
View File
@@ -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;
}
}
+43
View File
@@ -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*/
+86
View File
@@ -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;
}
+149
View File
@@ -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;
}
+38
View File
@@ -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*/
+80
View File
@@ -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);
}
+240
View File
@@ -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);
}
}
}
+67
View File
@@ -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*/
+216
View File
@@ -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;
}
+288
View File
@@ -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;
}
}
+55
View File
@@ -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*/
+68
View File
@@ -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);
}
+402
View File
@@ -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;
}
+54
View File
@@ -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*/
+129
View File
@@ -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);
}
+149
View File
@@ -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;
}
+47
View File
@@ -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*/
+76
View File
@@ -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;
}
+63
View File
@@ -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};
+23
View File
@@ -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*/