This commit is contained in:
2026-08-31 22:49:42 +08:00
parent 109e8af3d5
commit 4e5ae52e54
35 changed files with 3606 additions and 22 deletions
+412
View File
@@ -0,0 +1,412 @@
#include <c_TrieST.h>
#include <c_StringBuffer.h>
/**
* @brief 内部私有:安全开辟并清空一个全新的 Trie 物理节点
*/
C_STATIC_FORCE_INLINE
c_TrieNode* c_TrieST_CreateNode(c_Allocator_t* alloc) {
c_TrieNode* node = (c_TrieNode*)c_Allocator_Alloc(alloc, sizeof(c_TrieNode));
if (!node) return NULL;
node->val = NULL;
memset(node->next, 0, sizeof(node->next)); // 将 256 路子链接全部初始化填充为干净的 NULL
return node;
}
/**
* @brief 就地初始化多路单词查找树符号表
*
* @param val_cp 多态值拷贝函数指针。若传入 NULL,则自动对 Value 降级执行原始物理地址浅拷贝托管
* @param val_free 多态值销毁函数指针。若 Value 属于扁平内置类型无须释放,可传入 NULL
*/
c_err_t c_TrieST_Init(c_TrieST_t* self,
void* (*val_cp)(const void*, void*),
void (*val_free)(void*, void*),
void* val_arg,
c_Allocator_t* allocator)
{
if (!self) {
return C_ERR_PARAM;
}
if (allocator) {
self->allocator = *allocator;
} else {
self->allocator = c_DefaultAllocator;
}
self->root = NULL;
self->size = 0;
self->val_cp = val_cp;
self->val_free = val_free;
self->val_arg = val_arg;
return C_ERR_OK;
}
/**
* @brief 内部递归插入状态机
*/
static c_err_t c_TrieST_InternalPut(c_TrieST_t* self, c_TrieNode** node_ptr, const char* key, c_size_t d, const void* val, bool* is_new_key) {
// 递归基 A:若沿途下探到的物理链接为空,在此处延迟惰性加载开辟新控制头
if (*node_ptr == NULL) {
*node_ptr = c_TrieST_CreateNode(&self->allocator);
if (*node_ptr == NULL) return C_ERR_NOMEM;
}
c_TrieNode* curr = *node_ptr;
// 递归基 B:已经完美推进匹配到了变长键的物理末尾截止字符
if (key[d] == '\0') {
if (curr->val == NULL) {
*is_new_key = true; // 确定属于全新插入而非覆写
} else {
*is_new_key = false; // 属于相同键更新覆写语义
if (self->val_free) {
self->val_free(curr->val, self->val_arg); // 覆写前先摧毁老对象堆空间
}
}
// 执行值转储
if (self->val_cp) {
curr->val = self->val_cp(val, self->val_arg);
} else {
curr->val = (void*)val; // 浅拷贝放行
}
return C_ERR_OK;
}
// 多路高速变轨寻址:利用当前的无符号字符物理代码直接作为 256 维数组下标切入
unsigned char c = (unsigned char)key[d];
return c_TrieST_InternalPut(self, &(curr->next[c]), key, d + 1, val, is_new_key);
}
/**
* @brief 存入键值对(时间复杂度仅取决于变长键长 O(W),与总键数完全独立解耦)
*
* @param key 以 '\0' 结尾的标准变长字符串键
* @param val 指向待注入 Value 对象的指针
*/
c_err_t c_TrieST_Put(c_TrieST_t* self, const char* key, const void* val) {
if (!self || !key || !val) return C_ERR_PARAM;
bool is_new = false;
c_err_t err = c_TrieST_InternalPut(self, &(self->root), key, 0, val, &is_new);
if (err == C_ERR_OK && is_new) {
self->size++; // 只有在全新键插入时才增加全局计数
}
return err;
}
/**
* @brief 内部下探滑窗辅助:根据指定字符串前缀寻找对应的 Trie 树局部根节点
*/
static c_TrieNode* c_TrieST_SubtreeSearch(c_TrieNode* x, const char* key, c_size_t d) {
if (x == NULL) return NULL;
if (key[d] == '\0') return x; // 前缀完全匹配命中,给回当前局部根指针
unsigned char c = (unsigned char)key[d];
return c_TrieST_SubtreeSearch(x->next[c], key, d + 1);
}
/**
* @brief 依据指定字符串键,精准提取读取其关联多态 Value 的原生物理堆地址
*
* @return void* 找到时返回该值的原生指针;未找到或表为空时返回 NULL
*/
void* c_TrieST_Get(const c_TrieST_t* self, const char* key) {
if (!self || !key || self->size == 0) return NULL;
c_TrieNode* x = c_TrieST_SubtreeSearch(self->root, key, 0);
if (x == NULL) return NULL;
return x->val; // 🌟【重要约束】:必须返回 val,若 val 为 NULL 说明虽然路径前缀存在但并非完整键
}
/**
* @brief 检查符号表内是否有效包含指定的字符串键
*/
bool c_TrieST_Contains(const c_TrieST_t* self, const char* key) {
return c_TrieST_Get(self, key) != NULL;
}
/**
* @brief 内部私有:安全走查某个 Trie 节点当前是否彻底沦为空仓空节点
*/
C_STATIC_FORCE_INLINE
bool c_TrieST_HasChildren(const c_TrieNode* node) {
for (int r = 0; r < C_TRIE_R; r++) {
if (node->next[r] != NULL) return true; // 只要 256 路中有任一指针不为空,说明包含子代分支
}
return false;
}
/**
* @brief 内部级联递归惰性剪枝删除状态机
*/
static c_TrieNode* c_TrieST_InternalDelete(c_TrieST_t* self, c_TrieNode* x, const char* key, c_size_t d, c_err_t* out_err) {
if (x == NULL) {
*out_err = C_ERR_NOTFOUND;
return NULL;
}
if (key[d] == '\0') {
if (x->val != NULL) {
// 精确命中要斩断的目标完整键:调用用户自备析构函数原路瓦解清除 Value 堆空间
if (self->val_free) {
self->val_free(x->val, self->val_arg);
}
x->val = NULL;
self->size--; // 递减总容量计数
*out_err = C_ERR_OK;
} else {
*out_err = C_ERR_NOTFOUND; // 路径虽通,但该位置并无有效完整值键
}
} else {
unsigned char c = (unsigned char)key[d];
// 递归向下游层级探查推进
x->next[c] = c_TrieST_InternalDelete(self, x->next[c], key, d + 1, out_err);
}
// 🌟🌟🌟【自底向上绝对安全惰性剪枝防线(Lazy Pruning Countermeasure)】🌟🌟🌟
// 退栈回溯时,如果发现当前节点的值已经被抹除置空 (val == NULL)
if (x->val == NULL) {
// 且进一步走查发现它下方的 256 路子代指针也已全部被断开变空 (无子孙驻留)
if (!c_TrieST_HasChildren(x)) {
// 说明此节点已彻底丧失路由及存储价值,果断对其执行物理垃圾回收,并向父层级返回 NULL 断开挂载!
c_Allocator_Free(&self->allocator, x);
return NULL;
}
}
return x; // 沿途返回自身指针,维持上层树拓扑形态不破裂
}
/**
* @brief 根据指定变长字符串键彻底从树中斩断移除该符号,并自动自适应逆向触发级联剪枝释放
*/
c_err_t c_TrieST_Delete(c_TrieST_t* self, const char* key) {
if (!self || !key) return C_ERR_PARAM;
if (self->size == 0) return C_ERR_EMPTY;
c_err_t err = C_ERR_NOTFOUND;
self->root = c_TrieST_InternalDelete(self, self->root, key, 0, &err);
return err;
}
/**
* @brief 内部后序深度递归遍历解构辅助
*/
static void c_TrieST_InternalDestroy(c_Allocator_t* alloc, c_TrieNode* x,
void (*val_free)(void*, void*), void* val_arg)
{
if (x == NULL) return;
// 后序递归:先将 256 路子代网络彻底瓦解,最后再收拢解构自身
for (int r = 0; r < C_TRIE_R; r++) {
c_TrieST_InternalDestroy(alloc, x->next[r], val_free, val_arg);
}
if (x->val && val_free) {
val_free(x->val, val_arg);
}
c_Allocator_Free(alloc, x);
}
/**
* @brief 符号表注销解构:全多路节点级联物理洗刷清除
*/
void c_TrieST_Destroy(c_TrieST_t* self) {
if (self && self->root) {
c_TrieST_InternalDestroy(&self->allocator, self->root, self->val_free, self->val_arg);
self->root = NULL;
self->size = 0;
}
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/**
* @brief 内部私有:下探滑窗寻找特定前缀所在的局部子树根节点
*/
static c_TrieNode* c_TrieST_FindSubtreeRoot(c_TrieNode* x, const char* prefix, c_size_t d) {
if (x == NULL) return NULL;
if (prefix[d] == '\0') return x;
unsigned char c = (unsigned char)prefix[d];
return c_TrieST_FindSubtreeRoot(x->next[c], prefix, d + 1);
}
/**
* @brief 内部DFS辅助状态机:供 KeysWithPrefix 前缀联想收集路径使用
*/
static c_err_t c_TrieST_CollectPrefixDFS(c_TrieNode* x, c_StringBuffer_t* path_buf, c_StringList_t* results) {
if (x == NULL) return C_ERR_OK;
// 1. 若当前路径恰好构成一个有效独立键,从 StringBuffer 提取零拷贝只读 CStr 镜像,通过 StringList 值拷贝深拷贝压出
if (x->val != NULL) {
c_err_t err = c_StringList_Add(results, c_StringBuffer_CStr(path_buf));
if (err != C_ERR_OK) return err;
}
// 2. 单调向全 256 路子代网络执行就地滑窗探测
for (int r = 0; r < C_TRIE_R; r++) {
if (x->next[r] != NULL) {
// 🌟就地前推:将当前分支字符压入自适应缓冲区,零堆碎片开销
c_err_t err = c_StringBuffer_AppendChar(path_buf, (char)r);
if (err != C_ERR_OK) return err;
err = c_TrieST_CollectPrefixDFS(x->next[r], path_buf, results);
if (err != C_ERR_OK) return err;
// 🌟就地回溯:探查完当前分支后,执行 PopBack 弹栈抹除,恢复当前层缓冲区形态,物理杜绝内存污染
c_StringBuffer_RemoveAt(path_buf, path_buf->size-1, 1);
}
}
return C_ERR_OK;
}
/**
* @brief 🌟 高阶总线 API 1:前缀模糊联想匹配检索
*/
c_err_t c_TrieST_KeysWithPrefix(c_TrieST_t* self, const char* prefix, c_StringList_t* results) {
if (!self || !prefix || !results) return C_ERR_PARAM;
if (self->size == 0 || self->root == NULL) return C_ERR_OK;
// 下探锁定局部子树根位置
c_TrieNode* subtree_root = c_TrieST_FindSubtreeRoot(self->root, prefix, 0);
if (subtree_root == NULL) return C_ERR_OK;
// 初始化独立的弹性路径缓冲区,前置装填好已匹配的公共前缀
c_StringBuffer_t path_buffer;
c_size_t prefix_len = strlen(prefix);
c_err_t err = c_StringBuffer_Init(&path_buffer, prefix_len + 16, &self->allocator);
if (err != C_ERR_OK) return err;
// for (c_size_t i = 0; i < prefix_len; i++) {
// c_StringBuffer_AppendChar(&path_buffer, prefix[i]);
// }
c_StringBuffer_Append(&path_buffer, prefix, prefix_len);
// 启动多路并行 DFS 状态机
err = c_TrieST_CollectPrefixDFS(subtree_root, &path_buffer, results);
c_StringBuffer_Destroy(&path_buffer); // 原子回收局部弹性路径链,常数级空间控制
return err;
}
/* ------------------------------------------------------------------------------------------------------------------ */
static c_err_t c_TrieST_MatchPatternRecursive(c_TrieNode* x, const char* pattern, c_size_t d, c_StringBuffer_t* path_buf,
c_StringList_t* results)
{
if (x == NULL) return C_ERR_OK;
char current_pat = pattern[d];
// 递归基:模式串已步进到末尾截止位置
if (current_pat == '\0') {
if (x->val != NULL) {
c_err_t err = c_StringList_Add(results, c_StringBuffer_CStr(path_buf));
if (err != C_ERR_OK) return err;
}
return C_ERR_OK;
}
// 🌟🌟🌟【通配符万能字符信道辐射状态机】🌟🌟🌟
if (current_pat == '.') {
for (int r = 0; r < C_TRIE_R; r++) {
if (x->next[r] != NULL) {
c_StringBuffer_AppendChar(path_buf, (char)r); // 就地前推
c_err_t err = c_TrieST_MatchPatternRecursive(x->next[r], pattern, d + 1, path_buf, results);
if (err != C_ERR_OK) {
c_StringBuffer_PopBack(path_buf);
return err;
}
c_StringBuffer_PopBack(path_buf); // 就地回溯恢复
}
}
} else {
// 精确字符 O(1) 二级单路快速寻址
unsigned char c = (unsigned char)current_pat;
if (x->next[c] != NULL) {
c_StringBuffer_AppendChar(path_buf, (char)c);
c_err_t err = c_TrieST_MatchPatternRecursive(x->next[c], pattern, d + 1, path_buf, results);
if (err != C_ERR_OK) {
c_StringBuffer_PopBack(path_buf);
return err;
}
c_StringBuffer_PopBack(path_buf);
}
}
return C_ERR_OK;
}
/**
* @brief 🌟 高阶总线 API 2:通配符正则全词模糊匹配接口(StringBuffer 加固版)
*/
c_err_t c_TrieST_KeysThatMatch(c_TrieST_t* self, const char* pattern, c_StringList_t* results) {
if (!self || !pattern || !results) return C_ERR_PARAM;
if (self->size == 0 || self->root == NULL) return C_ERR_OK;
c_StringBuffer_t path_buffer;
c_err_t err = c_StringBuffer_Init(&path_buffer, strlen(pattern) + 8, &self->allocator);
if (err != C_ERR_OK) return err;
err = c_TrieST_MatchPatternRecursive(self->root, pattern, 0, &path_buffer, results);
c_StringBuffer_Destroy(&path_buffer);
return err;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/**
* @brief 内部寻阻辅助:供 LongestPrefixOf 深度爬坡状态机使用
*/
static void c_TrieST_FindLongestPrefixLength(c_TrieNode* x, const char* query, c_size_t d, c_size_t* out_longest_len) {
if (x == NULL) return;
if (x->val != NULL) {
*out_longest_len = d; // 沿途更新最大有效掩码位宽
}
if (query[d] == '\0') return;
unsigned char c = (unsigned char)query[d];
c_TrieST_FindLongestPrefixLength(x->next[c], query, d + 1, out_longest_len);
}
/**
* @brief 🌟 高阶总线 API 3:最长前缀分级掩码路由寻址(StringBuffer 加固版)
*/
c_err_t c_TrieST_LongestPrefixOf(c_TrieST_t* self, const char* query, c_StringList_t* results) {
if (!self || !query || !results) return C_ERR_PARAM;
if (self->size == 0 || self->root == NULL) return C_ERR_OK;
c_size_t longest_match_length = 0;
c_TrieST_FindLongestPrefixLength(self->root, query, 0, &longest_match_length);
if (longest_match_length == 0 && self->root->val == NULL) {
return C_ERR_NOTFOUND;
}
// 利用 StringBuffer 直接截断装填该寻址段
c_StringBuffer_t path_buffer;
c_err_t err = c_StringBuffer_Init(&path_buffer, longest_match_length + 4, &self->allocator);
if (err != C_ERR_OK) return err;
// for (c_size_t i = 0; i < longest_match_length; i++) {
// c_StringBuffer_AppendChar(&path_buffer, query[i]);
// }
c_StringBuffer_Append(&path_buffer, query, longest_match_length);
err = c_StringList_Add(results, c_StringBuffer_CStr(&path_buffer));
c_StringBuffer_Destroy(&path_buffer);
return err;
}