String
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user