From a0758766c5287e8b2b9722d908acd4f131db4d25 Mon Sep 17 00:00:00 2001 From: Chen Peng Date: Sat, 29 Aug 2026 01:58:00 +0800 Subject: [PATCH] Search/Sort --- Search/c_BST.c | 158 +++++++++++++ Search/c_BST.h | 40 ++++ Search/c_BinarySearch.c | 1 + Search/c_BinarySearch.h | 49 ++++ Search/c_BinarySearchST.c | 168 ++++++++++++++ Search/c_BinarySearchST.h | 35 +++ Search/c_HashMap.c | 269 ++++++++++++++++++++++ Search/c_HashMap.h | 59 +++++ Search/c_HashSet.c | 45 ++++ Search/c_HashSet.h | 36 +++ Search/c_LinearProbingHashST.c | 196 ++++++++++++++++ Search/c_LinearProbingHashST.h | 40 ++++ Search/c_RedBlackBST.c | 154 +++++++++++++ Search/c_RedBlackBST.h | 66 ++++++ Search/c_SeparateChainingHashST.c | 308 +++++++++++++++++++++++++ Search/c_SeparateChainingHashST.h | 78 +++++++ Search/c_TST.c | 312 +++++++++++++++++++++++++ Search/c_TST.h | 77 +++++++ Search/c_TreeMap.c | 365 ++++++++++++++++++++++++++++++ Search/c_TreeMap.h | 82 +++++++ Search/c_TreeSet.c | 347 ++++++++++++++++++++++++++++ Search/c_TreeSet.h | 80 +++++++ Search/c_Trie.c | 320 ++++++++++++++++++++++++++ Search/c_Trie.h | 82 +++++++ Sort/c_BinaryInsertionSort.c | 1 + Sort/c_BinaryInsertionSort.h | 79 +++++++ Sort/c_HeapSort.c | 2 + Sort/c_HeapSort.h | 103 +++++++++ Sort/c_InPlaceMSDRadixSort.c | 105 +++++++++ Sort/c_InPlaceMSDRadixSort.h | 20 ++ Sort/c_IndexMaxPQ.c | 184 +++++++++++++++ Sort/c_IndexMaxPQ.h | 41 ++++ Sort/c_IndexMinPQ.c | 192 ++++++++++++++++ Sort/c_IndexMinPQ.h | 42 ++++ Sort/c_InsertionSort.c | 1 + Sort/c_InsertionSort.h | 68 ++++++ Sort/c_LSDRadixSort.c | 62 +++++ Sort/c_LSDRadixSort.h | 16 ++ Sort/c_MSDRadixSort.c | 99 ++++++++ Sort/c_MSDRadixSort.h | 17 ++ Sort/c_MaxPQ.c | 208 +++++++++++++++++ Sort/c_MaxPQ.h | 34 +++ Sort/c_MergeSort.c | 54 +++++ Sort/c_MergeSort.h | 54 +++++ Sort/c_MergeSortBU.c | 1 + Sort/c_MergeSortBU.h | 98 ++++++++ Sort/c_MinPQ.c | 160 +++++++++++++ Sort/c_MinPQ.h | 34 +++ Sort/c_QuickSort.c | 84 +++++++ Sort/c_QuickSort.h | 56 +++++ Sort/c_QuickSortIterative.c | 1 + Sort/c_QuickSortIterative.h | 102 +++++++++ Sort/c_SelectionSort.c | 1 + Sort/c_SelectionSort.h | 67 ++++++ Sort/c_ShellSort.c | 1 + Sort/c_ShellSort.h | 71 ++++++ 56 files changed, 5425 insertions(+) create mode 100644 Search/c_BST.c create mode 100644 Search/c_BST.h create mode 100644 Search/c_BinarySearch.c create mode 100644 Search/c_BinarySearch.h create mode 100644 Search/c_BinarySearchST.c create mode 100644 Search/c_BinarySearchST.h create mode 100644 Search/c_HashMap.c create mode 100644 Search/c_HashMap.h create mode 100644 Search/c_HashSet.c create mode 100644 Search/c_HashSet.h create mode 100644 Search/c_LinearProbingHashST.c create mode 100644 Search/c_LinearProbingHashST.h create mode 100644 Search/c_RedBlackBST.c create mode 100644 Search/c_RedBlackBST.h create mode 100644 Search/c_SeparateChainingHashST.c create mode 100644 Search/c_SeparateChainingHashST.h create mode 100644 Search/c_TST.c create mode 100644 Search/c_TST.h create mode 100644 Search/c_TreeMap.c create mode 100644 Search/c_TreeMap.h create mode 100644 Search/c_TreeSet.c create mode 100644 Search/c_TreeSet.h create mode 100644 Search/c_Trie.c create mode 100644 Search/c_Trie.h create mode 100644 Sort/c_BinaryInsertionSort.c create mode 100644 Sort/c_BinaryInsertionSort.h create mode 100644 Sort/c_HeapSort.c create mode 100644 Sort/c_HeapSort.h create mode 100644 Sort/c_InPlaceMSDRadixSort.c create mode 100644 Sort/c_InPlaceMSDRadixSort.h create mode 100644 Sort/c_IndexMaxPQ.c create mode 100644 Sort/c_IndexMaxPQ.h create mode 100644 Sort/c_IndexMinPQ.c create mode 100644 Sort/c_IndexMinPQ.h create mode 100644 Sort/c_InsertionSort.c create mode 100644 Sort/c_InsertionSort.h create mode 100644 Sort/c_LSDRadixSort.c create mode 100644 Sort/c_LSDRadixSort.h create mode 100644 Sort/c_MSDRadixSort.c create mode 100644 Sort/c_MSDRadixSort.h create mode 100644 Sort/c_MaxPQ.c create mode 100644 Sort/c_MaxPQ.h create mode 100644 Sort/c_MergeSort.c create mode 100644 Sort/c_MergeSort.h create mode 100644 Sort/c_MergeSortBU.c create mode 100644 Sort/c_MergeSortBU.h create mode 100644 Sort/c_MinPQ.c create mode 100644 Sort/c_MinPQ.h create mode 100644 Sort/c_QuickSort.c create mode 100644 Sort/c_QuickSort.h create mode 100644 Sort/c_QuickSortIterative.c create mode 100644 Sort/c_QuickSortIterative.h create mode 100644 Sort/c_SelectionSort.c create mode 100644 Sort/c_SelectionSort.h create mode 100644 Sort/c_ShellSort.c create mode 100644 Sort/c_ShellSort.h diff --git a/Search/c_BST.c b/Search/c_BST.c new file mode 100644 index 0000000..a16afad --- /dev/null +++ b/Search/c_BST.c @@ -0,0 +1,158 @@ +#include +#include + +/** + * Helper accessors to safely locate key and value buffers within a generic node allocation block. + */ +C_STATIC_FORCE_INLINE void* c_BST_NodeKey(c_BSTNode_t* node) { + return (void*)((char*)node + sizeof(c_BSTNode_t)); +} + +C_STATIC_FORCE_INLINE void* c_BST_NodeVal(c_BSTNode_t* node, c_size_t key_size) { + return (void*)((char*)node + sizeof(c_BSTNode_t) + key_size); +} + +/** + * Creates and initializes a standalone tree node layout. + */ +C_STATIC_FORCE_INLINE +c_BSTNode_t* c_BST_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) { + c_BSTNode_t* node = (c_BSTNode_t*)C_ALLOC(sizeof(c_BSTNode_t) + ks + vs); + if (node == NULL) return NULL; + + node->left = NULL; + node->right = NULL; + memcpy(c_BST_NodeKey(node), key, ks); + memcpy(c_BST_NodeVal(node, ks), val, vs); + return node; +} + +/** + * Internal recursive post-order destructor helper. + */ +static void c_BST_DestroyNodes(c_BSTNode_t* node) { + if (node == NULL) return; + c_BST_DestroyNodes(node->left); + c_BST_DestroyNodes(node->right); + C_FREE(node); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_BST_Init(c_BST_t* tree, c_size_t key_size, c_size_t val_size, int (*compar)(const void*, const void*)) { + if (tree == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM; + tree->root = NULL; + tree->key_size = key_size; + tree->val_size = val_size; + tree->size = 0; + tree->compar = compar; + return C_ERR_OK; +} + +void c_BST_Destroy(c_BST_t* tree) { + if (tree) { + c_BST_DestroyNodes(tree->root); + tree->root = NULL; + tree->size = 0; + } +} + +c_err_t c_BST_Clear(c_BST_t* tree) { + if (tree == NULL) return C_ERR_PARAM; + c_BST_DestroyNodes(tree->root); + tree->root = NULL; + tree->size = 0; + return C_ERR_OK; +} + +c_bool_t c_BST_Contains(const c_BST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return C_FALSE; + c_BSTNode_t* curr = tree->root; + while (curr != NULL) { + int cmp = tree->compar(key, c_BST_NodeKey(curr)); + if (cmp == 0) return C_TRUE; + curr = (cmp < 0) ? curr->left : curr->right; + } + return C_FALSE; +} + +c_err_t c_BST_Put(c_BST_t* tree, const void* key, const void* val) { + if (tree == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + c_BSTNode_t** link = &tree->root; + c_BSTNode_t* curr = tree->root; + + while (curr != NULL) { + int cmp = tree->compar(key, c_BST_NodeKey(curr)); + if (cmp == 0) { + // Overwrite existing value for matching symbol key + memcpy(c_BST_NodeVal(curr, tree->key_size), val, tree->val_size); + return C_ERR_OK; + } + link = (cmp < 0) ? &curr->left : &curr->right; + curr = *link; + } + + // Key is unique, construct a new node configuration structure + c_BSTNode_t* new_node = c_BST_CreateNode(key, val, tree->key_size, tree->val_size); + if (new_node == NULL) return C_ERR_NOMEM; + + *link = new_node; + tree->size++; + return C_ERR_OK; +} + +void* c_BST_Get(const c_BST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return NULL; + c_BSTNode_t* curr = tree->root; + while (curr != NULL) { + int cmp = tree->compar(key, c_BST_NodeKey(curr)); + if (cmp == 0) return c_BST_NodeVal(curr, tree->key_size); + curr = (cmp < 0) ? curr->left : curr->right; + } + return NULL; +} + +c_err_t c_BST_Delete(c_BST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return C_ERR_PARAM; + + c_BSTNode_t** link = &tree->root; + c_BSTNode_t* curr = tree->root; + + while (curr != NULL) { + int cmp = tree->compar(key, c_BST_NodeKey(curr)); + if (cmp == 0) break; + link = (cmp < 0) ? &curr->left : &curr->right; + curr = *link; + } + + if (curr == NULL) return C_ERR_NOTFOUND; + + // Standard Hibbard deletion implementation sequence matching tree boundaries + if (curr->left == NULL) { + *link = curr->right; + } else if (curr->right == NULL) { + *link = curr->left; + } else { + // Node has two children; locate the successor node (smallest node in the right sub-tree) + c_BSTNode_t** succ_link = &curr->right; + c_BSTNode_t* succ = curr->right; + while (succ->left != NULL) { + succ_link = &succ->left; + succ = succ->left; + } + + // Delink the successor node from its previous position + *succ_link = succ->right; + + // Route child structures of the node being deleted into the successor + succ->left = curr->left; + succ->right = curr->right; + *link = succ; + } + + C_FREE(curr); + tree->size--; + return C_ERR_OK; +} diff --git a/Search/c_BST.h b/Search/c_BST.h new file mode 100644 index 0000000..b9cab47 --- /dev/null +++ b/Search/c_BST.h @@ -0,0 +1,40 @@ +#ifndef INCLUDED_C_BST_H +#define INCLUDED_C_BST_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Forward declaration of internal node structure +typedef struct c_BSTNode { + struct c_BSTNode* left; // Pointer to left child + struct c_BSTNode* right; // Pointer to right child + // Node payload layout: key block followed immediately by the value block in memory +} c_BSTNode_t; + +// Binary Search Tree Context Structure +typedef struct { + c_BSTNode_t* root; // Root node pointer + c_size_t key_size; // Size of each key in bytes + c_size_t val_size; // Size of each value in bytes + c_size_t size; // Total number of nodes in the tree + int (*compar)(const void*, const void*); // Key comparison rule pointer +} c_BST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_BST_Init(c_BST_t* tree, c_size_t key_size, c_size_t val_size, int (*compar)(const void*, const void*)) ; +void c_BST_Destroy(c_BST_t* tree); + +c_err_t c_BST_Clear(c_BST_t* tree); +c_bool_t c_BST_Contains(const c_BST_t* tree, const void* key); +c_err_t c_BST_Put(c_BST_t* tree, const void* key, const void* val); +void* c_BST_Get(const c_BST_t* tree, const void* key); +c_err_t c_BST_Delete(c_BST_t* tree, const void* key) ; + +#endif /*INCLUDED_C_BST_H*/ diff --git a/Search/c_BinarySearch.c b/Search/c_BinarySearch.c new file mode 100644 index 0000000..b3c7a34 --- /dev/null +++ b/Search/c_BinarySearch.c @@ -0,0 +1 @@ +#include diff --git a/Search/c_BinarySearch.h b/Search/c_BinarySearch.h new file mode 100644 index 0000000..3379f9e --- /dev/null +++ b/Search/c_BinarySearch.h @@ -0,0 +1,49 @@ +#ifndef INCLUDED_C_BINARYSEARCH_H +#define INCLUDED_C_BINARYSEARCH_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * 通用二分查找函数 + * @param key 指向要查找的目标元素的指针 + * @param base 指向待查找数组首元素的指针 + * @param num 数组中元素的个数 + * @param size 每个元素的大小(以字节为单位,使用 sizeof 获取) + * @param compar 指向比较函数的指针(由用户提供比较逻辑) + * @return 找到则返回指向该元素的指针,未找到则返回 NULL + */ +C_STATIC_FORCE_INLINE +void* c_BinarySearch(const void* key, const void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + c_size_t left = 0; + c_size_t right = num; // 使用左闭右开区间 [left, right) 逻辑更清晰 + + while (left < right) { + c_size_t mid = left + (right - left) / 2; + + // 计算 mid 元素的内存地址:首地址 + 索引 * 每个元素的字节大小 + // 先强转为 char* 是为了按单字节进行指针偏移 + const void* midElem = (const char*)base + (mid * size); + + // 调用用户自定义的比较函数 + int cmp = compar(key, midElem); + + if (cmp == 0) { + return (void*)midElem; // 找到目标,返回其在数组中的地址 + } else if (cmp > 0) { + left = mid + 1; // key 大于 midElem,往右半部分找 + } else { + right = mid; // key 小于 midElem,往左半部分找 + } + } + + return NULL; // 未找到 +} + +#endif /*INCLUDED_C_BINARYSEARCH_H*/ diff --git a/Search/c_BinarySearchST.c b/Search/c_BinarySearchST.c new file mode 100644 index 0000000..3cab316 --- /dev/null +++ b/Search/c_BinarySearchST.c @@ -0,0 +1,168 @@ +#include +#include + +/** + * Core Rank/Binary Search operation. + * Returns the exact index if the key is found, or the insertion slot index if not found. + */ +static inline c_size_t c_BSST_Rank(const c_BinarySearchST_t* st, const void* key, c_bool_t* out_found) { + c_size_t left = 0; + c_size_t right = st->size; + char* keys_base = (char*)st->keys; + c_size_t ks = st->key_size; + + while (left < right) { + c_size_t mid = left + (right - left) / 2; + int cmp = st->compar(key, keys_base + (mid * ks)); + + if (cmp == 0) { + if (out_found) *out_found = C_TRUE; + return mid; + } else if (cmp > 0) { + left = mid + 1; + } else { + right = mid; + } + } + + if (out_found) *out_found = C_FALSE; + return left; // 'left' represents the precise index where the key *should* go +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* st, c_size_t initial_capacity, + c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)) { + if (st == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM; + + st->capacity = (initial_capacity > 0) ? initial_capacity : 4; + st->key_size = key_size; + st->val_size = val_size; + st->size = 0; + st->compar = compar; + + st->keys = C_ALLOC(st->capacity * key_size); + st->vals = C_ALLOC(st->capacity * val_size); + + if (st->keys == NULL || st->vals == NULL) { + C_FREE(st->keys); + C_FREE(st->vals); + return C_ERR_NOMEM; + } + + return C_ERR_OK; +} + +void c_BinarySearchST_Destroy(c_BinarySearchST_t* st) { + if (st) { + C_FREE(st->keys); + C_FREE(st->vals); + st->size = 0; + st->capacity = 0; + } +} + +c_err_t c_BinarySearchST_Clear(c_BinarySearchST_t* st) { + if (st == NULL) return C_ERR_PARAM; + st->size = 0; // Soft reset clears tracking variables but keeps allocated memory blocks + return C_ERR_OK; +} + +c_bool_t c_BinarySearchST_Contains(const c_BinarySearchST_t* st, const void* key) { + if (st == NULL || key == NULL) return C_FALSE; + c_bool_t found = C_FALSE; + c_BSST_Rank(st, key, &found); + return found; +} + +c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* st, const void* key, const void* val) { + if (st == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + c_bool_t found = C_FALSE; + c_size_t idx = c_BSST_Rank(st, key, &found); + + char* keys_base = (char*)st->keys; + char* vals_base = (char*)st->vals; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + // Symbol Table Behavior: If the key already exists, overwrite the value + if (found) { + memcpy(vals_base + (idx * vs), val, vs); + return C_ERR_OK; + } + + // Dynamic parallel array capacity expansion + if (st->size >= st->capacity) { + c_size_t new_capacity = st->capacity * 2; + void* new_keys = C_ALLOC(new_capacity * ks); + void* new_vals = C_ALLOC(new_capacity * vs); + + if (new_keys == NULL || new_vals == NULL) { + C_FREE(new_keys); + C_FREE(new_vals); + return C_ERR_NOMEM; + } + + if (st->size > 0) { + memcpy(new_keys, st->keys, st->size * ks); + memcpy(new_vals, st->vals, st->size * vs); + } + + C_FREE(st->keys); C_FREE(st->vals); + st->keys = new_keys; st->vals = new_vals; + st->capacity = new_capacity; + keys_base = (char*)st->keys; + vals_base = (char*)st->vals; + } + + // Shift memory components to create a gap for insertion + if (idx < st->size) { + memmove(keys_base + ((idx + 1) * ks), keys_base + (idx * ks), (st->size - idx) * ks); + memmove(vals_base + ((idx + 1) * vs), vals_base + (idx * vs), (st->size - idx) * vs); + } + + // Drop elements directly into parallel array channels + memcpy(keys_base + (idx * ks), key, ks); + memcpy(vals_base + (idx * vs), val, vs); + st->size++; + + return C_ERR_OK; +} + +void* c_BinarySearchST_Get(const c_BinarySearchST_t* st, const void* key) { + if (st == NULL || key == NULL) return NULL; + + c_bool_t found = C_FALSE; + c_size_t idx = c_BSST_Rank(st, key, &found); + + if (found) { + return (char*)st->vals + (idx * st->val_size); + } + return NULL; +} + +c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* st, const void* key) { + if (st == NULL || key == NULL) return C_ERR_PARAM; + + c_bool_t found = C_FALSE; + c_size_t idx = c_BSST_Rank(st, key, &found); + + if (!found) return C_ERR_NOTFOUND; + + char* keys_base = (char*)st->keys; + char* vals_base = (char*)st->vals; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + // Compress parallel entries down over the deleted item slot + if (idx < st->size - 1) { + memmove(keys_base + (idx * ks), keys_base + ((idx + 1) * ks), (st->size - 1 - idx) * ks); + memmove(vals_base + (idx * vs), vals_base + ((idx + 1) * vs), (st->size - 1 - idx) * vs); + } + + st->size--; + return C_ERR_OK; +} diff --git a/Search/c_BinarySearchST.h b/Search/c_BinarySearchST.h new file mode 100644 index 0000000..8ef2593 --- /dev/null +++ b/Search/c_BinarySearchST.h @@ -0,0 +1,35 @@ +#ifndef INCLUDED_C_BINARYSEARCHST_H +#define INCLUDED_C_BINARYSEARCHST_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* keys; // Flat parallel array block storing keys + void* vals; // Flat parallel array block storing values + c_size_t key_size; // Size of each key in bytes (sizeof(Key)) + c_size_t val_size; // Size of each value in bytes (sizeof(Value)) + c_size_t capacity; // Maximum allocated element capacity + c_size_t size; // Current active entry count + int (*compar)(const void*, const void*); // Key comparison rule pointer +} c_BinarySearchST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ +c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* st, c_size_t initial_capacity, + c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)); +void c_BinarySearchST_Destroy(c_BinarySearchST_t* st); + +c_err_t c_BinarySearchST_Clear(c_BinarySearchST_t* st); +c_bool_t c_BinarySearchST_Contains(const c_BinarySearchST_t* st, const void* key); +c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* st, const void* key, const void* val); +void* c_BinarySearchST_Get(const c_BinarySearchST_t* st, const void* key); +c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* st, const void* key); + +#endif /*INCLUDED_C_BINARYSEARCHST_H*/ diff --git a/Search/c_HashMap.c b/Search/c_HashMap.c new file mode 100644 index 0000000..49b16e5 --- /dev/null +++ b/Search/c_HashMap.c @@ -0,0 +1,269 @@ +#include +#include + +#include "c_Macros.h" + +#define C_HASHMAP_LOAD_FACTOR_THRESHOLD 0.75f +#define DEFAULT_CAPACITY 16 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Internal Helper: Doubles bucket allocations and rehashes entries +static c_err_t hashmap_resize(c_HashMap_t* self) { + c_size_t new_capacity = self->capacity * 2; + c_HashMapEntry_t** new_buckets = (c_HashMapEntry_t**)C_CALLOC(new_capacity, sizeof(c_HashMapEntry_t*)); + if (!new_buckets) return C_ERR_NOMEM; + + // Migrate entries over from old buckets array + for (c_size_t i = 0; i < self->capacity; i++) { + c_HashMapEntry_t* entry = self->buckets[i]; + while (entry != NULL) { + c_HashMapEntry_t* next = entry->next; + + // Recompute new bucket index mapping constraints + uint32_t raw_hash = self->hash(entry->key, self->key_size); + c_size_t new_index = raw_hash % new_capacity; + + // Link into the new bucket array chain head + entry->next = new_buckets[new_index]; + new_buckets[new_index] = entry; + + entry = next; + } + } + + C_FREE(self->buckets); + self->buckets = new_buckets; + self->capacity = new_capacity; + return C_ERR_OK; +} + +C_STATIC_FORCE_INLINE +void hashmap_iter_advance_to_valid(c_HashMapKeyIter_t* self) { + while (self->bucket_index < self->map->capacity) { + // 如果當前桶子有鏈結節點,繫結其指標的指標 + if (self->map->buckets[self->bucket_index] != NULL) { + self->entry = &self->map->buckets[self->bucket_index]; + return; + } + self->bucket_index++; + } + // 若找不到任何有效節點,重置為 NULL 象徵迭代結束 + self->entry = NULL; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_HashMap_Init(c_HashMap_t* self, int key_size, int value_size, c_size_t initial_capacity, + c_HashMap_Hash_f hash, c_HashMap_Compare_f compare) { + if (!self || key_size <= 0 || value_size <= 0 || !hash || !compare) return C_ERR_PARAM; + + self->capacity = (initial_capacity > 0) ? initial_capacity : DEFAULT_CAPACITY; + self->size = 0; + self->key_size = key_size; + self->value_size = value_size; + self->hash = hash; + self->compare = compare; + + self->buckets = (c_HashMapEntry_t**)C_CALLOC(self->capacity, sizeof(c_HashMapEntry_t*)); + if (!self->buckets) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + return C_ERR_OK; +} + +void c_HashMap_Destroy(c_HashMap_t* self) { + if (!self) return; + + for (c_size_t i = 0; i < self->capacity; i++) { + c_HashMapEntry_t* entry = self->buckets[i]; + while (entry != NULL) { + c_HashMapEntry_t* next = entry->next; + // C_FREE(entry->key); + // C_FREE(entry->value); + C_FREE(entry); + entry = next; + } + } + C_FREE(self->buckets); + self->buckets = NULL; + self->capacity = 0; + self->size = 0; +} + + +// Maps/Overwrites keys to value entities in O(1) average time complexity +c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* value) { + if (!self || !self->buckets || !key || !value) return C_ERR_PARAM; + + // Trigger dynamic scale-out adjustments if load boundaries criteria are exceeded + if ((float)(self->size + 1) / self->capacity >= C_HASHMAP_LOAD_FACTOR_THRESHOLD) { + if (hashmap_resize(self) != C_ERR_OK) return C_ERR_NOMEM; + } + + uint32_t raw_hash = self->hash(key, self->key_size); + c_size_t index = raw_hash % self->capacity; + + // Scan the collision chain to check if the key already exists + c_HashMapEntry_t* entry = self->buckets[index]; + while (entry != NULL) { + if (self->compare(entry->key, key, self->key_size) == 0) { + // Overwrite existing value mapping using deep copy semantics + memcpy(entry->value, value, self->value_size); + return C_ERR_OK; + } + entry = entry->next; + } + + // Allocate a new node entry if the key does not exist + int size = (int)sizeof(c_HashMapEntry_t) + self->key_size + self->value_size; + size = C_ALIGN_UPB(size, C_ALIGN_SIZE); + c_HashMapEntry_t* new_entry = (c_HashMapEntry_t*)C_ALLOC(size); + if (!new_entry) return C_ERR_NOMEM; + + new_entry->key = new_entry+1; + new_entry->value = new_entry->key + self->key_size; + + // Deep copy payload bounds properties + memcpy(new_entry->key, key, self->key_size); + memcpy(new_entry->value, value, self->value_size); + + // Single-chain head link injection (O(1)) + new_entry->next = self->buckets[index]; + self->buckets[index] = new_entry; + self->size++; + + return C_ERR_OK; +} + +// Fetches value references safely into user-allocated destination spaces +c_err_t c_HashMap_Get(c_HashMap_t* self, const void* key, void* out_value) { + if (!self || !self->buckets || !key || !out_value) return C_ERR_PARAM; + + uint32_t raw_hash = self->hash(key, self->key_size); + c_size_t index = raw_hash % self->capacity; + + c_HashMapEntry_t* entry = self->buckets[index]; + while (entry != NULL) { + if (self->compare(entry->key, key, self->key_size) == 0) { + memcpy(out_value, entry->value, self->value_size); + return C_ERR_OK; + } + entry = entry->next; + } + + return C_ERR_NOTFOUND; +} + +// Unlinks map items matching key contexts safely (O(1) average time complexity) +c_err_t c_HashMap_Remove(c_HashMap_t* self, const void* key) { + if (!self || !self->buckets || !key) return C_ERR_PARAM; + + uint32_t raw_hash = self->hash(key, self->key_size); + c_size_t index = raw_hash % self->capacity; + + c_HashMapEntry_t** curr = &self->buckets[index]; + while (*curr != NULL) { + if (self->compare((*curr)->key, key, self->key_size) == 0) { + c_HashMapEntry_t* to_delete = *curr; + *curr = to_delete->next; // Unlink node entry frame properties + + // free(to_delete->key); + // free(to_delete->value); + C_FREE(to_delete); + self->size--; + return C_ERR_OK; + } + curr = &(*curr)->next; + } + + return C_ERR_NOTFOUND; +} + +c_bool_t c_HashMap_Contains(c_HashMap_t* self, const void* key) { + if (!self || !self->buckets || !key) return C_FALSE; + + uint32_t raw_hash = self->hash(key, self->key_size); + c_size_t index = raw_hash % self->capacity; + + c_HashMapEntry_t* entry = self->buckets[index]; + while (entry != NULL) { + if (self->compare(entry->key, key, self->key_size) == 0) return C_TRUE; + entry = entry->next; + } + return C_FALSE; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_HashMapKeyIter_Init(c_HashMapKeyIter_t* self, c_HashMap_t* map) { + if (!self || !map) return; + self->map = map; + self->bucket_index = 0; + self->entry = NULL; + + // 初始化時先定位到第一個有效節點 + hashmap_iter_advance_to_valid(self); +} + +// 檢查是否還有下一個元素 +c_bool_t c_HashMapKeyIter_HasNext(c_HashMapKeyIter_t* self) { + if (!self || !self->entry || !*(self->entry)) return C_FALSE; + return C_TRUE; +} + +// 查看目前指向的鍵(Key)指標 (不前進) +void* c_HashMapKeyIter_Get(c_HashMapKeyIter_t* self) { + if (!c_HashMapKeyIter_HasNext(self)) return NULL; + return (*(self->entry))->key; +} + +// 獲取目前指向的鍵(Key)指標,並將迭代器前進到下一個有效節點 +void* c_HashMapKeyIter_Next(c_HashMapKeyIter_t* self) { + if (!c_HashMapKeyIter_HasNext(self)) return NULL; + + c_HashMapEntry_t* curr = *(self->entry); + void* key_ptr = curr->key; + + // 如果當前衝突鏈結中還有下一個節點,直接移向 next + if (curr->next != NULL) { + self->entry = &(curr->next); + } else { + // 如果當前衝突鏈結已到底,前進到下一個桶子並搜尋有效節點 + self->bucket_index++; + hashmap_iter_advance_to_valid(self); + } + + return key_ptr; +} + +// 迭代器安全刪除:在走訪期間以 O(1) 的平均複雜度斷開鏈結並釋放記憶體 +void c_HashMapKeyIter_Remove(c_HashMapKeyIter_t* self) { + if (!c_HashMapKeyIter_HasNext(self)) return; + + c_HashMapEntry_t* to_delete = *(self->entry); + + // 關鍵指標斷開:讓前一個節點的 next(或是桶子的首節點指標)直接指向下一個節點 + *(self->entry) = to_delete->next; + + // 釋放該 Entry 的深複製記憶體 + // free(to_delete->key); + // free(to_delete->value); + C_FREE(to_delete); + + self->map->size--; + + // 檢查斷開後當前位置是否為空(代表原本該桶子的衝突鏈結已走訪完畢) + if (*(self->entry) == NULL) { + // 前進到下一個桶子搜尋下一個有效節點 + self->bucket_index++; + hashmap_iter_advance_to_valid(self); + } + // 備註:若 *(self->entry) != NULL,則 self->entry 自動留在了下一個節點上,不需額外處理 +} + diff --git a/Search/c_HashMap.h b/Search/c_HashMap.h new file mode 100644 index 0000000..2f68949 --- /dev/null +++ b/Search/c_HashMap.h @@ -0,0 +1,59 @@ +#ifndef INCLUDED_C_HASHMAP_H +#define INCLUDED_C_HASHMAP_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_HashMapEntry_t { + void* key; + void* value; + struct c_HashMapEntry_t* next; +} c_HashMapEntry_t; + +typedef uint32_t (*c_HashMap_Hash_f)(const void* key, int key_size); +typedef int (*c_HashMap_Compare_f)(const void* key1, const void* key2, int key_size); + +typedef struct { + c_HashMapEntry_t** buckets; // Array of entry linked list head pointers + c_size_t capacity; // Number of buckets allocated + c_size_t size; // Number of active key-value pairs stored + int key_size; // Byte footprint of the key type + int value_size; // Byte footprint of the value type + c_HashMap_Hash_f hash; // User hash calculation function + c_HashMap_Compare_f compare;// User key comparison function +} c_HashMap_t; + +typedef struct { + c_HashMap_t* map; // 繫結的雜湊表 + c_size_t bucket_index; // 當前走訪的桶子索引 (Bucket Index) + c_HashMapEntry_t** entry; // 指向當前節點指標的指標,用於 O(1) 安全刪除 +} c_HashMapKeyIter_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_HashMap_Init(c_HashMap_t* self, int key_size, int value_size, c_size_t initial_capacity, + c_HashMap_Hash_f hash, c_HashMap_Compare_f compare); +void c_HashMap_Destroy(c_HashMap_t* self); + +c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* value); +c_err_t c_HashMap_Get(c_HashMap_t* self, const void* key, void* out_value); +c_err_t c_HashMap_Remove(c_HashMap_t* self, const void* key); +c_bool_t c_HashMap_Contains(c_HashMap_t* self, const void* key); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_HashMapKeyIter_Init(c_HashMapKeyIter_t* self, c_HashMap_t* map); +c_bool_t c_HashMapKeyIter_HasNext(c_HashMapKeyIter_t* self); +void* c_HashMapKeyIter_Next(c_HashMapKeyIter_t* self); +void* c_HashMapKeyIter_Get(c_HashMapKeyIter_t* self); +void c_HashMapKeyIter_Remove(c_HashMapKeyIter_t* self); + +#endif /*INCLUDED_C_HASHMAP_H*/ diff --git a/Search/c_HashSet.c b/Search/c_HashSet.c new file mode 100644 index 0000000..5ad349e --- /dev/null +++ b/Search/c_HashSet.c @@ -0,0 +1,45 @@ +#include + +// 虛擬佔位常數,所有集合元素在底層對應同一個 Dummy 值的地址 +static const int dummy_value = 1; + +c_err_t c_HashSet_Init(c_HashSet_t* self, int obj_size, c_size_t initial_capacity, + c_HashMap_Hash_f hash, c_HashMap_Compare_f compare) { + if (!self) return C_ERR_PARAM; + + // 初始化底層對映的雜湊表,value_size 固定設為常數大小 + return c_HashMap_Init(&self->map, obj_size, sizeof(int), initial_capacity, hash, compare); +} + +void c_HashSet_Destroy(c_HashSet_t* self) { + if (!self) return; + c_HashMap_Destroy(&self->map); +} + +// 推入元素:若元素已存在則攔截並報錯,確保唯一性 +c_err_t c_HashSet_Add(c_HashSet_t* self, const void* obj) { + if (!self || !obj) return C_ERR_PARAM; + + // 先檢查是否已經存在此元素 + if (c_HashMap_Contains(&self->map, obj)) { + return C_ERR_ALREADY_EXISTS; + } + + // 將物件當作 Key 寫入,Value 塞入 Dummy 常數 + return c_HashMap_Put(&self->map, obj, &dummy_value); +} + +c_err_t c_HashSet_Remove(c_HashSet_t* self, const void* obj) { + if (!self || !obj) return C_ERR_PARAM; + return c_HashMap_Remove(&self->map, obj); +} + +c_bool_t c_HashSet_Contains(c_HashSet_t* self, const void* obj) { + if (!self || !obj) return C_FALSE; + return c_HashMap_Contains(&self->map, obj); +} + +c_size_t c_HashSet_GetSize(const c_HashSet_t* self) { + if (!self) return 0; + return self->map.size; +} \ No newline at end of file diff --git a/Search/c_HashSet.h b/Search/c_HashSet.h new file mode 100644 index 0000000..e83d57e --- /dev/null +++ b/Search/c_HashSet.h @@ -0,0 +1,36 @@ +#ifndef INCLUDED_C_HASHSET_H +#define INCLUDED_C_HASHSET_H + +#ifndef INCLUDED_C_HASHMAP_H +#include +#endif /*INCLUDED_C_HASHMAP_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + c_HashMap_t map; // 底層由 HashMap 驅動 +} c_HashSet_t; + +// 集合迭代器(直接重定向至您的 HashMapKeyIter) +typedef c_HashMapKeyIter_t c_HashSetIter_t; + +// 核心函數宣告 +c_err_t c_HashSet_Init(c_HashSet_t* self, int obj_size, c_size_t initial_capacity, + c_HashMap_Hash_f hash, c_HashMap_Compare_f compare); +void c_HashSet_Destroy(c_HashSet_t* self); + +c_err_t c_HashSet_Add(c_HashSet_t* self, const void* obj); +c_err_t c_HashSet_Remove(c_HashSet_t* self, const void* obj); +c_bool_t c_HashSet_Contains(c_HashSet_t* self, const void* obj); +c_size_t c_HashSet_GetSize(const c_HashSet_t* self); + +// 集合迭代器巨集/函數重定向(完美保持一致性) +#define c_HashSetIter_Init(self, set) c_HashMapKeyIter_Init(self, &(set)->map) +#define c_HashSetIter_HasNext(self) c_HashMapKeyIter_HasNext(self) +#define c_HashSetIter_Get(self) c_HashMapKeyIter_Get(self) +#define c_HashSetIter_Next(self) c_HashMapKeyIter_Next(self) +#define c_HashSetIter_Remove(self) c_HashMapKeyIter_Remove(self) + +#endif /*INCLUDED_C_HASHSET_H*/ diff --git a/Search/c_LinearProbingHashST.c b/Search/c_LinearProbingHashST.c new file mode 100644 index 0000000..5e83a09 --- /dev/null +++ b/Search/c_LinearProbingHashST.c @@ -0,0 +1,196 @@ +#include +#include + +/** + * FNV-1a baseline string/scalar data hash scrambling algorithm. + */ +C_STATIC_FORCE_INLINE +uint32_t c_LPHash_DefaultHash(const void* key, c_size_t key_size) { + const uint8_t* data = (const uint8_t*)key; + uint32_t hash = 0x811C9DC5; + for (c_size_t i = 0; i < key_size; i++) { + hash ^= data[i]; + hash *= 0x01000193; + } + return hash; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* st, c_size_t initial_capacity, + c_size_t key_size, c_size_t val_size, + uint32_t (*hash_fn)(const void*, c_size_t), + int (*key_compar)(const void*, const void*)) { + if (st == NULL || initial_capacity == 0 || key_size == 0 || val_size == 0 || key_compar == NULL) { + return C_ERR_PARAM; + } + + st->M = initial_capacity; + st->N = 0; + st->key_size = key_size; + st->val_size = val_size; + st->hash_fn = (hash_fn != NULL) ? hash_fn : c_LPHash_DefaultHash; + st->key_compar = key_compar; + + st->keys = C_ALLOC(st->M * key_size); + st->vals = C_ALLOC(st->M * val_size); + st->occupied = (c_bool_t*)C_ALLOC(st->M * sizeof(c_bool_t)); + + if (st->keys == NULL || st->vals == NULL || st->occupied == NULL) { + C_FREE(st->keys); C_FREE(st->vals); C_FREE(st->occupied); + st->keys = NULL; st->vals = NULL; st->occupied = NULL; + return C_ERR_NOMEM; + } + + memset(st->occupied, C_FALSE, st->M * sizeof(c_bool_t)); + return C_ERR_OK; +} + +void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* st) { + if (st) { + C_FREE(st->keys); st->keys = NULL; + C_FREE(st->vals); st->vals = NULL; + C_FREE(st->occupied); st->occupied = NULL; + st->M = 0; + st->N = 0; + } +} + +/** + * Explicit internal resizing routing handler. + * Essential for keeping the Load Factor (alpha) under 0.5 to prevent clustering. + */ +static c_err_t c_LinearProbingHashST_Resize(c_LinearProbingHashST_t* st, c_size_t capacity) { + c_LinearProbingHashST_t temp_st; + c_err_t err = c_LinearProbingHashST_Init(&temp_st, capacity, st->key_size, st->val_size, st->hash_fn, st->key_compar); + if (err != C_ERR_OK) return err; + + char* keys_base = (char*)st->keys; + char* vals_base = (char*)st->vals; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + // Rehash and insert all existing active items into the new, expanded table footprint + for (c_size_t i = 0; i < st->M; i++) { + if (st->occupied[i]) { + extern c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t*, const void*, const void*); + err = c_LinearProbingHashST_Put(&temp_st, keys_base + (i * ks), vals_base + (i * vs)); + if (err != C_ERR_OK) { + c_LinearProbingHashST_Destroy(&temp_st); + return err; + } + } + } + + // Swap parameters to apply the newly rehashed table context + C_FREE(st->keys); C_FREE(st->vals); C_FREE(st->occupied); + st->keys = temp_st.keys; + st->vals = temp_st.vals; + st->occupied = temp_st.occupied; + st->M = temp_st.M; + return C_ERR_OK; +} + +c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* st, const void* key, const void* val) { + if (st == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + // Enforce an upper bound load factor limit of 50% to mitigate clustering degradation + if (st->N >= st->M / 2) { + c_err_t err = c_LinearProbingHashST_Resize(st, st->M * 2); + if (err != C_ERR_OK) return err; + } + + char* keys_base = (char*)st->keys; + char* vals_base = (char*)st->vals; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + c_size_t i; + for (i = st->hash_fn(key, ks) % st->M; st->occupied[i]; i = (i + 1) % st->M) { + if (st->key_compar(keys_base + (i * ks), key) == 0) { + // Found existing key match: update values block payload in place + memcpy(vals_base + (i * vs), val, vs); + return C_ERR_OK; + } + } + + // Insert new item into the available open slot found by probing + memcpy(keys_base + (i * ks), key, ks); + memcpy(vals_base + (i * vs), val, vs); + st->occupied[i] = C_TRUE; + st->N++; + + return C_ERR_OK; +} + +void* c_LinearProbingHashST_Get(const c_LinearProbingHashST_t* st, const void* key) { + if (st == NULL || st->keys == NULL || key == NULL) return NULL; + + char* keys_base = (char*)st->keys; + c_size_t ks = st->key_size; + + for (c_size_t i = st->hash_fn(key, ks) % st->M; st->occupied[i]; i = (i + 1) % st->M) { + if (st->key_compar(keys_base + (i * ks), key) == 0) { + return (char*)st->vals + (i * st->val_size); + } + } + return NULL; +} + +c_bool_t c_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* st, const void* key) { + return c_LinearProbingHashST_Get(st, key) != NULL; +} + +c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* st, const void* key) { + if (st == NULL || key == NULL) return C_ERR_PARAM; + + char* keys_base = (char*)st->keys; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + c_size_t i = st->hash_fn(key, ks) % st->M; + while (st->occupied[i]) { + if (st->key_compar(keys_base + (i * ks), key) == 0) { + break; + } + i = (i + 1) % st->M; + } + + // Key to delete was not found in the hash table + if (!st->occupied[i]) return C_ERR_NOTFOUND; + + // Hard delete: Free the targeted slot index flag + st->occupied[i] = C_FALSE; + st->N--; + + // CRITICAL REQUIREMENT: Rehash all subsequent cluster elements + // to bridge the open slot gap caused by deletion, preventing future search short-circuits. + i = (i + 1) % st->M; + while (st->occupied[i]) { + // Capture old keys/values payload allocations locally + void* key_to_rehash = C_ALLOC(ks); + void* val_to_rehash = C_ALLOC(vs); + memcpy(key_to_rehash, keys_base + (i * ks), ks); + memcpy(val_to_rehash, (char*)st->vals + (i * vs), vs); + + // Explicitly clear the current cluster entry tracking variables + st->occupied[i] = C_FALSE; + st->N--; + + // Re-insert the captured element into the table using standard routing rules + c_LinearProbingHashST_Put(st, key_to_rehash, val_to_rehash); + + C_FREE(key_to_rehash); + C_FREE(val_to_rehash); + + i = (i + 1) % st->M; + } + + // Shrink the table capacity automatically if utilization drops below 12.5% + if (st->N > 0 && st->N <= st->M / 8) { + c_LinearProbingHashST_Resize(st, st->M / 2); + } + + return C_ERR_OK; +} diff --git a/Search/c_LinearProbingHashST.h b/Search/c_LinearProbingHashST.h new file mode 100644 index 0000000..5ab390f --- /dev/null +++ b/Search/c_LinearProbingHashST.h @@ -0,0 +1,40 @@ +#ifndef INCLUDED_C_LINEARPROBINGHASHST_H +#define INCLUDED_C_LINEARPROBINGHASHST_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Linear Probing Hash Symbol Table Instance Layout +typedef struct { + void* keys; // Flat parallel array block storing keys + void* vals; // Flat parallel array block storing values + c_bool_t* occupied; // Flag array tracking whether a specific slot is filled + + c_size_t M; // Linear probing table capacity (array size) + c_size_t N; // Current active element count + c_size_t key_size; // Size of each key in bytes + c_size_t val_size; // Size of each value in bytes + + uint32_t (*hash_fn)(const void* key, c_size_t key_size); // Hash function + int (*key_compar)(const void*, const void*); // Key comparison rule pointer +} c_LinearProbingHashST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* st, c_size_t initial_capacity, + c_size_t key_size, c_size_t val_size, + uint32_t (*hash_fn)(const void*, c_size_t), + int (*key_compar)(const void*, const void*)); +void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* st); +c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* st, const void* key, const void* val); +void* c_LinearProbingHashST_Get(const c_LinearProbingHashST_t* st, const void* key); +c_bool_t c_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* st, const void* key); +c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* st, const void* key); + +#endif /*INCLUDED_C_LINEARPROBINGHASHST_H*/ diff --git a/Search/c_RedBlackBST.c b/Search/c_RedBlackBST.c new file mode 100644 index 0000000..18e715e --- /dev/null +++ b/Search/c_RedBlackBST.c @@ -0,0 +1,154 @@ +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +// --- Structural Balancing Primitives --- + +C_STATIC_FORCE_INLINE +c_RBNode_t* c_RBBST_RotateLeft(c_RBNode_t* h) { + c_RBNode_t* x = h->right; + h->right = x->left; + x->left = h; + x->color = h->color; + h->color = C_RB_RED; + return x; +} + +C_STATIC_FORCE_INLINE +c_RBNode_t* c_RBBST_RotateRight(c_RBNode_t* h) { + c_RBNode_t* x = h->left; + h->left = x->right; + x->right = h; + x->color = h->color; + h->color = C_RB_RED; + return x; +} + +C_STATIC_FORCE_INLINE +void c_RBBST_FlipColors(c_RBNode_t* h) { + h->color = !h->color; + if (h->left) h->left->color = !h->left->color; + if (h->right) h->right->color = !h->right->color; +} + +/** + * Creates and initializes a standalone tree node. + */ +C_STATIC_FORCE_INLINE +c_RBNode_t* c_RBBST_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) { + c_RBNode_t* node = (c_RBNode_t*)C_ALLOC(sizeof(c_RBNode_t) + ks + vs); + if (node == NULL) return NULL; + + node->left = NULL; + node->right = NULL; + node->color = C_RB_RED; // New nodes are always inserted as RED links + memcpy(c_RBBST_NodeKey(node), key, ks); + memcpy(c_RBBST_NodeVal(node, ks), val, vs); + return node; +} + +static void c_RBBST_DestroyNodes(c_RBNode_t* node) { + if (node == NULL) return; + c_RBBST_DestroyNodes(node->left); + c_RBBST_DestroyNodes(node->right); + C_FREE(node); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_RedBlackBST_Init(c_RedBlackBST_t* tree, c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)) { + if (tree == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM; + tree->root = NULL; + tree->key_size = key_size; + tree->val_size = val_size; + tree->size = 0; + tree->compar = compar; + return C_ERR_OK; +} + +void c_RedBlackBST_Destroy(c_RedBlackBST_t* tree) { + if (tree) { + c_RBBST_DestroyNodes(tree->root); + tree->root = NULL; + tree->size = 0; + } +} + +c_bool_t c_RedBlackBST_Contains(const c_RedBlackBST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return C_FALSE; + c_RBNode_t* curr = tree->root; + while (curr != NULL) { + int cmp = tree->compar(key, c_RBBST_NodeKey(curr)); + if (cmp == 0) return C_TRUE; + curr = (cmp < 0) ? curr->left : curr->right; + } + return C_FALSE; +} + +void* c_RedBlackBST_Get(const c_RedBlackBST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return NULL; + c_RBNode_t* curr = tree->root; + while (curr != NULL) { + int cmp = tree->compar(key, c_RBBST_NodeKey(curr)); + if (cmp == 0) return c_RBBST_NodeVal(curr, tree->key_size); + curr = (cmp < 0) ? curr->left : curr->right; + } + return NULL; +} + +/** + * Recursive insertion core worker. + */ +static c_RBNode_t* c_RBBST_PutInternal(c_RedBlackBST_t* tree, c_RBNode_t* h, + const void* key, const void* val, c_err_t* err) { + if (h == NULL) { + c_RBNode_t* node = c_RBBST_CreateNode(key, val, tree->key_size, tree->val_size); + if (node == NULL) *err = C_ERR_NOMEM; + else tree->size++; + return node; + } + + int cmp = tree->compar(key, c_RBBST_NodeKey(h)); + if (cmp < 0) { + h->left = c_RBBST_PutInternal(tree, h->left, key, val, err); + } else if (cmp > 0) { + h->right = c_RBBST_PutInternal(tree, h->right, key, val, err); + } else { + // Enforce update if key matches existing tracking cell + memcpy(c_RBBST_NodeVal(h, tree->key_size), val, tree->val_size); + } + + // --- Left-Leaning Red-Black Balancing Pipeline Validation Steps --- + // Condition 1: Right child is red, left child is black -> Rotate Left + if (c_RBBST_IsRed(h->right) && !c_RBBST_IsRed(h->left)) { + h = c_RBBST_RotateLeft(h); + } + // Condition 2: Left child and left grandchild are both red -> Rotate Right + if (c_RBBST_IsRed(h->left) && c_RBBST_IsRed(h->left->left)) { + h = c_RBBST_RotateRight(h); + } + // Condition 3: Both children are red -> Color Split Flip + if (c_RBBST_IsRed(h->left) && c_RBBST_IsRed(h->right)) { + c_RBBST_FlipColors(h); + } + + return h; +} + +c_err_t c_RedBlackBST_Put(c_RedBlackBST_t* tree, const void* key, const void* val) { + if (tree == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + c_err_t err = C_ERR_OK; + tree->root = c_RBBST_PutInternal(tree, tree->root, key, val, &err); + + if (tree->root != NULL) { + tree->root->color = C_RB_BLACK; // Root link must consistently point black + } + + return err; +} diff --git a/Search/c_RedBlackBST.h b/Search/c_RedBlackBST.h new file mode 100644 index 0000000..5ae6859 --- /dev/null +++ b/Search/c_RedBlackBST.h @@ -0,0 +1,66 @@ +#ifndef INCLUDED_C_REDBLACKBST_H +#define INCLUDED_C_REDBLACKBST_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Link Color Definitions +typedef enum { + C_RB_BLACK = 0, + C_RB_RED = 1 +} c_RBColor_t; + +// Node Structure Layout +typedef struct c_RBNode { + struct c_RBNode* left; + struct c_RBNode* right; + c_RBColor_t color; + // Payload layout: key block followed immediately by the value block in memory +} c_RBNode_t; + +// Red-Black BST Context Structure +typedef struct { + c_RBNode_t* root; + c_size_t key_size; + c_size_t val_size; + c_size_t size; + int (*compar)(const void*, const void*); +} c_RedBlackBST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +// --- Internal Helper Accessors --- +C_STATIC_FORCE_INLINE +void* c_RBBST_NodeKey(c_RBNode_t* node) { + return (void*)((char*)node + sizeof(c_RBNode_t)); +} + +C_STATIC_FORCE_INLINE +void* c_RBBST_NodeVal(c_RBNode_t* node, c_size_t key_size) { + return (void*)((char*)node + sizeof(c_RBNode_t) + key_size); +} + +C_STATIC_FORCE_INLINE +c_bool_t c_RBBST_IsRed(c_RBNode_t* node) { + if (node == NULL) return C_FALSE; + return node->color == C_RB_RED; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_RedBlackBST_Init(c_RedBlackBST_t* tree, c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)); +void c_RedBlackBST_Destroy(c_RedBlackBST_t* tree); + +c_bool_t c_RedBlackBST_Contains(const c_RedBlackBST_t* tree, const void* key); +c_err_t c_RedBlackBST_Put(c_RedBlackBST_t* tree, const void* key, const void* val); +void* c_RedBlackBST_Get(const c_RedBlackBST_t* tree, const void* key); + +#endif /*INCLUDED_C_REDBLACKBST_H*/ diff --git a/Search/c_SeparateChainingHashST.c b/Search/c_SeparateChainingHashST.c new file mode 100644 index 0000000..f245af2 --- /dev/null +++ b/Search/c_SeparateChainingHashST.c @@ -0,0 +1,308 @@ +#include +#include + +/** + * Default MurmurHash3 (32-bit) implementation for basic scalar and string keys. + * Maximizes distribution and avalanche property to minimize bucket collisions. + */ +C_STATIC_FORCE_INLINE +uint32_t c_SCHash_DefaultHash(const void* key, c_size_t key_size) { + const uint8_t* data = (const uint8_t*)key; + uint32_t hash = 0x811C9DC5; // FNV-1a baseline for quick scrambling if needed, but using a robust mix + for (c_size_t i = 0; i < key_size; i++) { + hash ^= data[i]; + hash *= 0x01000193; + } + return hash; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets, + c_size_t key_size, c_size_t val_size, + uint32_t (*hash_fn)(const void*, c_size_t), + int (*key_compar)(const void*, const void*)) { + if (st == NULL || num_buckets == 0 || key_size == 0 || val_size == 0 || key_compar == NULL) { + return C_ERR_PARAM; + } + + st->num_buckets = num_buckets; + st->key_size = key_size; + st->val_size = val_size; + st->size = 0; + st->hash_fn = (hash_fn != NULL) ? hash_fn : c_SCHash_DefaultHash; + st->key_compar = key_compar; + + // Allocate array of bucket head pointers + st->buckets = (c_SCHashNode_t**)C_ALLOC(num_buckets * sizeof(c_SCHashNode_t*)); + if (st->buckets == NULL) return C_ERR_NOMEM; + + // Clear bucket heads cleanly + memset(st->buckets, 0, num_buckets * sizeof(c_SCHashNode_t*)); + + return C_ERR_OK; +} + +void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st) { + if (st && st->buckets) { + for (c_size_t i = 0; i < st->num_buckets; i++) { + c_SCHashNode_t* curr = st->buckets[i]; + while (curr != NULL) { + c_SCHashNode_t* next = curr->next; + C_FREE(curr); + curr = next; + } + } + C_FREE(st->buckets); + st->size = 0; + st->num_buckets = 0; + } +} + +c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key) { + if (st == NULL || st->buckets == NULL || key == NULL) return C_FALSE; + + uint32_t hash = st->hash_fn(key, st->key_size); + c_size_t bucket_idx = hash % st->num_buckets; + + c_SCHashNode_t* curr = st->buckets[bucket_idx]; + while (curr != NULL) { + if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) { + return C_TRUE; + } + curr = curr->next; + } + return C_FALSE; +} + +c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val) { + if (st == NULL || st->buckets == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + uint32_t hash = st->hash_fn(key, st->key_size); + c_size_t bucket_idx = hash % st->num_buckets; + + c_SCHashNode_t* curr = st->buckets[bucket_idx]; + while (curr != NULL) { + if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) { + // Key match: Overwrite value in place + memcpy(c_SCHash_NodeVal(curr, st->key_size), val, st->val_size); + return C_ERR_OK; + } + curr = curr->next; + } + + // Key not found: Construct a unified packed node + c_SCHashNode_t* new_node = (c_SCHashNode_t*)C_ALLOC(sizeof(c_SCHashNode_t) + st->key_size + st->val_size); + if (new_node == NULL) return C_ERR_NOMEM; + + memcpy(c_SCHash_NodeKey(new_node), key, st->key_size); + memcpy(c_SCHash_NodeVal(new_node, st->key_size), val, st->val_size); + + // Insert at head of the bucket chain (O(1) insertion) + new_node->next = st->buckets[bucket_idx]; + st->buckets[bucket_idx] = new_node; + st->size++; + + return C_ERR_OK; +} + +void* c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_t* st, const void* key) { + if (st == NULL || st->buckets == NULL || key == NULL) return NULL; + + uint32_t hash = st->hash_fn(key, st->key_size); + c_size_t bucket_idx = hash % st->num_buckets; + + c_SCHashNode_t* curr = st->buckets[bucket_idx]; + while (curr != NULL) { + if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) { + return c_SCHash_NodeVal(curr, st->key_size); + } + curr = curr->next; + } + return NULL; +} + +c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* st, const void* key) { + if (st == NULL || st->buckets == NULL || key == NULL) return C_ERR_PARAM; + + uint32_t hash = st->hash_fn(key, st->key_size); + c_size_t bucket_idx = hash % st->num_buckets; + + c_SCHashNode_t** link = &st->buckets[bucket_idx]; + c_SCHashNode_t* curr = st->buckets[bucket_idx]; + + while (curr != NULL) { + if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) { + // Unlink node cleanly using double pointer redirection + *link = curr->next; + C_FREE(curr); + st->size--; + return C_ERR_OK; + } + link = &curr->next; + curr = curr->next; + } + + return C_ERR_NOTFOUND; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ +/** + * Steps the cursor forward to the next occupied bucket slot. + */ +C_STATIC_FORCE_INLINE +void c_SCHashIter_AdvanceToNextValid(c_SeparateChainingHashSTKeyIter_t* iter) { + iter->curr_node = NULL; + iter->curr_bucket++; + + while (iter->curr_bucket < iter->st->num_buckets) { + if (iter->st->buckets[iter->curr_bucket] != NULL) { + iter->curr_node = iter->st->buckets[iter->curr_bucket]; + break; + } + iter->curr_bucket++; + } +} + +/** + * Initialize the Separate Chaining Hash Symbol Table Key Iterator. + * Traverses forward to latch onto the very first active key node element across bucket slots. + * + * Time Complexity: O(M) worst-case to locate first entry where M is bucket count | Space Complexity: O(1) + */ +c_err_t c_SeparateChainingHashSTKeyIter_Init(c_SeparateChainingHashSTKeyIter_t* iter, + const c_SeparateChainingHashST_t* st) { + if (iter == NULL || st == NULL) return C_ERR_PARAM; + + // Cast away constness to bind to the non-const structural field required for Remove() + iter->st = (c_SeparateChainingHashST_t*)st; + iter->curr_bucket = 0; + iter->curr_node = NULL; + iter->last_returned = NULL; + + // Advance forward to locate the first populated bucket slot index context + while (iter->curr_bucket < st->num_buckets) { + if (st->buckets[iter->curr_bucket] != NULL) { + iter->curr_node = st->buckets[iter->curr_bucket]; + break; + } + iter->curr_bucket++; + } + + return C_ERR_OK; +} + +/** + * Clean up allocations within the context wrapper safely. + */ +void c_SeparateChainingHashSTKeyIter_Destroy(c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter) { + iter->st = NULL; + iter->curr_bucket = 0; + iter->curr_node = NULL; + iter->last_returned = NULL; + } +} + +/** + * Evaluates whether any keys remain unread inside the look-ahead pipeline. + */ +c_bool_t c_SeparateChainingHashSTKeyIter_HasNext(const c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter == NULL) return C_FALSE; + return iter->curr_node != NULL; +} + +/** + * Retrieve a reference pointer to the key most recently extracted by Next(). + * + * Time Complexity: O(1) constant runtime overhead + */ +void* c_SeparateChainingHashSTKeyIter_Get(c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter == NULL || iter->curr_node == NULL) return NULL; + + // 直接回傳當前指標停靠節點的 Key + iter->last_returned = c_SCHash_NodeKey(iter->curr_node); + return iter->last_returned; +} + +/** + * Extracts a pointer to the next consecutive key element. + * Updates internal path registers to step along table slots. + * + * Time Complexity: O(M) worst case to skip empty buckets, O(1) amortized + */ +void* c_SeparateChainingHashSTKeyIter_Next(c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter == NULL || iter->curr_node == NULL) return NULL; + + c_SCHashNode_t* node = iter->curr_node; + void* current_key = c_SCHash_NodeKey(node); + + // Track history for the Remove state machine + iter->last_returned = current_key; + + // Advance forward natively + if (node->next != NULL) { + iter->curr_node = node->next; + } else { + c_SCHashIter_AdvanceToNextValid(iter); + } + + return current_key; +} + + + +/** + * Stateful Removal Engine for the Hash table chain structure layout. + * Safely handles unlinking modifications and heals traversal registers in O(1) amortized time. + */ +c_err_t c_SeparateChainingHashSTKeyIter_Remove(c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter == NULL || iter->st == NULL) return C_ERR_PARAM; + if (iter->last_returned == NULL) return C_ERR_NOTFOUND; + + // Find the targeted bucket index for the key we are deleting + uint32_t hash = iter->st->hash_fn(iter->last_returned, iter->st->key_size); + c_size_t target_bucket = hash % iter->st->num_buckets; + + // Use a double pointer to locate and delete the node from the backing list chain + c_SCHashNode_t** link = &iter->st->buckets[target_bucket]; + c_SCHashNode_t* curr = iter->st->buckets[target_bucket]; + c_SCHashNode_t* next_valid_node = NULL; + + while (curr != NULL) { + if (iter->st->key_compar(iter->last_returned, c_SCHash_NodeKey(curr)) == 0) { + // Capture the next element pointer in the link chain before unlinking + next_valid_node = curr->next; + + // Perform the structural delete + *link = curr->next; + C_FREE(curr); + iter->st->size--; + break; + } + link = &curr->next; + curr = curr->next; + } + + // Reset the state machine tracking register to prevent double deletion + iter->last_returned = NULL; + + // --- EXPLICIT FORWARD SYNCHRONIZATION --- + // Update the iterator's position to point to the correct next element + if (next_valid_node != NULL) { + iter->curr_node = next_valid_node; + iter->curr_bucket = target_bucket; + } else { + // If the deletion emptied out the remainder of this bucket chain, + // search forward through subsequent buckets to find the next valid node. + iter->curr_bucket = target_bucket; + c_SCHashIter_AdvanceToNextValid(iter); + } + + return C_ERR_OK; +} + + + diff --git a/Search/c_SeparateChainingHashST.h b/Search/c_SeparateChainingHashST.h new file mode 100644 index 0000000..bd39ba0 --- /dev/null +++ b/Search/c_SeparateChainingHashST.h @@ -0,0 +1,78 @@ +#ifndef INCLUDED_C_SEPARATECHAININGHASHST_H +#define INCLUDED_C_SEPARATECHAININGHASHST_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Forward declaration of internal node structure +typedef struct c_SCHashNode { + struct c_SCHashNode* next; // Pointer to next node in the chain + // Payload layout: key block followed immediately by the value block in memory +} c_SCHashNode_t; + +// Separate Chaining Hash ST Context Structure +typedef struct { + c_SCHashNode_t** buckets; // Array of linked list head pointers + c_size_t num_buckets; // Total number of buckets (M) + c_size_t key_size; // Size of each key in bytes + c_size_t val_size; // Size of each value in bytes + c_size_t size; // Total number of key-value pairs (N) + + uint32_t (*hash_fn)(const void* key, c_size_t key_size); // Custom hash function + int (*key_compar)(const void*, const void*); // Key comparison rule pointer +} c_SeparateChainingHashST_t; + +typedef struct { + c_SeparateChainingHashST_t* st; // Reference link to backing hash table container + c_size_t curr_bucket; // Active index tracking variable inside the flat array + c_SCHashNode_t* curr_node; // Head cursor tracking elements inside linked list buckets + void* last_returned; // Pointer caching the key payload returned by Next() +} c_SeparateChainingHashSTKeyIter_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// --- Internal Helper Accessors --- +C_STATIC_FORCE_INLINE +void* c_SCHash_NodeKey(c_SCHashNode_t* node) { + if (node == NULL) return NULL; + return (void*)((char*)node + sizeof(c_SCHashNode_t)); +} + +C_STATIC_FORCE_INLINE +void* c_SCHash_NodeVal(c_SCHashNode_t* node, c_size_t key_size) { + if (!node) return NULL; + return (void*)((char*)node + sizeof(c_SCHashNode_t) + key_size); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets, + c_size_t key_size, c_size_t val_size, + uint32_t (*hash_fn)(const void*, c_size_t), + int (*key_compar)(const void*, const void*)); + +void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st); + +c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key); +c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val); +void* c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_t* st, const void* key); +c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* st, const void* key); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_SeparateChainingHashSTKeyIter_Init(c_SeparateChainingHashSTKeyIter_t* iter, + const c_SeparateChainingHashST_t* st); +void c_SeparateChainingHashSTKeyIter_Destroy(c_SeparateChainingHashSTKeyIter_t* iter); +c_bool_t c_SeparateChainingHashSTKeyIter_HasNext(const c_SeparateChainingHashSTKeyIter_t* iter); +void* c_SeparateChainingHashSTKeyIter_Get(c_SeparateChainingHashSTKeyIter_t* iter); +void* c_SeparateChainingHashSTKeyIter_Next(c_SeparateChainingHashSTKeyIter_t* iter); +c_err_t c_SeparateChainingHashSTKeyIter_Remove(c_SeparateChainingHashSTKeyIter_t* iter) ; + +#endif /*INCLUDED_C_SEPARATECHAININGHASHST_H*/ diff --git a/Search/c_TST.c b/Search/c_TST.c new file mode 100644 index 0000000..09c9609 --- /dev/null +++ b/Search/c_TST.c @@ -0,0 +1,312 @@ +#include +#include +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/* Internal constructor helper to build an isolated TST node capsule */ +static c_TSTNode_t* c_TSTNode_Create(char c) { + c_TSTNode_t* node = (c_TSTNode_t*)C_CALLOC(1, sizeof(c_TSTNode_t)); + if (node) { + node->c = c; + } + return node; +} + +/* Internal destructor helper to clear TST nodes non-recursively using an explicit heap stack */ +static void c_TSTNode_DestroyRecursive(c_TSTNode_t* root) { + if (!root) return; + + c_ArrayStack_t node_stack; + c_ArrayStack_Init(&node_stack, sizeof(c_TSTNode_t*), 256); + c_ArrayStack_Push(&node_stack, &root); + + while (!c_ArrayStack_IsEmpty(&node_stack)) { + c_TSTNode_t* curr = 0; + c_ArrayStack_Pop(&node_stack, &curr); + c_bool_t advanced = C_FALSE; + + // Push children onto the cleanup stack frame and clear links to avoid cycles + if (curr->left) { + c_ArrayStack_Push(&node_stack, &curr->left); + curr->left = NULL; + advanced = C_TRUE; + } else if (curr->mid) { + c_ArrayStack_Push(&node_stack, &curr->mid); + curr->mid = NULL; + advanced = C_TRUE; + } else if (curr->right) { + c_ArrayStack_Push(&node_stack, &curr->right); + curr->right = NULL; + advanced = C_TRUE; + } + + if (advanced == C_FALSE) { + C_FREE(curr); + } + } + + c_ArrayStack_Destroy(&node_stack); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_TST_Init(c_TST_t* self) { + if (!self) return C_ERR_PARAM; + self->root = NULL; + self->size = 0; + return C_ERR_OK; +} + +void c_TST_Destroy(c_TST_t* self) { + if (!self) return; + c_TSTNode_DestroyRecursive(self->root); + self->root = NULL; + self->size = 0; +} + +/* Internal recursive worker to support clean TST node creation and value insertions */ +static c_TSTNode_t* c_TST_PutWorker(c_TSTNode_t* x, const char* key, c_size_t d, void* value, c_bool_t* is_new, c_err_t* err) { + char c = key[d]; + if (!x) { + x = c_TSTNode_Create(c); + if (!x) { + *err = C_ERR_NOMEM; + return NULL; + } + } + + if (c < x->c) { + x->left = c_TST_PutWorker(x->left, key, d, value, is_new, err); + } else if (c > x->c) { + x->right = c_TST_PutWorker(x->right, key, d, value, is_new, err); + } else if (d < strlen(key) - 1) { + x->mid = c_TST_PutWorker(x->mid, key, d + 1, value, is_new, err); + } else { + if (x->value == NULL) { + *is_new = C_TRUE; + } + x->value = value; + } + return x; +} + +c_err_t c_TST_Put(c_TST_t* self, const char* key, void* value) { + if (!self || !key || strlen(key) == 0 || !value) return C_ERR_PARAM; + + c_bool_t is_new = C_FALSE; + c_err_t err = C_ERR_OK; + self->root = c_TST_PutWorker(self->root, key, 0, value, &is_new, &err); + + if (err == C_ERR_OK && is_new == C_TRUE) { + self->size++; + } + return err; +} + +void* c_TST_Get(c_TST_t* self, const char* key) { + if (!self || !key || strlen(key) == 0 || !self->root) return NULL; + + c_TSTNode_t* curr = self->root; + c_size_t d = 0; + c_size_t len = strlen(key); + + while (curr) { + char c = key[d]; + if (c < curr->c) { + curr = curr->left; + } else if (c > curr->c) { + curr = curr->right; + } else if (d < len - 1) { + curr = curr->mid; + d++; + } else { + return curr->value; + } + } + return NULL; +} + +c_bool_t c_TST_Contains(c_TST_t* self, const char* key) { + return (c_TST_Get(self, key) != NULL) ? C_TRUE : C_FALSE; +} + +/* Internal prefix traversal worker */ +static void c_TST_CollectWorker(c_TSTNode_t* x, c_StringBuffer_t* sb, c_size_t depth, c_StringList* result) { + if (!x) return; + + // Explore smaller alphabetical character trees leftward (keeps current string prefix length unchanged) + c_TST_CollectWorker(x->left, sb, depth, result); + + // Append the matching node character token directly to the string builder + c_StringBuffer_Append(sb, &(x->c), 1); + if (x->value != NULL) { + c_StringList_Append(result, sb->buffer); + } + + // Continue crawling down matching children on the middle branch + c_TST_CollectWorker(x->mid, sb, depth + 1, result); + + // Backtrack step: restore parent character layout configuration length boundaries + c_StringBuffer_SetLength(sb, depth); + + // Explore larger alphabetical character trees rightward + c_TST_CollectWorker(x->right, sb, depth, result); +} + +c_err_t c_TST_KeysWithPrefix(c_TST_t* self, const char* prefix, c_StringList* result) { + if (!self || !prefix || !result || !self->root) return C_ERR_PARAM; + + c_TSTNode_t* curr = self->root; + c_size_t d = 0; + c_size_t len = strlen(prefix); + + // Navigate to the end node matching the prefix string character rules + while (curr) { + char c = prefix[d]; + if (c < curr->c) { + curr = curr->left; + } else if (c > curr->c) { + curr = curr->right; + } else if (d < len - 1) { + curr = curr->mid; + d++; + } else { + break; // Prefix matched up to curr node boundaries + } + } + + if (!curr) return C_ERR_OK; // Prefix not found safely yields 0 matches + + c_StringBuffer_t sb; + if (c_StringBuffer_Init(&sb, 256) != C_ERR_OK) return C_ERR_NOMEM; + + // Seed our builder with the matching prefix handle string tokens + c_StringBuffer_Append(&sb, prefix, len); + + // If the prefix node boundary itself holds an active value, record it first + if (curr->value != NULL) { + c_StringList_Append(result, prefix); + } + + // Crawl down the middle branch to extract all matching multi-character children variations + c_TST_CollectWorker(curr->mid, &sb, len, result); + + c_StringBuffer_Destroy(&sb); + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/* Internal recursive wildcard collector worker */ +static void c_TST_MatchWorker(c_TSTNode_t* x, c_StringBuffer_t* sb, const char* pattern, c_size_t d, c_StringList* result) { + if (!x) return; + + char c = pattern[d]; + c_size_t len = strlen(pattern); + + // Explore smaller characters leftward if the pattern permits or if it's a wildcard + if (c == '.' || c < x->c) { + c_TST_MatchWorker(x->left, sb, pattern, d, result); + } + + // Process current node character matching boundaries + if (c == '.' || c == x->c) { + // Append the current split character token onto your string buffer builder stack + c_StringBuffer_Append(sb, &(x->c), 1); + + // Terminal case: If we have reached the final character index of the pattern layout string + if (d == len - 1) { + if (x->value != NULL) { + c_StringList_Append(result, sb->buffer); + } + } else { + // Advance deeper down the middle branch to explore matching multi-character continuations + c_TST_MatchWorker(x->mid, sb, pattern, d + 1, result); + } + + // Backtracking unwinding step: reset logical buffer length configuration framework + c_StringBuffer_SetLength(sb, d); + } + + // Explore larger characters rightward if the pattern permits or if it's a wildcard + if (c == '.' || c > x->c) { + c_TST_MatchWorker(x->right, sb, pattern, d, result); + } +} + +/** + * Gather all keys currently matching a specific wildcard pattern string inside the TST + */ +c_err_t c_TST_KeysThatMatch(c_TST_t* self, const char* pattern, c_StringList* result) { + if (!self || !pattern || strlen(pattern) == 0 || !result || !self->root) { + return C_ERR_PARAM; + } + + c_StringBuffer_t sb; + if (c_StringBuffer_Init(&sb, 256) != C_ERR_OK) { + return C_ERR_NOMEM; + } + + // Start crawling the ternary search tree using our shared string buffer accumulator + c_TST_MatchWorker(self->root, &sb, pattern, 0, result); + + c_StringBuffer_Destroy(&sb); + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +char* c_TST_LongestPrefixOf(c_TST_t* self, const char* query) { + if (!self || !query || !self->root) { + return NULL; + } + + c_TSTNode_t* curr = self->root; + c_size_t query_len = strlen(query); + c_size_t longest_match_len = 0; + c_bool_t match_found = C_FALSE; + c_size_t d = 0; + + // Run an iterative O(L) scan across TST split character tokens + while (curr && d < query_len) { + char c = query[d]; + + if (c < curr->c) { + curr = curr->left; // Step left: current character is smaller + } else if (c > curr->c) { + curr = curr->right; // Step right: current character is larger + } else { + // Character matches curr->c exactly! Check if this marks a complete key + if (curr->value != NULL) { + longest_match_len = d + 1; + match_found = C_TRUE; + } + curr = curr->mid; // Advance down the middle branch + d++; // Advance to next character in query string + } + } + + // Allocate an isolated heap buffer to hold the output string copy + c_size_t output_bytes = match_found ? (longest_match_len + 1) : 1; + char* result_str = (char*)C_ALLOC(output_bytes); + if (!result_str) { + return NULL; + } + + if (match_found == C_TRUE) { + memcpy(result_str, query, longest_match_len); + result_str[longest_match_len] = '\0'; + } else { + result_str[0] = '\0'; // Return a clean empty string if no prefix matches + } + + return result_str; +} \ No newline at end of file diff --git a/Search/c_TST.h b/Search/c_TST.h new file mode 100644 index 0000000..8b2e707 --- /dev/null +++ b/Search/c_TST.h @@ -0,0 +1,77 @@ +#ifndef INCLUDED_C_TST_H +#define INCLUDED_C_TST_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_STRINGLIST_H +#include +#endif /*INCLUDED_C_STRINGLIST_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_TSTNode { + char c; // The single character split character token for this node + void* value; // Generic client value pointer associated with a complete key string + struct c_TSTNode* left; // Left branch pointer: character is smaller (<) + struct c_TSTNode* mid; // Middle branch pointer: character matches (==) + struct c_TSTNode* right; // Right branch pointer: character is larger (>) +} c_TSTNode_t; + +typedef struct { + c_TSTNode_t* root; // Reference root pointer of the TST structure capsule + c_size_t size; // Total count of distinct key-value pairs stored inside the TST +} c_TST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TST_Init(c_TST_t* self); +void c_TST_Destroy(c_TST_t* self); + +/** + * Insert or update a string key mapped to a generic value pointer inside the table + */ +c_err_t c_TST_Put(c_TST_t* self, const char* key, void* value); + +/** + * Retrieve the generic client value pointer mapped to a string key + * @return The stored value address, or NULL if the key does not exist + */ +void* c_TST_Get(c_TST_t* self, const char* key); + +/** + * Check if the TST contains a matching entry for a specific string key + */ +c_bool_t c_TST_Contains(c_TST_t* self, const char* key); + +/** + * Gather all keys currently matching a specific character prefix layout string + * @param result An initialized c_StringList container to append the extracted string records + */ +c_err_t c_TST_KeysWithPrefix(c_TST_t* self, const char* prefix, c_StringList* result); + +/** + * Gather all keys currently matching a specific wildcard pattern string (where '.' matches any character) + * @param pattern String pattern containing characters and '.' wildcards + * @param result An initialized c_StringList container to append the extracted string records + */ +c_err_t c_TST_KeysThatMatch(c_TST_t* self, const char* pattern, c_StringList* result); + +/** + * Find the longest key registered in the TST that is a prefix of the query string. + * For example, if "a", "app", and "apple" are in the TST, LongestPrefixOf("applepie") returns "apple". + * + * @param query The source text string to analyze + * @return + * - A dynamically allocated copy of the longest matching prefix string (managed via C_ALLOC, caller frees) + * - An empty string copy "" if no prefix is matched + * - NULL if system parameters are invalid + */ +char* c_TST_LongestPrefixOf(c_TST_t* self, const char* query); + +#endif /*INCLUDED_C_TST_H*/ diff --git a/Search/c_TreeMap.c b/Search/c_TreeMap.c new file mode 100644 index 0000000..fc3e8b6 --- /dev/null +++ b/Search/c_TreeMap.c @@ -0,0 +1,365 @@ +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// --- Structural Balancing Primitives --- + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_RotateLeft(c_TMNode_t* h) { + c_TMNode_t* x = h->right; + h->right = x->left; + x->left = h; + x->color = h->color; + h->color = C_TM_RED; + return x; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_RotateRight(c_TMNode_t* h) { + c_TMNode_t* x = h->left; + h->left = x->right; + x->right = h; + x->color = h->color; + h->color = C_TM_RED; + return x; +} + +C_STATIC_FORCE_INLINE +void c_TreeMap_FlipColors(c_TMNode_t* h) { + h->color = !h->color; + if (h->left) h->left->color = !h->left->color; + if (h->right) h->right->color = !h->right->color; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_MoveRedLeft(c_TMNode_t* h) { + c_TreeMap_FlipColors(h); + if (c_TreeMap_IsRed(h->right->left)) { + h->right = c_TreeMap_RotateRight(h->right); + h = c_TreeMap_RotateLeft(h); + c_TreeMap_FlipColors(h); + } + return h; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_MoveRedRight(c_TMNode_t* h) { + c_TreeMap_FlipColors(h); + if (c_TreeMap_IsRed(h->left->left)) { + h = c_TreeMap_RotateRight(h); + c_TreeMap_FlipColors(h); + } + return h; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_Balance(c_TMNode_t* h) { + if (c_TreeMap_IsRed(h->right) && !c_TreeMap_IsRed(h->left)) h = c_TreeMap_RotateLeft(h); + if (c_TreeMap_IsRed(h->left) && c_TreeMap_IsRed(h->left->left)) h = c_TreeMap_RotateRight(h); + if (c_TreeMap_IsRed(h->left) && c_TreeMap_IsRed(h->right)) c_TreeMap_FlipColors(h); + return h; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) { + c_TMNode_t* node = (c_TMNode_t*)C_ALLOC(sizeof(c_TMNode_t) + ks + vs); + if (node == NULL) return NULL; + node->left = NULL; + node->right = NULL; + node->color = C_TM_RED; + memcpy(c_TreeMap_NodeKey(node), key, ks); + memcpy(c_TreeMap_NodeVal(node, ks), val, vs); + return node; +} + +static void c_TreeMap_DestroyNodes(c_TMNode_t* node) { + if (node == NULL) return; + c_TreeMap_DestroyNodes(node->left); + c_TreeMap_DestroyNodes(node->right); + C_FREE(node); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeMap_Init(c_TreeMap_t* map, c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)) { + if (map == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM; + map->root = NULL; + map->key_size = key_size; + map->val_size = val_size; + map->size = 0; + map->compar = compar; + return C_ERR_OK; +} + +void c_TreeMap_Destroy(c_TreeMap_t* map) { + if (map) { + c_TreeMap_DestroyNodes(map->root); + map->root = NULL; + map->size = 0; + } +} + +c_bool_t c_TreeMap_Contains(const c_TreeMap_t* map, const void* key) { + if (map == NULL || key == NULL) return C_FALSE; + c_TMNode_t* curr = map->root; + while (curr != NULL) { + int cmp = map->compar(key, c_TreeMap_NodeKey(curr)); + if (cmp == 0) return C_TRUE; + curr = (cmp < 0) ? curr->left : curr->right; + } + return C_FALSE; +} + +void* c_TreeMap_Get(const c_TreeMap_t* map, const void* key) { + if (map == NULL || key == NULL) return NULL; + c_TMNode_t* curr = map->root; + while (curr != NULL) { + int cmp = map->compar(key, c_TreeMap_NodeKey(curr)); + if (cmp == 0) return c_TreeMap_NodeVal(curr, map->key_size); + curr = (cmp < 0) ? curr->left : curr->right; + } + return NULL; +} + +static c_TMNode_t* c_TreeMap_PutInternal(c_TreeMap_t* map, c_TMNode_t* h, + const void* key, const void* val, c_err_t* err) { + if (h == NULL) { + c_TMNode_t* node = c_TreeMap_CreateNode(key, val, map->key_size, map->val_size); + if (node == NULL) *err = C_ERR_NOMEM; + else map->size++; + return node; + } + + int cmp = map->compar(key, c_TreeMap_NodeKey(h)); + if (cmp < 0) h->left = c_TreeMap_PutInternal(map, h->left, key, val, err); + else if (cmp > 0) h->right = c_TreeMap_PutInternal(map, h->right, key, val, err); + else memcpy(c_TreeMap_NodeVal(h, map->key_size), val, map->val_size); + + return c_TreeMap_Balance(h); +} + +c_err_t c_TreeMap_Put(c_TreeMap_t* map, const void* key, const void* val) { + if (map == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + c_err_t err = C_ERR_OK; + map->root = c_TreeMap_PutInternal(map, map->root, key, val, &err); + if (map->root) map->root->color = C_TM_BLACK; + return err; +} + +static c_TMNode_t* c_TreeMap_DeleteMin(c_TreeMap_t* map, c_TMNode_t* h, c_TMNode_t** out_min) { + if (h->left == NULL) { + *out_min = h; + return NULL; + } + if (!c_TreeMap_IsRed(h->left) && !c_TreeMap_IsRed(h->left->left)) { + h = c_TreeMap_MoveRedLeft(h); + } + h->left = c_TreeMap_DeleteMin(map, h->left, out_min); + return c_TreeMap_Balance(h); +} + +static c_TMNode_t* c_TreeMap_RemoveInternal(c_TreeMap_t* map, c_TMNode_t* h, const void* key, c_err_t* err) { + if (map->compar(key, c_TreeMap_NodeKey(h)) < 0) { + if (h->left == NULL) { *err = C_ERR_FAIL; return h; } + if (!c_TreeMap_IsRed(h->left) && !c_TreeMap_IsRed(h->left->left)) { + h = c_TreeMap_MoveRedLeft(h); + } + h->left = c_TreeMap_RemoveInternal(map, h->left, key, err); + } else { + if (c_TreeMap_IsRed(h->left)) { + h = c_TreeMap_RotateRight(h); + } + if (map->compar(key, c_TreeMap_NodeKey(h)) == 0 && (h->right == NULL)) { + map->size--; + C_FREE(h); + return NULL; + } + if (h->right == NULL) { *err = C_ERR_FAIL; return h; } + if (!c_TreeMap_IsRed(h->right) && !c_TreeMap_IsRed(h->right->left)) { + h = c_TreeMap_MoveRedRight(h); + } + if (map->compar(key, c_TreeMap_NodeKey(h)) == 0) { + c_TMNode_t* successor = NULL; + h->right = c_TreeMap_DeleteMin(map, h->right, &successor); + + successor->left = h->left; + successor->right = h->right; + successor->color = h->color; + + C_FREE(h); + map->size--; + h = successor; + } else { + h->right = c_TreeMap_RemoveInternal(map, h->right, key, err); + } + } + return c_TreeMap_Balance(h); +} + +c_err_t c_TreeMap_Remove(c_TreeMap_t* map, const void* key) { + if (map == NULL || key == NULL) return C_ERR_PARAM; + if (map->root == NULL) return C_ERR_NOTFOUND; + + c_err_t err = C_ERR_OK; + if (!c_TreeMap_IsRed(map->root->left) && !c_TreeMap_IsRed(map->root->right)) { + map->root->color = C_TM_RED; + } + + map->root = c_TreeMap_RemoveInternal(map, map->root, key, &err); + if (map->root) map->root->color = C_TM_BLACK; + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Initialize the TreeMap Key Iterator. + * Performs dynamic heap stack initialization and loads the initial minimum path context. + * + * Time Complexity: O(log n) | Space Complexity: O(log n) heap initialization + */ +c_err_t c_TreeMapKeyIter_Init(c_TreeMapKeyIter_t* iter, const c_TreeMap_t* map) { + if (iter == NULL || map == NULL) return C_ERR_PARAM; + + // Cast away constness to bind to the non-const structural field required for Remove() + iter->map = (c_TreeMap_t*)map; + iter->stack_top = -1; + iter->last_returned = NULL; + + // Safety depth boundary limit (Handles worst-case height for massive LLRB trees) + iter->max_depth = 64; + iter->stack = (c_TMNode_t**)C_ALLOC(iter->max_depth * sizeof(c_TMNode_t*)); + if (iter->stack == NULL) return C_ERR_NOMEM; + + // Load initial lookup vector matching the minimum starting key node context + c_TMNode_t* curr = map->root; + while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + iter->stack[++iter->stack_top] = curr; + curr = curr->left; + } + + return C_ERR_OK; +} + +/** + * Clean up allocations within the context wrapper safely. + * Resets tracking registers to guard against dangling usage. + */ +void c_TreeMapKeyIter_Destroy(c_TreeMapKeyIter_t* iter) { + if (iter) { + C_FREE(iter->stack); + iter->stack_top = -1; + iter->max_depth = 0; + iter->last_returned = NULL; + iter->map = NULL; + } +} + +/** + * Evaluates whether any keys remain unread inside the look-ahead pipeline. + */ +c_bool_t c_TreeMapKeyIter_HasNext(const c_TreeMapKeyIter_t* iter) { + if (iter == NULL || iter->stack == NULL) return C_FALSE; + return iter->stack_top >= 0; +} + +/** + * Retrieve a reference pointer to the key most recently extracted by Next(). + * + * Time Complexity: O(1) constant runtime overhead + */ +void* c_TreeMapKeyIter_Get(c_TreeMapKeyIter_t* iter) { + if (iter == NULL || iter->stack==NULL || iter->stack_top<0) return NULL; + c_TMNode_t* node = iter->stack[iter->stack_top]; + iter->last_returned = c_TreeMap_NodeKey(node); + return iter->last_returned; +} + +/** + * Extracts a pointer to the next consecutive key in sorted order. + * Updates internal path registers to step along the sequence. + */ +void* c_TreeMapKeyIter_Next(c_TreeMapKeyIter_t* iter) { + if (iter == NULL || iter->stack_top < 0 || iter->stack == NULL) return NULL; + + // Pop the current minimal node out of the active stack frame + c_TMNode_t* node = iter->stack[iter->stack_top--]; + iter->last_returned = c_TreeMap_NodeKey(node); + + // If a right subtree exists, loop down its left-most branches + c_TMNode_t* curr = node->right; + while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + iter->stack[++iter->stack_top] = curr; + curr = curr->left; + } + + return iter->last_returned; +} + +/** + * High-performance companion helper to reconstruct dynamic stack positions + * back down to a specified target key without memory leaks. + */ +static void c_TreeMapKeyIter_RebuildDynamicStack(c_TreeMapKeyIter_t* iter, c_TMNode_t* node, const void* target_key) { + while (node != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + int cmp = iter->map->compar(target_key, c_TreeMap_NodeKey(node)); + if (cmp < 0) { + iter->stack[++iter->stack_top] = node; + node = node->left; + } else if (cmp > 0) { + node = node->right; + } else { + iter->stack[++iter->stack_top] = node; + break; + } + } +} + +/** + * Stateful Removal Execution Engine. + * Safely handles LLRB tree balancing modifications and heals stack tracking frames in O(log n). + */ +c_err_t c_TreeMapKeyIter_Remove(c_TreeMapKeyIter_t* iter) { + if (iter == NULL || iter->map == NULL || iter->stack == NULL) return C_ERR_PARAM; + if (iter->last_returned == NULL) return C_ERR_FAIL; // Guard against double-deletion/unstarted cursor + + c_bool_t has_next = (iter->stack_top >= 0) ? C_TRUE : C_FALSE; + c_size_t ks = iter->map->key_size; + + // Use a stack-allocated cache buffer to avoid dynamic allocation penalties during deletion hotpaths + #define TRANS_LIMIT 64 + char backup_buffer[TRANS_LIMIT]; + void* next_key_backup = NULL; + + if (has_next) { + next_key_backup = (ks <= TRANS_LIMIT) ? (void*)backup_buffer : C_ALLOC(ks); + if (next_key_backup == NULL) return C_ERR_NOMEM; + memcpy(next_key_backup, c_TreeMap_NodeKey(iter->stack[iter->stack_top]), ks); + } + + // Perform the actual LLRB tree element removal balancing routine + c_err_t err = c_TreeMap_Remove(iter->map, iter->last_returned); + if (err != C_ERR_OK) { + if (has_next && ks > TRANS_LIMIT) C_FREE(next_key_backup); + return err; + } + + iter->last_returned = NULL; // Clear tracking state to prevent invalid double-delete calls + iter->stack_top = -1; // Flush old stack frames corrupted by tree rotations + + // Rebuild the path map using the new root context down to our tracked lookahead key + if (has_next && iter->map->root != NULL) { + c_TreeMapKeyIter_RebuildDynamicStack(iter, iter->map->root, next_key_backup); + if (ks > TRANS_LIMIT) C_FREE(next_key_backup); + } + + #undef TRANS_LIMIT + return C_ERR_OK; +} + + diff --git a/Search/c_TreeMap.h b/Search/c_TreeMap.h new file mode 100644 index 0000000..0dbf7d3 --- /dev/null +++ b/Search/c_TreeMap.h @@ -0,0 +1,82 @@ +#ifndef INCLUDED_C_TREEMAP_H +#define INCLUDED_C_TREEMAP_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Link Color Definitions +typedef enum { + C_TM_BLACK = 0, + C_TM_RED = 1 +} c_TMColor_t; + +// TreeMap Inlined Node Layout Configuration +typedef struct c_TMNode { + struct c_TMNode* left; + struct c_TMNode* right; + c_TMColor_t color; + // Payload layout: key block followed immediately by the value block in memory +} c_TMNode_t; + +// TreeMap Context Structure +typedef struct { + c_TMNode_t* root; + c_size_t key_size; + c_size_t val_size; + c_size_t size; + int (*compar)(const void*, const void*); +} c_TreeMap_t; + +typedef struct { + c_TreeMap_t* map; // Non-const to allow operations on the backing collection + c_TMNode_t** stack; // Dynamic lookup-vector tracking block + long long stack_top; // Explicit tracking index pointer limits + c_size_t max_depth; // Safety boundary memory cushion + void* last_returned; // Pointer tracking the key returned by the most recent Next() call +} c_TreeMapKeyIter_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// --- Internal Helper Accessors --- +C_STATIC_FORCE_INLINE void* c_TreeMap_NodeKey(c_TMNode_t* node) { + return (void*)((char*)node + sizeof(c_TMNode_t)); +} + +C_STATIC_FORCE_INLINE void* c_TreeMap_NodeVal(c_TMNode_t* node, c_size_t key_size) { + return (void*)((char*)node + sizeof(c_TMNode_t) + key_size); +} + +C_STATIC_FORCE_INLINE c_bool_t c_TreeMap_IsRed(c_TMNode_t* node) { + if (node == NULL) return C_FALSE; + return node->color == C_TM_RED; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeMap_Init(c_TreeMap_t* map, c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)); +void c_TreeMap_Destroy(c_TreeMap_t* map); + +c_bool_t c_TreeMap_Contains(const c_TreeMap_t* map, const void* key); +void* c_TreeMap_Get(const c_TreeMap_t* map, const void* key); +c_err_t c_TreeMap_Put(c_TreeMap_t* map, const void* key, const void* val); +c_err_t c_TreeMap_Remove(c_TreeMap_t* map, const void* key); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeMapKeyIter_Init(c_TreeMapKeyIter_t* iter, const c_TreeMap_t* map); +void c_TreeMapKeyIter_Destroy(c_TreeMapKeyIter_t* iter); +c_bool_t c_TreeMapKeyIter_HasNext(const c_TreeMapKeyIter_t* iter); +void* c_TreeMapKeyIter_Get(c_TreeMapKeyIter_t* iter); +void* c_TreeMapKeyIter_Next(c_TreeMapKeyIter_t* iter); +c_err_t c_TreeMapKeyIter_Remove(c_TreeMapKeyIter_t* iter); + + +#endif /*INCLUDED_C_TREEMAP_H*/ diff --git a/Search/c_TreeSet.c b/Search/c_TreeSet.c new file mode 100644 index 0000000..767384f --- /dev/null +++ b/Search/c_TreeSet.c @@ -0,0 +1,347 @@ +#include +#include + +// --- Structural Balancing Primitives --- + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_RotateLeft(c_TSNode_t* h) { + c_TSNode_t* x = h->right; + h->right = x->left; + x->left = h; + x->color = h->color; + h->color = C_TS_RED; + return x; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_RotateRight(c_TSNode_t* h) { + c_TSNode_t* x = h->left; + h->left = x->right; + x->right = h; + x->color = h->color; + h->color = C_TS_RED; + return x; +} + +C_STATIC_FORCE_INLINE +void c_TreeSet_FlipColors(c_TSNode_t* h) { + h->color = !h->color; + if (h->left) h->left->color = !h->left->color; + if (h->right) h->right->color = !h->right->color; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_MoveRedLeft(c_TSNode_t* h) { + c_TreeSet_FlipColors(h); + if (c_TreeSet_IsRed(h->right->left)) { + h->right = c_TreeSet_RotateRight(h->right); + h = c_TreeSet_RotateLeft(h); + c_TreeSet_FlipColors(h); + } + return h; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_MoveRedRight(c_TSNode_t* h) { + c_TreeSet_FlipColors(h); + if (c_TreeSet_IsRed(h->left->left)) { + h = c_TreeSet_RotateRight(h); + c_TreeSet_FlipColors(h); + } + return h; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_Balance(c_TSNode_t* h) { + if (c_TreeSet_IsRed(h->right) && !c_TreeSet_IsRed(h->left)) h = c_TreeSet_RotateLeft(h); + if (c_TreeSet_IsRed(h->left) && c_TreeSet_IsRed(h->left->left)) h = c_TreeSet_RotateRight(h); + if (c_TreeSet_IsRed(h->left) && c_TreeSet_IsRed(h->right)) c_TreeSet_FlipColors(h); + return h; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_CreateNode(const void* element, c_size_t es) { + c_TSNode_t* node = (c_TSNode_t*)C_ALLOC(sizeof(c_TSNode_t) + es); + if (node == NULL) return NULL; + node->left = NULL; + node->right = NULL; + node->color = C_TS_RED; + memcpy(c_TreeSet_NodeKey(node), element, es); + return node; +} + +static void c_TreeSet_DestroyNodes(c_TSNode_t* node) { + if (node == NULL) return; + c_TreeSet_DestroyNodes(node->left); + c_TreeSet_DestroyNodes(node->right); + C_FREE(node); +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeSet_Init(c_TreeSet_t* set, c_size_t element_size, int (*compar)(const void*, const void*)) { + if (set == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM; + set->root = NULL; + set->element_size = element_size; + set->size = 0; + set->compar = compar; + + return C_ERR_OK; +} + +void c_TreeSet_Destroy(c_TreeSet_t* set) { + if (set) { + c_TreeSet_DestroyNodes(set->root); + set->root = NULL; + set->size = 0; + } +} + +c_bool_t c_TreeSet_Contains(const c_TreeSet_t* set, const void* element) { + if (set == NULL || element == NULL) return C_FALSE; + c_TSNode_t* curr = set->root; + while (curr != NULL) { + int cmp = set->compar(element, c_TreeSet_NodeKey(curr)); + if (cmp == 0) return C_TRUE; + curr = (cmp < 0) ? curr->left : curr->right; + } + return C_FALSE; +} + +static c_TSNode_t* c_TreeSet_AddInternal(c_TreeSet_t* set, c_TSNode_t* h, const void* element, c_err_t* err) { + if (h == NULL) { + c_TSNode_t* node = c_TreeSet_CreateNode(element, set->element_size); + if (node == NULL) *err = C_ERR_NOMEM; + else set->size++; + return node; + } + + int cmp = set->compar(element, c_TreeSet_NodeKey(h)); + if (cmp < 0) h->left = c_TreeSet_AddInternal(set, h->left, element, err); + else if (cmp > 0) h->right = c_TreeSet_AddInternal(set, h->right, element, err); + else *err = C_ERR_ALREADY_EXISTS; // Set constraint violation: duplicates forbidden + + return c_TreeSet_Balance(h); +} + +c_err_t c_TreeSet_Add(c_TreeSet_t* set, const void* element) { + if (set == NULL || element == NULL) return C_ERR_PARAM; + c_err_t err = C_ERR_OK; + set->root = c_TreeSet_AddInternal(set, set->root, element, &err); + if (set->root) set->root->color = C_TS_BLACK; + return err; +} + +static c_TSNode_t* c_TreeSet_DeleteMin(c_TreeSet_t* set, c_TSNode_t* h, c_TSNode_t** out_min) { + if (h->left == NULL) { + *out_min = h; + return NULL; + } + if (!c_TreeSet_IsRed(h->left) && !c_TreeSet_IsRed(h->left->left)) { + h = c_TreeSet_MoveRedLeft(h); + } + h->left = c_TreeSet_DeleteMin(set, h->left, out_min); + return c_TreeSet_Balance(h); +} + +static c_TSNode_t* c_TreeSet_RemoveInternal(c_TreeSet_t* set, c_TSNode_t* h, const void* element, c_err_t* err) { + if (set->compar(element, c_TreeSet_NodeKey(h)) < 0) { + if (h->left == NULL) { *err = C_ERR_FAIL; return h; } + if (!c_TreeSet_IsRed(h->left) && !c_TreeSet_IsRed(h->left->left)) { + h = c_TreeSet_MoveRedLeft(h); + } + h->left = c_TreeSet_RemoveInternal(set, h->left, element, err); + } else { + if (c_TreeSet_IsRed(h->left)) { + h = c_TreeSet_RotateRight(h); + } + if (set->compar(element, c_TreeSet_NodeKey(h)) == 0 && (h->right == NULL)) { + set->size--; + C_FREE(h); + return NULL; + } + if (h->right == NULL) { *err = C_ERR_FAIL; return h; } + if (!c_TreeSet_IsRed(h->right) && !c_TreeSet_IsRed(h->right->left)) { + h = c_TreeSet_MoveRedRight(h); + } + if (set->compar(element, c_TreeSet_NodeKey(h)) == 0) { + c_TSNode_t* successor = NULL; + h->right = c_TreeSet_DeleteMin(set, h->right, &successor); + + successor->left = h->left; + successor->right = h->right; + successor->color = h->color; + + C_FREE(h); + set->size--; + h = successor; + } else { + h->right = c_TreeSet_RemoveInternal(set, h->right, element, err); + } + } + return c_TreeSet_Balance(h); +} + +c_err_t c_TreeSet_Remove(c_TreeSet_t* set, const void* element) { + if (set == NULL || element == NULL) return C_ERR_PARAM; + if (set->root == NULL) return C_ERR_FAIL; + + c_err_t err = C_ERR_OK; + if (!c_TreeSet_IsRed(set->root->left) && !c_TreeSet_IsRed(set->root->right)) { + set->root->color = C_TS_RED; + } + + set->root = c_TreeSet_RemoveInternal(set, set->root, element, &err); + if (set->root) set->root->color = C_TS_BLACK; + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ +/** + * High-performance helper to reconstruct dynamic stack positions + * back down to a specified target key without memory leaks. + */ +C_STATIC_FORCE_INLINE +void c_TreeSetIter_RebuildDynamicStack(c_TreeSetIter_t* iter, c_TSNode_t* node, const void* target_key) { + while (node != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + int cmp = iter->set->compar(target_key, c_TreeSet_NodeKey(node)); + if (cmp < 0) { + iter->stack[++iter->stack_top] = node; + node = node->left; + } else if (cmp > 0) { + node = node->right; + } else { + iter->stack[++iter->stack_top] = node; + break; + } + } +} + + +/** + * Initialize the dynamic lookup-vector tracking iterator context. + * Computes initial left-most branching bounds down to the minimal key node. + * + * Time Complexity: O(log n) | Space Complexity: O(log n) heap initialization + */ +c_err_t c_TreeSetIter_Init(c_TreeSetIter_t* iter, const c_TreeSet_t* set) { + if (iter == NULL || set == NULL) return C_ERR_PARAM; + + // Cast away constness to bind to the non-const structural field required for Remove() + iter->set = (c_TreeSet_t*)set; + iter->stack_top = -1; + iter->last_returned = NULL; + + // Safety depth boundary limit (Handles worst-case height for massive LLRB trees) + iter->max_depth = 64; + iter->stack = (c_TSNode_t**)C_ALLOC(iter->max_depth * sizeof(c_TSNode_t*)); + if (iter->stack == NULL) return C_ERR_NOMEM; + + // Load initial lookup vector matching the minimum starting key node context + c_TSNode_t* curr = set->root; + while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + iter->stack[++iter->stack_top] = curr; + curr = curr->left; + } + + return C_ERR_OK; +} + +/** + * Lifecycle Management: Free allocated structural tracking path arrays. + */ +void c_TreeSetIter_Destroy(c_TreeSetIter_t* iter) { + if (iter) { + C_FREE(iter->stack); + iter->stack_top = -1; + iter->max_depth = 0; + iter->last_returned = NULL; + iter->set = NULL; + } +} + +/** + * Evaluates whether any element remains unread inside the look-ahead pipeline. + */ +c_bool_t c_TreeSetIter_HasNext(const c_TreeSetIter_t* iter) { + if (iter == NULL || iter->stack == NULL) return C_FALSE; + return iter->stack_top >= 0; +} + +/** + * Extracts a pointer to the next consecutive element in sorted order. + * Updates internal path registers to step along the sequence. + * @return void* pointer to the key payload region, or NULL if empty/exhausted. + */ +void* c_TreeSetIter_Next(c_TreeSetIter_t* iter) { + if (iter == NULL || iter->stack_top < 0 || iter->stack == NULL) return NULL; + + // Pop the current minimal node out of the active stack frame + c_TSNode_t* node = iter->stack[iter->stack_top--]; + iter->last_returned = c_TreeSet_NodeKey(node); + + // If a right subtree exists, it holds the next sequence elements. + // Shift tracking focus down over that node's leftmost boundary path. + c_TSNode_t* curr = node->right; + while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + iter->stack[++iter->stack_top] = curr; + curr = curr->left; + } + + return iter->last_returned; +} + +/** + * Safely removes the element most recently returned by c_TreeSetIter_Next(). + * Re-synchronizes structural lookup maps dynamically post-balance rotation shifts. + * + * Time Complexity: O(log n) | Call Stack: O(1) in-place + * @return C_ERR_OK if successful, or C_ERR_INVALID if invalid iterator state sequence. + */ +c_err_t c_TreeSetIter_Remove(c_TreeSetIter_t* iter) { + if (iter == NULL || iter->set == NULL || iter->stack == NULL) return C_ERR_PARAM; + if (iter->last_returned == NULL) return C_ERR_FAIL; // Guard against double-deletion/unstarted cursor + + c_bool_t has_next = (iter->stack_top >= 0) ? C_TRUE : C_FALSE; + c_size_t es = iter->set->element_size; + + // Use a stack-allocated cache buffer to avoid dynamic allocation penalties during deletion hotpaths +#define TRANS_LIMIT 64 + char backup_buffer[TRANS_LIMIT]; + void* next_key_backup = NULL; + + if (has_next) { + next_key_backup = (es <= TRANS_LIMIT) ? (void*)backup_buffer : C_ALLOC(es); + if (next_key_backup == NULL) return C_ERR_NOMEM; + memcpy(next_key_backup, c_TreeSet_NodeKey(iter->stack[iter->stack_top]), es); + } + + // Perform the actual LLRB tree element removal balancing routine + c_err_t err = c_TreeSet_Remove(iter->set, iter->last_returned); + if (err != C_ERR_OK) { + if (has_next && es > TRANS_LIMIT) C_FREE(next_key_backup); + return err; + } + + iter->last_returned = NULL; // Clear tracking state to prevent invalid double-delete calls + iter->stack_top = -1; // Flush old stack frames corrupted by tree rotations + + // Rebuild the path map using the new root context down to our tracked lookahead key + if (has_next && iter->set->root != NULL) { + c_TreeSetIter_RebuildDynamicStack(iter, iter->set->root, next_key_backup); + if (es > TRANS_LIMIT) C_FREE(next_key_backup); + } + +#undef TRANS_LIMIT + return C_ERR_OK; +} + +void* c_TreeSetIter_Get(c_TreeSetIter_t* iter) { + if (iter == NULL || iter->stack==NULL || iter->stack_top<0) return NULL; + c_TSNode_t* node = iter->stack[iter->stack_top]; + iter->last_returned = c_TreeSet_NodeKey(node); + return iter->last_returned; +} diff --git a/Search/c_TreeSet.h b/Search/c_TreeSet.h new file mode 100644 index 0000000..4929edd --- /dev/null +++ b/Search/c_TreeSet.h @@ -0,0 +1,80 @@ +#ifndef INCLUDED_C_TREESET_H +#define INCLUDED_C_TREESET_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Link Color Definitions +typedef enum { + C_TS_BLACK = 0, + C_TS_RED = 1 +} c_TSColor_t; + +// TreeSet Inlined Node Layout Configuration +typedef struct c_TSNode { + struct c_TSNode* left; + struct c_TSNode* right; + c_TSColor_t color; + // Payload layout: element block resides immediately after this structure in memory +} c_TSNode_t; + +// TreeSet Context Structure +typedef struct { + c_TSNode_t* root; + c_size_t element_size; // Size of each unified unique element in bytes + c_size_t size; // Total number of unique nodes inside the set + int (*compar)(const void*, const void*); // Key comparison rule pointer +} c_TreeSet_t; + +typedef struct { + c_TreeSet_t* set; // Modified to non-const to allow operations on the set + c_TSNode_t** stack; + long long stack_top; + c_size_t max_depth; + void* last_returned; // Pointer tracking the key returned by the most recent Next() call +} c_TreeSetIter_t; + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// --- Internal Helper Accessors --- +C_STATIC_FORCE_INLINE +void* c_TreeSet_NodeKey(c_TSNode_t* node) { + if (node == NULL) return NULL; + return (void*)((char*)node + sizeof(c_TSNode_t)); +} + +C_STATIC_FORCE_INLINE +c_bool_t c_TreeSet_IsRed(c_TSNode_t* node) { + if (node == NULL) return C_FALSE; + return node->color == C_TS_RED; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeSet_Init(c_TreeSet_t* set, c_size_t element_size, int (*compar)(const void*, const void*)); +void c_TreeSet_Destroy(c_TreeSet_t* set); + +c_bool_t c_TreeSet_Contains(const c_TreeSet_t* set, const void* element); +c_err_t c_TreeSet_Add(c_TreeSet_t* set, const void* element); +c_err_t c_TreeSet_Remove(c_TreeSet_t* set, const void* element); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeSetIter_Init(c_TreeSetIter_t* iter, const c_TreeSet_t* set); +void c_TreeSetIter_Destroy(c_TreeSetIter_t* iter); +c_bool_t c_TreeSetIter_HasNext(const c_TreeSetIter_t* iter); +void* c_TreeSetIter_Next(c_TreeSetIter_t* iter); +void* c_TreeSetIter_Get(c_TreeSetIter_t* iter); +c_err_t c_TreeSetIter_Remove(c_TreeSetIter_t* iter); + + + +#endif /*INCLUDED_C_TREESET_H*/ diff --git a/Search/c_Trie.c b/Search/c_Trie.c new file mode 100644 index 0000000..f3ebfe4 --- /dev/null +++ b/Search/c_Trie.c @@ -0,0 +1,320 @@ +#include +#include +#include +#include "c_StringBuffer.h" + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/* Internal constructor helper to build an isolated trie node capsule */ +C_STATIC_FORCE_INLINE +c_TrieNode_t* c_TrieNode_Create(void) { + c_TrieNode_t* node = (c_TrieNode_t*)C_CALLOC(1, sizeof(c_TrieNode_t)); + return node; // C_CALLOC initializes all children nodes inside next[] to NULL +} + +/* Internal destructor helper to clear trie nodes post-order non-recursively using an explicit stack */ +static void c_TrieNode_DestroyRecursive(c_TrieNode_t* root) { + if (!root) return; + + // Explicit tree-cleanup stack configuration limits space constraints safely + c_ArrayStack_t node_stack; + c_ArrayStack_Init(&node_stack, sizeof(c_TrieNode_t*), 4); + c_ArrayStack_Push(&node_stack, &root); + + while (!c_ArrayStack_IsEmpty(&node_stack)) { + c_TrieNode_t* curr = 0; + c_err_t err = c_ArrayStack_Pop(&node_stack, &curr); + c_bool_t has_children = C_FALSE; + + for (int i = 0; i < C_TRIE_R; i++) { + if (curr->next[i]) { + c_ArrayStack_Push(&node_stack, &curr->next[i]); + curr->next[i] = NULL; // Break loop linkage to track post-order cleanup processing + has_children = C_TRUE; + break; + } + } + + if (has_children == C_FALSE) { + C_FREE(curr); + } + } + c_ArrayStack_Destroy(&node_stack); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_Trie_Init(c_Trie_t* self) { + if (!self) return C_ERR_PARAM; + self->root = c_TrieNode_Create(); + self->size = 0; + return self->root ? C_ERR_OK : C_ERR_NOMEM; +} + +void c_Trie_Destroy(c_Trie_t* self) { + if (!self) return; + c_TrieNode_DestroyRecursive(self->root); + self->root = NULL; + self->size = 0; +} + +c_err_t c_Trie_Put(c_Trie_t* self, const char* key, void* value) { + if (!self || !key) return C_ERR_PARAM; + if (!self->root) { + self->root = c_TrieNode_Create(); + if (!self->root) return C_ERR_NOMEM; + } + + c_TrieNode_t* curr = self->root; + c_size_t len = strlen(key); + + for (c_size_t i = 0; i < len; i++) { + unsigned char c = (unsigned char)key[i]; + if (!curr->next[c]) { + curr->next[c] = c_TrieNode_Create(); + if (!curr->next[c]) return C_ERR_NOMEM; + } + curr = curr->next[c]; + } + + if (curr->value == NULL && value != NULL) { + self->size++; + } else if (curr->value != NULL && value == NULL) { + self->size--; + } + + curr->value = value; + return C_ERR_OK; +} + +void* c_Trie_Get(c_Trie_t* self, const char* key) { + if (!self || !key || !self->root) return NULL; + + c_TrieNode_t* curr = self->root; + c_size_t len = strlen(key); + + for (c_size_t i = 0; i < len; i++) { + unsigned char c = (unsigned char)key[i]; + curr = curr->next[c]; + if (!curr) return NULL; + } + return curr->value; +} + +c_bool_t c_Trie_Contains(c_Trie_t* self, const char* key) { + return (c_Trie_Get(self, key) != NULL) ? C_TRUE : C_FALSE; +} + +/* Internal recursive worker to support automated character branch purging on node deletions */ +static c_TrieNode_t* c_Trie_DeleteWorker(c_TrieNode_t* x, const char* key, c_size_t d, c_bool_t* out_deleted, c_size_t* size_ref) { + if (!x) return NULL; + + if (d == strlen(key)) { + if (x->value != NULL) { + x->value = NULL; + (*size_ref)--; + *out_deleted = C_TRUE; + } + } else { + unsigned char c = (unsigned char)key[d]; + x->next[c] = c_Trie_DeleteWorker(x->next[c], key, d + 1, out_deleted, size_ref); + } + + // Clean up empty nodes dynamically: if this node holds a value or has other sub-branches, preserve it + if (x->value != NULL) return x; + for (int c = 0; c < C_TRIE_R; c++) { + if (x->next[c] != NULL) return x; + } + + // Completely orphaned branch slot achieved; purge memory to prevent layout leaks + C_FREE(x); + return NULL; +} + +c_err_t c_Trie_Delete(c_Trie_t* self, const char* key) { + if (!self || !key || !self->root) return C_ERR_PARAM; + c_bool_t deleted = C_FALSE; + self->root = c_Trie_DeleteWorker(self->root, key, 0, &deleted, &(self->size)); + return deleted ? C_ERR_OK : C_ERR_PARAM; +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +static void c_Trie_CollectWorker(c_TrieNode_t* x, c_StringBuffer_t* sb, c_size_t depth, c_StringList* result) { + if (!x) return; + + // 1. If an active data payload value exists, export a snapshot copy directly to your StringList + if (x->value != NULL) { + // Retrieve the current continuous null-terminated string handle from the buffer wrapper + // const char* completed_string = c_StringBuffer_CStr(sb); + c_StringList_Append(result, sb->buffer); + } + + // 2. Iterate sequentially through all possible child alphabet pathways + for (int c = 0; c < C_TRIE_R; c++) { + if (x->next[c]) { + // Append the edge character representation directly onto your builder stack frame + char character_token = (char)c; + c_StringBuffer_Append(sb, &character_token, 1); + + // Descend recursively down to explore child nodes + c_Trie_CollectWorker(x->next[c], sb, depth + 1, result); + + /* + * 🛡️ BACKTRACKING STRING INVARIANT: + * When winding back up a call frame stack, we must pop/truncate the last character + * from your string buffer to restore the parent prefix context state cleanly. + */ + c_StringBuffer_SetLength(sb, depth); + } + } +} + +c_err_t c_Trie_KeysWithPrefix(c_Trie_t* self, const char* prefix, c_StringList* result) { + if (!self || !prefix || !result) return C_ERR_PARAM; + + c_TrieNode_t* curr = self->root; + c_size_t len = strlen(prefix); + for (c_size_t i = 0; i < len; i++) { + unsigned char c = (unsigned char)prefix[i]; + curr = curr->next[c]; + if (!curr) return C_ERR_OK; + } + + c_StringBuffer_t sb; + if (c_StringBuffer_Init(&sb, len+256) != C_ERR_OK) { + return C_ERR_NOMEM; + } + + c_StringBuffer_Append(&sb, (const char*)prefix, len); + + c_Trie_CollectWorker(curr, &sb, len, result); + + c_StringBuffer_Destroy(&sb); + + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/* Internal recursive matching collector worker */ +static void c_Trie_MatchWorker(c_TrieNode_t* x, c_StringBuffer_t* sb, const char* pattern, c_size_t depth, c_StringList* result) { + if (!x) return; + + c_size_t pattern_len = strlen(pattern); + + // Invariant Guard: If we have reached the pattern length, check for an active terminal value payload + if (depth == pattern_len) { + if (x->value != NULL) { + c_StringList_Append(result, sb->buffer); + } + return; + } + + unsigned char c = (unsigned char)pattern[depth]; + + // Case A: The active cursor encounters the dot wildcard character ('.') + if (c == '.') { + for (int next_char = 0; next_char < C_TRIE_R; next_char++) { + if (x->next[next_char]) { + char token = (char)next_char; + c_StringBuffer_Append(sb, &token, 1); + + c_Trie_MatchWorker(x->next[next_char], sb, pattern, depth + 1, result); + + // Backtracking unwinding step: reset logical buffer length context + c_StringBuffer_SetLength(sb, depth); + } + } + } + // Case B: Explicit character absolute matching step + else { + if (x->next[c]) { + char token = (char)c; + c_StringBuffer_Append(sb, &token, 1); + + c_Trie_MatchWorker(x->next[c], sb, pattern, depth + 1, result); + + // Backtracking unwinding step: reset logical buffer length context + c_StringBuffer_SetLength(sb, depth); + } + } +} + +/** + * Gather all keys currently matching a specific wildcard pattern string + */ +c_err_t c_Trie_KeysThatMatch(c_Trie_t* self, const char* pattern, c_StringList* result) { + if (!self || !pattern || !result || !self->root) { + return C_ERR_PARAM; + } + + c_StringBuffer_t sb; + if (c_StringBuffer_Init(&sb, 256) != C_ERR_OK) { + return C_ERR_NOMEM; + } + + // Start crawling the trie from root utilizing our string buffer builder + c_Trie_MatchWorker(self->root, &sb, pattern, 0, result); + + c_StringBuffer_Destroy(&sb); + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * Find the longest key registered in the Trie that is a prefix of the query string. + */ +char* c_Trie_LongestPrefixOf(c_Trie_t* self, const char* query) { + if (!self || !query || !self->root) { + return NULL; + } + + c_TrieNode_t* curr = self->root; + c_size_t query_len = strlen(query); + c_size_t longest_match_len = 0; + c_bool_t match_found = C_FALSE; + + // Iterate through characters of the query string sequentially + for (c_size_t i = 0; i < query_len; i++) { + unsigned char c = (unsigned char)query[i]; + curr = curr->next[c]; + + // If the path breaks, stop the search + if (!curr) { + break; + } + + // If this intermediate node marks a complete registered key, record its length + if (curr->value != NULL) { + longest_match_len = i + 1; + match_found = C_TRUE; + } + } + + // Allocate an isolated heap buffer to hold the output copy string + c_size_t output_bytes = match_found ? (longest_match_len + 1) : 1; + char* result_str = (char*)C_ALLOC(output_bytes); + if (!result_str) { + return NULL; + } + + if (match_found == C_TRUE) { + memcpy(result_str, query, longest_match_len); + result_str[longest_match_len] = '\0'; + } else { + result_str[0] = '\0'; // Return a clean empty string if no prefix matches + } + + return result_str; +} diff --git a/Search/c_Trie.h b/Search/c_Trie.h new file mode 100644 index 0000000..6c14365 --- /dev/null +++ b/Search/c_Trie.h @@ -0,0 +1,82 @@ +#ifndef INCLUDED_C_TRIE_H +#define INCLUDED_C_TRIE_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_STRINGLIST_H +#include +#endif /*INCLUDED_C_STRINGLIST_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_TRIE_R 256 + + +typedef struct c_TrieNode { + void* value; // Generic client value pointer associated with a complete key string + struct c_TrieNode* next[C_TRIE_R]; // Flat array of child node pointers mapping to character offsets +} c_TrieNode_t; + +typedef struct { + c_TrieNode_t* root; // Reference root pointer of the trie structure capsule + c_size_t size; // Total count of distinct key-value pairs stored inside the trie +} c_Trie_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_Trie_Init(c_Trie_t* self); + +void c_Trie_Destroy(c_Trie_t* self); + +/** + * Insert or update a string key mapped to a generic value pointer inside the table + */ +c_err_t c_Trie_Put(c_Trie_t* self, const char* key, void* value); + +/** + * Retrieve the generic client value pointer mapped to a string key + * @return The stored value address, or NULL if the key does not exist + */ +void* c_Trie_Get(c_Trie_t* self, const char* key); + +/** + * Check if the trie contains a matching entry for a specific string key + */ +c_bool_t c_Trie_Contains(c_Trie_t* self, const char* key); + +/** + * Remove a key-value mapping from the trie table. Cleans up orphaned down-stream nodes automatically. + */ +c_err_t c_Trie_Delete(c_Trie_t* self, const char* key); + +/** + * Gather all keys currently matching a specific character prefix layout string + * @param result An initialized c_StringList container to append the extracted string records + */ +c_err_t c_Trie_KeysWithPrefix(c_Trie_t* self, const char* prefix, c_StringList* result); + +/** + * Gather all keys currently matching a specific wildcard pattern string (where '.' matches any character) + * @param pattern String pattern containing characters and '.' wildcards + * @param result An initialized c_StringList container to append the extracted string records + */ +c_err_t c_Trie_KeysThatMatch(c_Trie_t* self, const char* pattern, c_StringList* result); + +/** + * Find the longest key registered in the Trie that is a prefix of the query string. + * For example, if "a", "app", and "apple" are in the Trie, LongestPrefixOf("applepie") returns "apple". + * + * @param query The source text string to analyze + * @return + * - A dynamically allocated copy of the longest matching prefix string (managed via C_ALLOC, caller frees) + * - An empty string copy "" if no prefix is matched + * - NULL if system parameters are invalid + */ +char* c_Trie_LongestPrefixOf(c_Trie_t* self, const char* query); + +#endif /*INCLUDED_C_TRIE_H*/ diff --git a/Sort/c_BinaryInsertionSort.c b/Sort/c_BinaryInsertionSort.c new file mode 100644 index 0000000..209282d --- /dev/null +++ b/Sort/c_BinaryInsertionSort.c @@ -0,0 +1 @@ +#include diff --git a/Sort/c_BinaryInsertionSort.h b/Sort/c_BinaryInsertionSort.h new file mode 100644 index 0000000..e67e4fb --- /dev/null +++ b/Sort/c_BinaryInsertionSort.h @@ -0,0 +1,79 @@ +#ifndef INCLUDED_C_BINARYINSERTIONSORT_H +#define INCLUDED_C_BINARYINSERTIONSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * 通用折半插入排序函数 + * @param base 指向待排序数组首元素的指针 + * @param num 数组中元素的个数 + * @param size 每个元素的大小(字节数) + * @param compar 指向比较函数的指针 + */ +C_STATIC_FORCE_INLINE +void c_BinaryInsertionSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + char* arr = (char*)base; // 强转为 char* 以便按单字节进行指针偏移 + + // 分配一块临时内存,用于存放当前要插入的“哨兵”元素(temp) +#define STACK_LIMIT 128 + char stack_buf[STACK_LIMIT]; + void* temp = NULL; + + if (size <= STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + for (c_size_t i = 1; i < num; i++) { + // temp = arr[i]:备份当前要插入的元素 + memcpy(temp, arr + (i * size), size); + + // 1. 使用二分查找决定插入位置 [left, right] + long long left = 0; + long long right = i - 1; + + while (left <= right) { + long long mid = left + (right - left) / 2; + + // 为了保证排序的稳定性(Stability), + // 当 mid 元素等于 temp 时,应当继续向右区间查找,把 temp 放到相同元素的后面 + if (compar(arr + (mid * size), temp) <= 0) { + left = mid + 1; // 目标位置在右边 + } else { + right = mid - 1; // 目标位置在左边 + } + } + // 循环结束时,left 就是元素应该插入的目标索引位置 + + // 2. 将 [left, i-1] 区间的元素全部向后移动一个位置 + for (long long j = i - 1; j >= left; j--) { + memcpy(arr + ((j + 1) * size), arr + (j * size), size); + } + + // 3. 将 temp 插入到腾出来的 left 位置 + memcpy(arr + (left * size), temp, size); + } + + // Only trigger free if it was actually allocated from the heap + if (size > STACK_LIMIT) { + C_FREE(temp); + } +#undef STACK_LIMIT +} + + +#endif /*INCLUDED_C_BINARYINSERTIONSORT_H*/ diff --git a/Sort/c_HeapSort.c b/Sort/c_HeapSort.c new file mode 100644 index 0000000..c4aba39 --- /dev/null +++ b/Sort/c_HeapSort.c @@ -0,0 +1,2 @@ +#include +#include diff --git a/Sort/c_HeapSort.h b/Sort/c_HeapSort.h new file mode 100644 index 0000000..98441a7 --- /dev/null +++ b/Sort/c_HeapSort.h @@ -0,0 +1,103 @@ +#ifndef INCLUDED_C_HEAPSORT_H +#define INCLUDED_C_HEAPSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * Internal macro helper to swap two arbitrary blocks of memory of given size. + */ +C_STATIC_FORCE_INLINE +void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) { + if (a == b) return; + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +/** + * Standard Sift-Down structural loop modified to build/maintain a Max-Heap. + */ +C_STATIC_FORCE_INLINE +void c_Heapify(char* arr, c_size_t num, c_size_t root, c_size_t size, + void* temp, int (*compar)(const void*, const void*)) { + c_int_t current = root; + + while (1) { + c_int_t left_child = (2 * current) + 1; + c_int_t right_child = (2 * current) + 2; + c_int_t largest = current; + + if (left_child < num && + compar(arr + (left_child * size), arr + (largest * size)) > 0) { + largest = left_child; + } + + if (right_child < num && + compar(arr + (right_child * size), arr + (largest * size)) > 0) { + largest = right_child; + } + + if (largest == current) { + break; + } + + c_SwapInternal(arr + (current * size), arr + (largest * size), size, temp); + current = largest; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Top-Level Framework Entry Point for Heapsort. + * Time Complexity: O(n log n) | Space Complexity: O(1) in-place | Stable: No + */ +C_STATIC_FORCE_INLINE +void c_HeapSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + +#define HEAP_STACK_LIMIT 128 + char stack_buf[HEAP_STACK_LIMIT]; + void* temp = (size <= HEAP_STACK_LIMIT) ? stack_buf : C_ALLOC(size); + if (temp == NULL) return; + + // Safely cast size parameters to signed c_int_t variables before starting the algorithm loops + c_int_t total_items = (c_int_t)num; + + // Phase 1: Build the Max-Heap from the bottom up (Floyd's heap construction) + for (c_int_t i = (total_items / 2) - 1; i >= 0; i--) { + c_Heapify(arr, total_items, i, size, temp, compar); + } + + // Phase 2: In-place sorted array extraction + for (c_int_t k = total_items - 1; k > 0; k--) { + // Swap root max element to current end positions + c_SwapInternal(arr, arr + (k * size), size, temp); + + // Re-heapify the remaining sub-heap structure + c_Heapify(arr, k, 0, size, temp, compar); + } + + if (size > HEAP_STACK_LIMIT) { + C_FREE(temp); + } +#undef HEAP_STACK_LIMIT +} + +#endif /*INCLUDED_C_HEAPSORT_H*/ diff --git a/Sort/c_InPlaceMSDRadixSort.c b/Sort/c_InPlaceMSDRadixSort.c new file mode 100644 index 0000000..f12af74 --- /dev/null +++ b/Sort/c_InPlaceMSDRadixSort.c @@ -0,0 +1,105 @@ +#include +#include + +/** + * Inline helper to safely extract a character at string offset d. + * Automatically maps a string's null terminator to a sentinel value of -1. + */ +C_STATIC_FORCE_INLINE +int c_InPlaceMSDRadixSort_CharAt(const char* str, c_size_t d) { + if (str == NULL) return -1; + c_size_t i = 0; + while (i < d && str[i] != '\0') { + i++; + } + if (str[i] == '\0' || i < d) return -1; + return (unsigned char)str[i]; +} + +/** + * Core Private Recursive Sub-partition In-place Sorting Subroutine. + * Employs a localized head/tail lookup permutation ring to operate directly within array slices. + */ +static void c_InPlaceMSDRadixSort_Recursive(char** arr, long long lo, long long hi, c_size_t d, + c_size_t R, c_size_t* count_buf, long long* heads, long long* tails) { + if (hi <= lo) return; + + // Elements are shifted forward by +2 slots to absorb the -1 string end sentinel gracefully + c_size_t total_buckets = R + 2; + memset(count_buf, 0, total_buckets * sizeof(c_size_t)); + + // Pass A: Compute frequency counts for the current digit slice + for (long long i = lo; i <= hi; i++) { + int c = c_InPlaceMSDRadixSort_CharAt(arr[i], d); + count_buf[c + 2]++; + } + + // Pass B: Transform frequencies into absolute head and tail cursor index maps + heads[0] = lo; + tails[0] = lo + (long long)count_buf[0]; + for (c_size_t r = 1; r < total_buckets; r++) { + heads[r] = tails[r - 1]; + tails[r] = heads[r] + (long long)count_buf[r]; + } + + // Pass C: Cyclic Permutation Swap Element Loop (In-Place Distribution) + for (c_size_t r = 0; r < total_buckets; r++) { + while (heads[r] < tails[r]) { + long long curr_idx = heads[r]; + int c = c_InPlaceMSDRadixSort_CharAt(arr[curr_idx], d); + c_size_t bucket = (c_size_t)(c + 2); + + if (bucket == r) { + heads[r]++; // Element is already in its correct bucket, step forward + } else { + // Evict the element to its correct destination bucket via data swap + long long dest_idx = heads[bucket]; + char* temp = arr[curr_idx]; + arr[curr_idx] = arr[dest_idx]; + arr[dest_idx] = temp; + + heads[bucket]++; // Increment the destination bucket's cursor + } + } + } + + // Pass D: Recursively process sub-arrays for each character bucket + // Shorter strings that terminated (sentinel character index 0) do not need deeper processing + long long current_lo = lo + (long long)count_buf[0]; + for (c_size_t r = 1; r < total_buckets; r++) { + long long current_hi = current_lo + (long long)count_buf[r] - 1; + + if (current_hi > current_lo) { + c_InPlaceMSDRadixSort_Recursive(arr, current_lo, current_hi, d + 1, R, count_buf, heads, tails); + } + current_lo = current_hi + 1; + } +} + +/** + * Sorts an array of variable-length strings completely in-place. + * Space Complexity: O(1) Auxiliary Heap memory footprint (Excluding recursive tracking arrays bound to R) + */ +c_err_t c_InPlaceMSDRadixSort_Sort(const c_InPlaceMSDRadixSort_t* sort, char** arr, c_size_t n) { + if (sort == NULL || arr == NULL || sort->R == 0) return C_ERR_PARAM; + if (n <= 1) return C_ERR_OK; + + // Radix-bound tracking buffers are allocated once upfront to eliminate heap overhead in hot loops + c_size_t total_buckets = sort->R + 2; + c_size_t* count_buf = (c_size_t*)C_ALLOC(total_buckets * sizeof(c_size_t)); + long long* heads = (long long*)C_ALLOC(total_buckets * sizeof(long long)); + long long* tails = (long long*)C_ALLOC(total_buckets * sizeof(long long)); + + if (count_buf == NULL || heads == NULL || tails == NULL) { + C_FREE(count_buf); C_FREE(heads); C_FREE(tails); + return C_ERR_NOMEM; + } + + // Launch the in-place cyclic permutation partition tree + c_InPlaceMSDRadixSort_Recursive(arr, 0, (long long)n - 1, 0, sort->R, count_buf, heads, tails); + + C_FREE(count_buf); + C_FREE(heads); + C_FREE(tails); + return C_ERR_OK; +} diff --git a/Sort/c_InPlaceMSDRadixSort.h b/Sort/c_InPlaceMSDRadixSort.h new file mode 100644 index 0000000..9c5311f --- /dev/null +++ b/Sort/c_InPlaceMSDRadixSort.h @@ -0,0 +1,20 @@ +#ifndef INCLUDED_C_INPLACEMSDRADIXSORT_H +#define INCLUDED_C_INPLACEMSDRADIXSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for standard byte arrays) +} c_InPlaceMSDRadixSort_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_InPlaceMSDRadixSort_Sort(const c_InPlaceMSDRadixSort_t* sort, char** arr, c_size_t n); + +#endif /*INCLUDED_C_INPLACEMSDRADIXSORT_H*/ diff --git a/Sort/c_IndexMaxPQ.c b/Sort/c_IndexMaxPQ.c new file mode 100644 index 0000000..7c606b2 --- /dev/null +++ b/Sort/c_IndexMaxPQ.c @@ -0,0 +1,184 @@ +#include + +#include +#include + +/** + * Internal helper to swap two positions inside the heap structures. + * Keeps the inverted index (qp) tightly synchronized. + */ +C_STATIC_FORCE_INLINE +void c_IndexMaxPQ_Swap(c_IndexMaxPQ_t* pq_inst, c_size_t i, c_size_t j) { + long long temp_pq = pq_inst->pq[i]; + pq_inst->pq[i] = pq_inst->pq[j]; + pq_inst->pq[j] = temp_pq; + + pq_inst->qp[pq_inst->pq[i]] = (long long)i; + pq_inst->qp[pq_inst->pq[j]] = (long long)j; +} + +/** + * Sift-Up Operational Core for Max-Heap + */ +C_STATIC_FORCE_INLINE +void c_IndexMaxPQ_SiftUp(c_IndexMaxPQ_t* pq_inst, c_size_t current) { + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + while (current > 0) { + c_size_t parent = (current - 1) / 2; + + const void* current_key = keys_arr + (pq_inst->pq[current] * es); + const void* parent_key = keys_arr + (pq_inst->pq[parent] * es); + + // Max-Heap condition: Break if current element is less than or equal to its parent + if (pq_inst->compar(current_key, parent_key) <= 0) { + break; + } + c_IndexMaxPQ_Swap(pq_inst, current, parent); + current = parent; + } +} + +/** + * Sift-Down Operational Core for Max-Heap + */ +C_STATIC_FORCE_INLINE +void c_IndexMaxPQ_SiftDown(c_IndexMaxPQ_t* pq_inst, c_size_t current) { + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + while (1) { + c_size_t left_child = (2 * current) + 1; + c_size_t right_child = (2 * current) + 2; + c_size_t largest = current; + + // Max-Heap condition: Target the larger of the two children to sift down + if (left_child < pq_inst->size) { + if (pq_inst->compar(keys_arr + (pq_inst->pq[left_child] * es), keys_arr + (pq_inst->pq[largest] * es)) > 0) { + largest = left_child; + } + } + if (right_child < pq_inst->size) { + if (pq_inst->compar(keys_arr + (pq_inst->pq[right_child] * es), keys_arr + (pq_inst->pq[largest] * es)) > 0) { + largest = right_child; + } + } + + if (largest == current) { + break; + } + c_IndexMaxPQ_Swap(pq_inst, current, largest); + current = largest; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ + +c_err_t c_IndexMaxPQ_Init(c_IndexMaxPQ_t* pq_inst, c_size_t max_items, c_size_t element_size, + int (*compar)(const void*, const void*)) { + if (pq_inst == NULL || max_items == 0 || element_size == 0 || compar == NULL) return C_ERR_PARAM; + + pq_inst->max_items = max_items; + pq_inst->element_size = element_size; + pq_inst->size = 0; + pq_inst->compar = compar; + + pq_inst->keys = C_ALLOC(max_items * element_size); + pq_inst->pq = (long long*)C_ALLOC(max_items * sizeof(long long)); + pq_inst->qp = (long long*)C_ALLOC(max_items * sizeof(long long)); + + if (pq_inst->keys == NULL || pq_inst->pq == NULL || pq_inst->qp == NULL) { + C_FREE(pq_inst->keys); C_FREE(pq_inst->pq); C_FREE(pq_inst->qp); + return C_ERR_NOMEM; + } + + for (c_size_t i = 0; i < max_items; i++) { + pq_inst->qp[i] = -1; + } + + return C_ERR_OK; +} + +void c_IndexMaxPQ_Destroy(c_IndexMaxPQ_t* pq_inst) { + if (pq_inst) { + C_FREE(pq_inst->keys); pq_inst->keys = NULL; + C_FREE(pq_inst->pq); pq_inst->pq = NULL; + C_FREE(pq_inst->qp); pq_inst->qp = NULL; + pq_inst->size = 0; + pq_inst->max_items = 0; + } +} + +c_bool_t c_IndexMaxPQ_Contains(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items) return C_FALSE; + return pq_inst->qp[ext_id] != -1; +} + +c_err_t c_IndexMaxPQ_Push(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* element) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items || element == NULL) return C_ERR_PARAM; + if (c_IndexMaxPQ_Contains(pq_inst, ext_id)) return C_ERR_ALREADY_EXISTS; + + char* keys_arr = (char*)pq_inst->keys; + memcpy(keys_arr + (ext_id * pq_inst->element_size), element, pq_inst->element_size); + + c_size_t current = pq_inst->size; + pq_inst->pq[current] = (long long)ext_id; + pq_inst->qp[ext_id] = (long long)current; + + pq_inst->size++; + + c_IndexMaxPQ_SiftUp(pq_inst, current); + + return C_ERR_OK; +} + +c_err_t c_IndexMaxPQ_Pop(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) { + if (pq_inst == NULL) return C_ERR_PARAM; + if (pq_inst->size == 0) return C_ERR_EMPTY; + + long long max_ext_id = pq_inst->pq[0]; + if (out_ext_id) *out_ext_id = (c_size_t)max_ext_id; + + if (out_element_buffer) { + char* keys_arr = (char*)pq_inst->keys; + memcpy(out_element_buffer, keys_arr + (max_ext_id * pq_inst->element_size), pq_inst->element_size); + } + + c_IndexMaxPQ_Swap(pq_inst, 0, pq_inst->size - 1); + + pq_inst->qp[max_ext_id] = -1; + pq_inst->size--; + + if (pq_inst->size > 0) { + c_IndexMaxPQ_SiftDown(pq_inst, 0); + } + + return C_ERR_OK; +} + +c_err_t c_IndexMaxPQ_ChangeKey(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* new_element) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items || new_element == NULL) return C_ERR_PARAM; + if (!c_IndexMaxPQ_Contains(pq_inst, ext_id)) return C_ERR_NOTFOUND; + + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + memcpy(keys_arr + (ext_id * es), new_element, es); + + c_size_t heap_pos = (c_size_t)pq_inst->qp[ext_id]; + + c_IndexMaxPQ_SiftUp(pq_inst, heap_pos); + c_IndexMaxPQ_SiftDown(pq_inst, heap_pos); + + return C_ERR_OK; +} + +void* c_IndexMaxPQ_Peek(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id) { + if (pq_inst == NULL || pq_inst->size == 0) return NULL; + if (out_ext_id) *out_ext_id = (c_size_t)pq_inst->pq[0]; + + char* keys_arr = (char*)pq_inst->keys; + return keys_arr + (pq_inst->pq[0] * pq_inst->element_size); +} + diff --git a/Sort/c_IndexMaxPQ.h b/Sort/c_IndexMaxPQ.h new file mode 100644 index 0000000..e3d5271 --- /dev/null +++ b/Sort/c_IndexMaxPQ.h @@ -0,0 +1,41 @@ +#ifndef INCLUDED_C_INDEXMAXPQ_H +#define INCLUDED_C_INDEXMAXPQ_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* keys; + c_size_t element_size; + c_size_t max_items; + c_size_t size; + long long* pq; + long long* qp; + int (*compar)(const void*, const void*); +} c_IndexMaxPQ_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_IndexMaxPQ_Init(c_IndexMaxPQ_t* pq_inst, c_size_t max_items, c_size_t element_size, + int (*compar)(const void*, const void*)); + +void c_IndexMaxPQ_Destroy(c_IndexMaxPQ_t* pq_inst); + +c_bool_t c_IndexMaxPQ_Contains(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id); + +c_err_t c_IndexMaxPQ_Push(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* element); + +c_err_t c_IndexMaxPQ_Pop(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer); + +c_err_t c_IndexMaxPQ_ChangeKey(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* new_element); + +void* c_IndexMaxPQ_Peek(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id); + + +#endif /*INCLUDED_C_INDEXMAXPQ_H*/ diff --git a/Sort/c_IndexMinPQ.c b/Sort/c_IndexMinPQ.c new file mode 100644 index 0000000..8ecd6d0 --- /dev/null +++ b/Sort/c_IndexMinPQ.c @@ -0,0 +1,192 @@ +#include +#include + +/** + * Internal helper to swap two positions inside the heap structures. + * Also keeps the inverted index (qp) tightly synchronized. + */ +C_STATIC_FORCE_INLINE +void c_IndexMinPQ_Swap(c_IndexMinPQ_t* pq_inst, c_size_t i, c_size_t j) { + long long temp_pq = pq_inst->pq[i]; + pq_inst->pq[i] = pq_inst->pq[j]; + pq_inst->pq[j] = temp_pq; + + pq_inst->qp[pq_inst->pq[i]] = (long long)i; + pq_inst->qp[pq_inst->pq[j]] = (long long)j; +} + +/** + * Sift-Up Operational Core + */ +C_STATIC_FORCE_INLINE +void c_IndexMinPQ_SiftUp(c_IndexMinPQ_t* pq_inst, c_size_t current) { + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + while (current > 0) { + c_size_t parent = (current - 1) / 2; + + const void* current_key = keys_arr + (pq_inst->pq[current] * es); + const void* parent_key = keys_arr + (pq_inst->pq[parent] * es); + + if (pq_inst->compar(current_key, parent_key) >= 0) { + break; + } + c_IndexMinPQ_Swap(pq_inst, current, parent); + current = parent; + } +} + +/** + * Sift-Down Operational Core + */ +C_STATIC_FORCE_INLINE +void c_IndexMinPQ_SiftDown(c_IndexMinPQ_t* pq_inst, c_size_t current) { + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + while (1) { + c_size_t left_child = (2 * current) + 1; + c_size_t right_child = (2 * current) + 2; + c_size_t smallest = current; + + if (left_child < pq_inst->size) { + if (pq_inst->compar(keys_arr + (pq_inst->pq[left_child] * es), keys_arr + (pq_inst->pq[smallest] * es)) < 0) { + smallest = left_child; + } + } + if (right_child < pq_inst->size) { + if (pq_inst->compar(keys_arr + (pq_inst->pq[right_child] * es), keys_arr + (pq_inst->pq[smallest] * es)) < 0) { + smallest = right_child; + } + } + + if (smallest == current) { + break; + } + c_IndexMinPQ_Swap(pq_inst, current, smallest); + current = smallest; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_IndexMinPQ_Init(c_IndexMinPQ_t* pq_inst, c_size_t max_items, c_size_t element_size, + int (*compar)(const void*, const void*)) { + if (pq_inst == NULL || max_items == 0 || element_size == 0 || compar == NULL) return C_ERR_PARAM; + + pq_inst->max_items = max_items; + pq_inst->element_size = element_size; + pq_inst->size = 0; + pq_inst->compar = compar; + + pq_inst->keys = C_ALLOC(max_items * element_size); + pq_inst->pq = (long long*)C_ALLOC(max_items * sizeof(long long)); + pq_inst->qp = (long long*)C_ALLOC(max_items * sizeof(long long)); + + if (pq_inst->keys == NULL || pq_inst->pq == NULL || pq_inst->qp == NULL) { + // Safe cleanup if any allocation segment drops offline + C_FREE(pq_inst->keys); C_FREE(pq_inst->pq); C_FREE(pq_inst->qp); + return C_ERR_NOMEM; + } + + // Initialize inverted tracking array cells to -1 (indicating absent from heap) + for (c_size_t i = 0; i < max_items; i++) { + pq_inst->qp[i] = -1; + } + + return C_ERR_OK; +} + +void c_IndexMinPQ_Destroy(c_IndexMinPQ_t* pq_inst) { + if (pq_inst) { + C_FREE(pq_inst->keys); pq_inst->keys = NULL; + C_FREE(pq_inst->pq); pq_inst->pq = NULL; + C_FREE(pq_inst->qp); pq_inst->qp = NULL; + pq_inst->size = 0; + pq_inst->max_items = 0; + } +} + +c_bool_t c_IndexMinPQ_Contains(c_IndexMinPQ_t* pq_inst, c_size_t ext_id) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items) return C_FALSE; + return pq_inst->qp[ext_id] != -1; +} + +c_err_t c_IndexMinPQ_Push(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* element) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items || element == NULL) return C_ERR_PARAM; + if (c_IndexMinPQ_Contains(pq_inst, ext_id)) return C_ERR_ALREADY_EXISTS; // Must use Change Key API if already existing + + char* keys_arr = (char*)pq_inst->keys; + + // Store key inside the primary index table slot + memcpy(keys_arr + (ext_id * pq_inst->element_size), element, pq_inst->element_size); + + // Append to bottom leaf array trackers + c_size_t current = pq_inst->size; + pq_inst->pq[current] = (long long)ext_id; + pq_inst->qp[ext_id] = (long long)current; + + pq_inst->size++; + + // Sift upward to re-stabilize the min-heap property + c_IndexMinPQ_SiftUp(pq_inst, current); + + return C_ERR_OK; +} + +c_err_t c_IndexMinPQ_Pop(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) { + if (pq_inst == NULL) return C_ERR_PARAM; + if (pq_inst->size == 0) return C_ERR_EMPTY; + + long long min_ext_id = pq_inst->pq[0]; + if (out_ext_id) *out_ext_id = (c_size_t)min_ext_id; + + if (out_element_buffer) { + char* keys_arr = (char*)pq_inst->keys; + memcpy(out_element_buffer, keys_arr + (min_ext_id * pq_inst->element_size), pq_inst->element_size); + } + + // Swap top root with the trailing active leaf node + c_IndexMinPQ_Swap(pq_inst, 0, pq_inst->size - 1); + + // Clean up tracking registers for extracted ID + pq_inst->qp[min_ext_id] = -1; + pq_inst->size--; + + // Sift down from the root to re-balance the tree bounds + if (pq_inst->size > 0) { + c_IndexMinPQ_SiftDown(pq_inst, 0); + } + + return C_ERR_OK; +} + +c_err_t c_IndexMinPQ_ChangeKey(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* new_element) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items || new_element == NULL) return C_ERR_PARAM; + if (!c_IndexMinPQ_Contains(pq_inst, ext_id)) return C_ERR_NOTFOUND; + + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + // Update raw payload data in-place + memcpy(keys_arr + (ext_id * es), new_element, es); + + // Leverage the inverted index (qp) to instantly locate the item's position inside the heap tree + c_size_t heap_pos = (c_size_t)pq_inst->qp[ext_id]; + + // Trigger localized sifting in both directions. Only one will execute depending on whether the key grew or shrank. + c_IndexMinPQ_SiftUp(pq_inst, heap_pos); + c_IndexMinPQ_SiftDown(pq_inst, heap_pos); + + return C_ERR_OK; +} + +void* c_IndexMinPQ_Peek(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id) { + if (pq_inst == NULL || pq_inst->size == 0) return NULL; + if (out_ext_id) *out_ext_id = (c_size_t)pq_inst->pq[0]; + + char* keys_arr = (char*)pq_inst->keys; + return keys_arr + (pq_inst->pq[0] * pq_inst->element_size); +} \ No newline at end of file diff --git a/Sort/c_IndexMinPQ.h b/Sort/c_IndexMinPQ.h new file mode 100644 index 0000000..de0dcd8 --- /dev/null +++ b/Sort/c_IndexMinPQ.h @@ -0,0 +1,42 @@ +#ifndef INCLUDED_C_INDEXMINPQ_H +#define INCLUDED_C_INDEXMINPQ_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct { + void* keys; // Flat array storing user's complex items (indexed by external ID) + c_size_t element_size; // Size of each element in bytes + c_size_t max_items; // Maximum external ID capacity (0 to max_items - 1) + c_size_t size; // Current active element count inside the heap + + long long* pq; // Heap array: map heap position -> external ID + long long* qp; // Inverted index array: map external ID -> heap position (-1 if not in heap) + + int (*compar)(const void*, const void*); // Custom comparison rule pointer +} c_IndexMinPQ_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_IndexMinPQ_Init(c_IndexMinPQ_t* pq_inst, c_size_t max_items, c_size_t element_size, + int (*compar)(const void*, const void*)); + +void c_IndexMinPQ_Destroy(c_IndexMinPQ_t* pq_inst); + +c_bool_t c_IndexMinPQ_Contains(c_IndexMinPQ_t* pq_inst, c_size_t ext_id); + +c_err_t c_IndexMinPQ_Push(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* element); + +c_err_t c_IndexMinPQ_Pop(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) ; + +c_err_t c_IndexMinPQ_ChangeKey(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* new_element); + +void* c_IndexMinPQ_Peek(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id); + +#endif /*INCLUDED_C_INDEXMINPQ_H*/ diff --git a/Sort/c_InsertionSort.c b/Sort/c_InsertionSort.c new file mode 100644 index 0000000..4ffb681 --- /dev/null +++ b/Sort/c_InsertionSort.c @@ -0,0 +1 @@ +#include diff --git a/Sort/c_InsertionSort.h b/Sort/c_InsertionSort.h new file mode 100644 index 0000000..6c4b40e --- /dev/null +++ b/Sort/c_InsertionSort.h @@ -0,0 +1,68 @@ +#ifndef INCLUDED_C_INSERTIONSORT_H +#define INCLUDED_C_INSERTIONSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * 通用插入排序函数 + * @param base 指向待排序数组首元素的指针 + * @param num 数组中元素的个数 + * @param size 每个元素的大小(字节数) + * @param compar 指向比较函数的指针 + */ +C_STATIC_FORCE_INLINE +void c_InsertionSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + char* arr = (char*)base; // 强转为 char* 以便按单字节进行指针偏移 + + // 分配一块临时内存,用于存放当前要插入的“哨兵”元素(temp) +#define STACK_LIMIT 128 + char stack_buf[STACK_LIMIT]; + void* temp = NULL; + + if (size <= STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + for (c_size_t i = 1; i < num; i++) { + // temp = arr[i]:将当前元素复制到临时空间 + memcpy(temp, arr + (i * size), size); + + long long j = i - 1; + + // 循环条件:j >= 0 且 arr[j] > temp + // 使用 compar(arr + (j * size), temp) > 0 来判断是否需要后移 + while (j >= 0 && compar(arr + (j * size), temp) > 0) { + // arr[j + 1] = arr[j]:将前面的元素往后移一位 + memcpy(arr + ((j + 1) * size), arr + (j * size), size); + j--; + } + + // arr[j + 1] = temp:将目标元素插入到正确位置 + memcpy(arr + ((j + 1) * size), temp, size); + } + + // Only trigger free if it was actually allocated from the heap + if (size > STACK_LIMIT) { + C_FREE(temp); + } +#undef STACK_LIMIT +} + + + +#endif /*INCLUDED_C_INSERTIONSORT_H*/ diff --git a/Sort/c_LSDRadixSort.c b/Sort/c_LSDRadixSort.c new file mode 100644 index 0000000..63eeed6 --- /dev/null +++ b/Sort/c_LSDRadixSort.c @@ -0,0 +1,62 @@ +#include +#include + +/** + * Sorts an array of fixed-length strings stably using the LSD Radix Sort pipeline. + * Features upfront workspace allocations to completely eliminate heap thrashing in hot loops. + * + * Time Complexity: O(W * (N + R)) | Space Complexity: O(N + R) transient workspace memory + * @param sort Pointer to the initialized LSD config profile. + * @param arr Array of pointers to null-terminated char arrays (each must be at least W long). + * @param n Total number of strings inside the array. + */ +c_err_t c_LSDRadixSort_Sort(const c_LSDRadixSort_t* sort, char** arr, c_size_t n) { + if (sort == NULL || arr == NULL || sort->R == 0 || sort->W == 0) return C_ERR_PARAM; + if (n <= 1) return C_ERR_OK; // Trivial exit pass + + c_size_t R = sort->R; + c_size_t W = sort->W; + + // Upfront Transient Workspace Allocation: Eliminates allocation overhead in hot paths + char** aux = (char**)C_ALLOC(n * sizeof(char*)); + c_size_t* count = (c_size_t*)C_ALLOC((R + 1) * sizeof(c_size_t)); + + if (aux == NULL || count == NULL) { + C_FREE(aux); + C_FREE(count); + return C_ERR_NOMEM; + } + + // --- Core Iterative LSD Pass Loop --- + // Travel from right to left (Least Significant to Most Significant) + for (long long d = (long long)W - 1; d >= 0; d--) { + + // Reset the counting frequency registers + memset(count, 0, (R + 1) * sizeof(c_size_t)); + + // Pass A: Compute frequency counts using character indices as bucket addresses + for (c_size_t i = 0; i < n; i++) { + unsigned char c = (unsigned char)arr[i][d]; + count[c + 1]++; + } + + // Pass B: Transform frequencies into structural start indexes (Prefix Sums) + for (c_size_t r = 0; r < R; r++) { + count[r + 1] += count[r]; + } + + // Pass C: Distribute strings to the temporary aux array (Guarantees Stable Order Sorting) + for (c_size_t i = 0; i < n; i++) { + unsigned char c = (unsigned char)arr[i][d]; + aux[count[c]++] = arr[i]; + } + + // Pass D: Copy back copies natively to the primary tracking layout pointers + memcpy(arr, aux, n * sizeof(char*)); + } + + // Purge temporary scratchpad workspace containers cleanly + C_FREE(aux); + C_FREE(count); + return C_ERR_OK; +} diff --git a/Sort/c_LSDRadixSort.h b/Sort/c_LSDRadixSort.h new file mode 100644 index 0000000..20a5d6a --- /dev/null +++ b/Sort/c_LSDRadixSort.h @@ -0,0 +1,16 @@ +#ifndef INCLUDED_C_LSDRADIXSORT_H +#define INCLUDED_C_LSDRADIXSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +typedef struct { + c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for ASCII chars or bytes) + c_size_t W; // Fixed length key width criteria (number of sorting passes/characters) +} c_LSDRadixSort_t; + +c_err_t c_LSDRadixSort_Sort(const c_LSDRadixSort_t* sort, char** arr, c_size_t n); + + +#endif /*INCLUDED_C_LSDRADIXSORT_H*/ diff --git a/Sort/c_MSDRadixSort.c b/Sort/c_MSDRadixSort.c new file mode 100644 index 0000000..a47dfc0 --- /dev/null +++ b/Sort/c_MSDRadixSort.c @@ -0,0 +1,99 @@ +#include +#include + +/** + * Inline helper to safely extract a character at string offset d. + * Automatically maps a string's null terminator to a sentinel value of -1. + */ +C_STATIC_FORCE_INLINE +int c_MSDRadixSort_CharAt(const char* str, c_size_t d) { + if (str == NULL) return -1; + // Walk down to offset d without triggering a buffer overflow lookup violation + c_size_t i = 0; + while (i < d && str[i] != '\0') { + i++; + } + if (str[i] == '\0' || i < d) return -1; + return (unsigned char)str[i]; +} + +/** + * Core Private Recursive Sub-partition Sorting Subroutine. + * Shares a single pre-allocated auxiliary buffer across stack frames to prevent heap allocation overhead. + * + * @param lo Lower boundary index of the target partition array slice (inclusive). + * @param hi Upper boundary index of the target partition array slice (inclusive). + * @param d The current character string evaluation offset cursor. + */ +static void c_MSDRadixSort_SortRecursive(char** arr, long long lo, long long hi, c_size_t d, + c_size_t R, char** aux, c_size_t* count_buf) { + if (hi <= lo) return; + + // Cutoff to Insertion Sort for tiny sub-arrays can be added here for production fine-tuning. + + // Calculate sub-slice width and initialize count registers + c_size_t n = (c_size_t)(hi - lo + 1); + // Elements are shifted forward by +2 slots to gracefully absorb the -1 string end sentinel + memset(count_buf, 0, (R + 2) * sizeof(c_size_t)); + + // Pass A: Compute frequency buckets + for (long long i = lo; i <= hi; i++) { + int c = c_MSDRadixSort_CharAt(arr[i], d); + count_buf[c + 2]++; + } + + // Pass B: Transform frequencies into structural start indexes (Prefix Sums) + for (c_size_t r = 0; r < R + 1; r++) { + count_buf[r + 1] += count_buf[r]; + } + + // Pass C: Distribute strings stably to the temporary auxiliary workspace slice + for (long long i = lo; i <= hi; i++) { + int c = c_MSDRadixSort_CharAt(arr[i], d); + aux[count_buf[c + 1]++] = arr[i]; + } + + // Pass D: Copy back copies natively to the primary tracking layout pointers + for (long long i = lo; i <= hi; i++) { + arr[i] = aux[i - lo]; + } + + // Recursively sort sub-arrays for each character bucket + // Note: count_buf[0] handles strings that hit a terminal '\0' sentinel, so we skip it to prevent loops + for (c_size_t r = 0; r < R; r++) { + long long next_lo = lo + (long long)count_buf[r]; + long long next_hi = lo + (long long)count_buf[r + 1] - 1; + + if (next_hi > next_lo) { + c_MSDRadixSort_SortRecursive(arr, next_lo, next_hi, d + 1, R, aux, count_buf); + } + } +} + +/** + * Sorts an array of variable-length strings using the MSD Radix Sort pipeline. + * Guarantees zero runtime heap thrashing via upfront workspace pooling. + * + * Time Complexity: O(N * String_Length) optimal | Space Complexity: O(N + R) transient workspace memory + */ +c_err_t c_MSDRadixSort_Sort(const c_MSDRadixSort_t* sort, char** arr, c_size_t n) { + if (sort == NULL || arr == NULL || sort->R == 0) return C_ERR_PARAM; + if (n <= 1) return C_ERR_OK; + + // Upfront Transient Workspace Allocation: Eliminates allocation overhead in deep recursions + char** aux = (char**)C_ALLOC(n * sizeof(char*)); + c_size_t* count_buf = (c_size_t*)C_ALLOC((sort->R + 2) * sizeof(c_size_t)); + + if (aux == NULL || count_buf == NULL) { + C_FREE(aux); + C_FREE(count_buf); + return C_ERR_NOMEM; + } + + // Launch the core string-wise recursive partition tree + c_MSDRadixSort_SortRecursive(arr, 0, (long long)n - 1, 0, sort->R, aux, count_buf); + + C_FREE(aux); + C_FREE(count_buf); + return C_ERR_OK; +} diff --git a/Sort/c_MSDRadixSort.h b/Sort/c_MSDRadixSort.h new file mode 100644 index 0000000..21ed3e0 --- /dev/null +++ b/Sort/c_MSDRadixSort.h @@ -0,0 +1,17 @@ +#ifndef INCLUDED_C_MSDRADIXSORT_H +#define INCLUDED_C_MSDRADIXSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +typedef struct { + c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for standard byte arrays) +} c_MSDRadixSort_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MSDRadixSort_Sort(const c_MSDRadixSort_t* sort, char** arr, c_size_t n); + +#endif /*INCLUDED_C_MSDRADIXSORT_H*/ diff --git a/Sort/c_MaxPQ.c b/Sort/c_MaxPQ.c new file mode 100644 index 0000000..55b2472 --- /dev/null +++ b/Sort/c_MaxPQ.c @@ -0,0 +1,208 @@ +#include +#include + + +/** + * Internal macro helper to swap two arbitrary blocks of memory. + */ +C_STATIC_FORCE_INLINE +void c_HeapSwap(char* arr, c_size_t idx1, c_size_t idx2, c_size_t size, void* temp) { + if (idx1 == idx2) return; + char* a = arr + (idx1 * size); + char* b = arr + (idx2 * size); + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MaxPQ_Init(c_MaxPQ_t* pq, c_size_t initial_capacity, c_size_t element_size, + int (*compar)(const void*, const void*)) { + if (pq == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM; + + pq->capacity = (initial_capacity > 0) ? initial_capacity : 4; + pq->element_size = element_size; + pq->size = 0; + pq->compar = compar; + pq->data = C_ALLOC(pq->capacity * element_size); + + if (pq->data == NULL) return C_ERR_NOMEM; + return C_ERR_OK; +} + +void c_MaxPQ_Destroy(c_MaxPQ_t* pq) { + if (!pq) return; + C_FREE(pq->data); + pq->size = 0; + pq->capacity = 0; +} + +c_err_t c_MaxPQ_Push(c_MaxPQ_t* pq, const void* element) { + if (pq == NULL || element == NULL) return C_ERR_PARAM; + + char* arr = (char*)pq->data; + + // Capacity check: Scale memory boundary out if full + if (pq->size >= pq->capacity) { + c_size_t new_capacity = pq->capacity * 2; + // Reallocate manually utilizing framework macros + void* new_data = C_ALLOC(new_capacity * pq->element_size); + if (new_data == NULL) return C_ERR_NOMEM; // Allocation failure block + + memcpy(new_data, pq->data, pq->size * pq->element_size); + C_FREE(pq->data); + pq->data = new_data; + pq->capacity = new_capacity; + arr = (char*)pq->data; + } + + // Allocate stack cache buffer for object swapping routines +#define PQ_STACK_LIMIT 128 + char stack_buf[PQ_STACK_LIMIT]; + void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size); + if (temp == NULL) return C_ERR_NOMEM; + + // Place new element at the bottom-most leaf slot of the max-heap tree + c_size_t current = pq->size; + memcpy(arr + (current * pq->element_size), element, pq->element_size); + pq->size++; + + // Sift-Up loop processing + while (current > 0) { + c_size_t parent = (current - 1) / 2; + + // Max-heap rule tracking: If child <= parent, tree balancing properties are correct + if (pq->compar(arr + (current * pq->element_size), arr + (parent * pq->element_size)) <= 0) { + break; + } + + c_HeapSwap(arr, current, parent, pq->element_size, temp); + current = parent; + } + + if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp); +#undef PQ_STACK_LIMIT + return C_ERR_OK; +} + + +c_err_t c_MaxPQ_Pop(c_MaxPQ_t* pq, void* output_buffer) { + if (!pq) return C_ERR_PARAM; + + if (pq->size == 0) return C_ERR_EMPTY; + + char* arr = (char*)pq->data; + + // If a tracking output buffer pointer is supplied, export the maximum item + if (output_buffer != NULL) { + memcpy(output_buffer, arr, pq->element_size); + } + + // Shrink element count tracking early + pq->size--; + + if (pq->size > 0) { + // Swap the last leaf node up to root position + memcpy(arr, arr + (pq->size * pq->element_size), pq->element_size); + +#define PQ_STACK_LIMIT 128 + char stack_buf[PQ_STACK_LIMIT]; + void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size); + if (temp == NULL) return C_ERR_NOMEM; + + // Sift-Down balancing loop processing + c_size_t current = 0; + while (1) { + c_size_t left_child = (2 * current) + 1; + c_size_t right_child = (2 * current) + 2; + c_size_t largest = current; + + // Check if left child is larger than current node + if (left_child < pq->size && + pq->compar(arr + (left_child * pq->element_size), arr + (largest * pq->element_size)) > 0) { + largest = left_child; + } + + // Check if right child is larger than the currently tracked largest node + if (right_child < pq->size && + pq->compar(arr + (right_child * pq->element_size), arr + (largest * pq->element_size)) > 0) { + largest = right_child; + } + + // Balanced condition achieved + if (largest == current) { + break; + } + + c_HeapSwap(arr, current, largest, pq->element_size, temp); + current = largest; + } + + if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp); +#undef PQ_STACK_LIMIT + } + + return C_ERR_OK; +} + +void* c_MaxPQ_Peek(c_MaxPQ_t* pq) { + if (pq == NULL || pq->size == 0) return NULL; + return pq->data; // Root node is consistently maximum element +} + +c_err_t c_MaxPQ_Clear(c_MaxPQ_t* pq) { + if (pq == NULL) return C_ERR_PARAM; + + // Simply reset the size to zero. The underlying buffer remains allocated. + pq->size = 0; + + return C_ERR_OK; +} + +/** + * Manually resize the memory allocation capacity of the Priority Queue. + * @param pq Pointer to the Max Priority Queue instance. + * @param new_capacity The desired number of element slots to allocate. + * @return C_ERR_OK if successful, C_ERR_INVALID for bad arguments, + * or C_ERR_NOMEM if memory allocation fails. + */ +c_err_t c_MaxPQ_Resize(c_MaxPQ_t* pq, c_size_t new_capacity) { + if (pq == NULL) return C_ERR_PARAM; + + // Prevent shrinking below the current number of active elements inside the heap + if (new_capacity < pq->size) return C_ERR_PARAM; + + // If the capacity is already identical, skip processing to avoid memory overhead + if (new_capacity == pq->capacity) return C_ERR_OK; + + // Handle downsizing down to 0 safely if the queue is empty + if (new_capacity == 0) { + if (pq->data != NULL) { + C_FREE(pq->data); + pq->data = NULL; + } + pq->capacity = 0; + return C_ERR_OK; + } + + // Allocate a new memory block according to your framework specification + void* new_data = C_ALLOC(new_capacity * pq->element_size); + if (new_data == NULL) return C_ERR_NOMEM; + + // If there are existing active elements, move them to the newly allocated block + if (pq->size > 0 && pq->data != NULL) { + memcpy(new_data, pq->data, pq->size * pq->element_size); + } + + // Free the old array and bind the new tracking parameters + if (pq->data != NULL) { + C_FREE(pq->data); + } + pq->data = new_data; + pq->capacity = new_capacity; + + return C_ERR_OK; +} + diff --git a/Sort/c_MaxPQ.h b/Sort/c_MaxPQ.h new file mode 100644 index 0000000..d6ac7d4 --- /dev/null +++ b/Sort/c_MaxPQ.h @@ -0,0 +1,34 @@ +#ifndef INCLUDED_C_MAXPQ_H +#define INCLUDED_C_MAXPQ_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* data; // Flat char pointer block tracking memory slots + c_size_t element_size; // Size of each complex structure element in bytes + c_size_t capacity; // Maximum allocated element capacity + c_size_t size; // Current active element count inside the heap + int (*compar)(const void*, const void*); // Custom comparison rule pointer +} c_MaxPQ_t; + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MaxPQ_Init(c_MaxPQ_t* pq, c_size_t initial_capacity, c_size_t element_size, + int (*compar)(const void*, const void*)); + +void c_MaxPQ_Destroy(c_MaxPQ_t* pq); + +c_err_t c_MaxPQ_Push(c_MaxPQ_t* pq, const void* element); +c_err_t c_MaxPQ_Pop(c_MaxPQ_t* pq, void* output_buffer); +void* c_MaxPQ_Peek(c_MaxPQ_t* pq); +c_err_t c_MaxPQ_Clear(c_MaxPQ_t* pq); +c_err_t c_MaxPQ_Resize(c_MaxPQ_t* pq, c_size_t new_capacity); + +#endif /*INCLUDED_C_MAXPQ_H*/ diff --git a/Sort/c_MergeSort.c b/Sort/c_MergeSort.c new file mode 100644 index 0000000..da73c93 --- /dev/null +++ b/Sort/c_MergeSort.c @@ -0,0 +1,54 @@ +#include + +/** + * Internal merging routine for top-down merge sort. + */ +C_STATIC_FORCE_INLINE +void c_MergeInternal(char* arr, c_size_t left, c_size_t mid, c_size_t right, + c_size_t size, char* aux, + int (*compar)(const void*, const void*)) { + c_size_t i = left; + c_size_t j = mid + 1; + c_size_t k = left; + + // Copy the target segment into the auxiliary working buffer + memcpy(aux + (left * size), arr + (left * size), (right - left + 1) * size); + + // Merge back into the original array tracking sorted boundaries + while (i <= mid && j <= right) { + if (compar(aux + (i * size), aux + (j * size)) <= 0) { + memcpy(arr + (k * size), aux + (i * size), size); + i++; + } else { + memcpy(arr + (k * size), aux + (j * size), size); + j++; + } + k++; + } + + // Copy any remaining elements of the left sub-array if any + while (i <= mid) { + memcpy(arr + (k * size), aux + (i * size), size); + i++; + k++; + } + // Note: Remaining items on the right side are already natively sitting in the correct slots. +} + +/** + * Recursive structural block splitting segments into halves. + */ +void c_MergeSortSub(char* arr, c_size_t left, c_size_t right, c_size_t size, char* aux, + int (*compar)(const void*, const void*)) { + if (left >= right) return; + + c_size_t mid = left + (right - left) / 2; + + c_MergeSortSub(arr, left, mid, size, aux, compar); + c_MergeSortSub(arr, mid + 1, right, size, aux, compar); + + // Optimization: If the array segment is already naturally sorted, skip the merge routine + if (compar(arr + (mid * size), arr + ((mid + 1) * size)) > 0) { + c_MergeInternal(arr, left, mid, right, size, aux, compar); + } +} diff --git a/Sort/c_MergeSort.h b/Sort/c_MergeSort.h new file mode 100644 index 0000000..9ae1551 --- /dev/null +++ b/Sort/c_MergeSort.h @@ -0,0 +1,54 @@ +#ifndef INCLUDED_C_MERGESORT_H +#define INCLUDED_C_MERGESORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_MergeSortSub(char* arr, c_size_t left, c_size_t right, c_size_t size, char* aux, + int (*compar)(const void*, const void*)); + +/** + * Top-Level Custom Framework Entry Point for Top-Down Merge Sort. + * Time Complexity: O(n log n) | Space Complexity: O(n) | Stable: Yes + */ +C_STATIC_FORCE_INLINE +void c_MergeSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + c_size_t total_bytes = num * size; + + // Optimization: Fallback to stack buffer allocation if overall payload fits locally + #define MERGE_STACK_LIMIT 512 + char stack_buf[MERGE_STACK_LIMIT]; + char* aux = NULL; + + if (total_bytes <= MERGE_STACK_LIMIT) { + aux = stack_buf; + } else { + aux = (char*)C_ALLOC(total_bytes); + if (aux == NULL) return; // Allocation safeguard + } + + // Execute recursive divide-and-conquer strategy + c_MergeSortSub(arr, 0, num - 1, size, aux, compar); + + if (total_bytes > MERGE_STACK_LIMIT) { + C_FREE(aux); + } + #undef MERGE_STACK_LIMIT +} + + +#endif /*INCLUDED_C_MERGESORT_H*/ diff --git a/Sort/c_MergeSortBU.c b/Sort/c_MergeSortBU.c new file mode 100644 index 0000000..a956dc2 --- /dev/null +++ b/Sort/c_MergeSortBU.c @@ -0,0 +1 @@ +#include diff --git a/Sort/c_MergeSortBU.h b/Sort/c_MergeSortBU.h new file mode 100644 index 0000000..fec8875 --- /dev/null +++ b/Sort/c_MergeSortBU.h @@ -0,0 +1,98 @@ +#ifndef INCLUDED_C_MERGESORTBU_H +#define INCLUDED_C_MERGESORTBU_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ +#include "c_Macros.h" + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * Internal merging routine for iterative blocks. + * Re-used from the top-down logic to guarantee stability. + */ +C_STATIC_FORCE_INLINE +void c_MergeInternalBU(char* arr, c_size_t left, c_size_t mid, c_size_t right, + c_size_t size, char* aux, + int (*compar)(const void*, const void*)) { + c_size_t i = left; + c_size_t j = mid + 1; + c_size_t k = left; + + // Snapshot target segment into tracking auxiliary zone + memcpy(aux + (left * size), arr + (left * size), (right - left + 1) * size); + + // Merge operational slots back sequentially + while (i <= mid && j <= right) { + if (compar(aux + (i * size), aux + (j * size)) <= 0) { + memcpy(arr + (k * size), aux + (i * size), size); + i++; + } else { + memcpy(arr + (k * size), aux + (j * size), size); + j++; + } + k++; + } + + // Flush remaining items sitting on the left slice + while (i <= mid) { + memcpy(arr + (k * size), aux + (i * size), size); + i++; + k++; + } +} + +/** + * Top-Level Iterative Framework Entry Point for Bottom-Up Merge Sort. + * Time Complexity: O(n log n) | Space Complexity: O(n) | Stable: Yes | Call Stack: O(1) + */ +C_STATIC_FORCE_INLINE +void c_MergeSortBottomUp(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + c_size_t total_bytes = num * size; + + // Optimization: Fallback to stack buffer allocation if memory payload fits locally + #define MERGE_BU_STACK_LIMIT 512 + char stack_buf[MERGE_BU_STACK_LIMIT]; + char* aux = NULL; + + if (total_bytes <= MERGE_BU_STACK_LIMIT) { + aux = stack_buf; + } else { + aux = (char*)C_ALLOC(total_bytes); + if (aux == NULL) return; + } + + // Step-wise sub-array size doubling loop: 1, 2, 4, 8, 16... + for (c_size_t width = 1; width < num; width *= 2) { + // Iterate through segments by blocks of 2 * width + for (c_size_t left = 0; left < num - width; left += 2 * width) { + c_size_t mid = left + width - 1; + c_size_t right = C_MIN(left + (2 * width) - 1, num - 1); + + // Optimization: Skip merge phase if the adjacent sub-segments are already sequentially sorted + if (compar(arr + (mid * size), arr + ((mid + 1) * size)) > 0) { + c_MergeInternalBU(arr, left, mid, right, size, aux, compar); + } + } + } + + if (total_bytes > MERGE_BU_STACK_LIMIT) { + C_FREE(aux); + } + #undef MERGE_BU_STACK_LIMIT +} + + +#endif /*INCLUDED_C_MERGESORTBU_H*/ diff --git a/Sort/c_MinPQ.c b/Sort/c_MinPQ.c new file mode 100644 index 0000000..67371e6 --- /dev/null +++ b/Sort/c_MinPQ.c @@ -0,0 +1,160 @@ +#include +#include + +/** + * Internal helper to swap two memory slots inside the heap array. + */ +C_STATIC_FORCE_INLINE +void c_MinPQ_HeapSwap(char* arr, c_size_t idx1, c_size_t idx2, c_size_t size, void* temp) { + if (idx1 == idx2) return; + char* a = arr + (idx1 * size); + char* b = arr + (idx2 * size); + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +c_err_t c_MinPQ_Init(c_MinPQ_t* pq, c_size_t initial_capacity, c_size_t element_size, + int (*compar)(const void*, const void*)) { + if (pq == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM; + + pq->capacity = (initial_capacity > 0) ? initial_capacity : 4; + pq->element_size = element_size; + pq->size = 0; + pq->compar = compar; + pq->data = C_ALLOC(pq->capacity * element_size); + + return (pq->data != NULL) ? C_ERR_OK : C_ERR_NOMEM; +} + +void c_MinPQ_Destroy(c_MinPQ_t* pq) { + if (!pq) return; + C_FREE(pq->data); + pq->size = 0; + pq->capacity = 0; +} + +c_err_t c_MinPQ_Clear(c_MinPQ_t* pq) { + if (pq == NULL) return C_ERR_PARAM; + pq->size = 0; + return C_ERR_OK; +} + +c_err_t c_MinPQ_Resize(c_MinPQ_t* pq, c_size_t new_capacity) { + if (pq == NULL || new_capacity < pq->size) return C_ERR_PARAM; + if (new_capacity == pq->capacity) return C_ERR_OK; + + if (new_capacity == 0) { + if (pq->data != NULL) C_FREE(pq->data); + pq->data = NULL; + pq->capacity = 0; + return C_ERR_OK; + } + + void* new_data = C_ALLOC(new_capacity * pq->element_size); + if (new_data == NULL) return C_ERR_NOMEM; + + if (pq->size > 0 && pq->data != NULL) { + memcpy(new_data, pq->data, pq->size * pq->element_size); + } + + if (pq->data != NULL) C_FREE(pq->data); + pq->data = new_data; + pq->capacity = new_capacity; + + return C_ERR_OK; +} + +c_err_t c_MinPQ_Push(c_MinPQ_t* pq, const void* element) { + if (pq == NULL || element == NULL) return C_ERR_PARAM; + + char* arr = (char*)pq->data; + + // Handle dynamic capacity auto-doubling + if (pq->size >= pq->capacity) { + c_err_t err = c_MinPQ_Resize(pq, pq->capacity * 2); + if (err != C_ERR_OK) return err; + arr = (char*)pq->data; + } + + #define PQ_STACK_LIMIT 128 + char stack_buf[PQ_STACK_LIMIT]; + void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size); + if (temp == NULL) return C_ERR_NOMEM; + + // Place element at the next available leaf position + c_size_t current = pq->size; + memcpy(arr + (current * pq->element_size), element, pq->element_size); + pq->size++; + + // Sift-Up for Min-Heap: Move up while element < parent + while (current > 0) { + c_size_t parent = (current - 1) / 2; + if (pq->compar(arr + (current * pq->element_size), arr + (parent * pq->element_size)) >= 0) { + break; + } + c_MinPQ_HeapSwap(arr, current, parent, pq->element_size, temp); + current = parent; + } + + if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp); + #undef PQ_STACK_LIMIT + return C_ERR_OK; +} + +c_err_t c_MinPQ_Pop(c_MinPQ_t* pq, void* output_buffer) { + if (pq == NULL) return C_ERR_PARAM; + if (pq->size == 0) return C_ERR_EMPTY; + + char* arr = (char*)pq->data; + + // Export root minimum element if buffer is provided + if (output_buffer != NULL) { + memcpy(output_buffer, arr, pq->element_size); + } + + pq->size--; + + if (pq->size > 0) { + // Move last leaf to root position + memcpy(arr, arr + (pq->size * pq->element_size), pq->element_size); + + #define PQ_STACK_LIMIT 128 + char stack_buf[PQ_STACK_LIMIT]; + void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size); + if (temp == NULL) return C_ERR_NOMEM; + + // Sift-Down for Min-Heap: Swap with the smaller of the two children + c_size_t current = 0; + while (1) { + c_size_t left_child = (2 * current) + 1; + c_size_t right_child = (2 * current) + 2; + c_size_t smallest = current; + + if (left_child < pq->size && + pq->compar(arr + (left_child * pq->element_size), arr + (smallest * pq->element_size)) < 0) { + smallest = left_child; + } + if (right_child < pq->size && + pq->compar(arr + (right_child * pq->element_size), arr + (smallest * pq->element_size)) < 0) { + smallest = right_child; + } + if (smallest == current) { + break; + } + + c_MinPQ_HeapSwap(arr, current, smallest, pq->element_size, temp); + current = smallest; + } + + if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp); + #undef PQ_STACK_LIMIT + } + + return C_ERR_OK; +} + +void* c_MinPQ_Peek(c_MinPQ_t* pq) { + if (pq == NULL || pq->size == 0) return NULL; + return pq->data; +} diff --git a/Sort/c_MinPQ.h b/Sort/c_MinPQ.h new file mode 100644 index 0000000..2100633 --- /dev/null +++ b/Sort/c_MinPQ.h @@ -0,0 +1,34 @@ +#ifndef INCLUDED_C_MINPQ_H +#define INCLUDED_C_MINPQ_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* data; + c_size_t element_size; + c_size_t capacity; + c_size_t size; + int (*compar)(const void*, const void*); +} c_MinPQ_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MinPQ_Init(c_MinPQ_t* pq, c_size_t initial_capacity, c_size_t element_size, + int (*compar)(const void*, const void*)); + +void c_MinPQ_Destroy(c_MinPQ_t* pq); + +c_err_t c_MinPQ_Clear(c_MinPQ_t* pq); +c_err_t c_MinPQ_Resize(c_MinPQ_t* pq, c_size_t new_capacity); + +c_err_t c_MinPQ_Push(c_MinPQ_t* pq, const void* element); +c_err_t c_MinPQ_Pop(c_MinPQ_t* pq, void* output_buffer); +void* c_MinPQ_Peek(c_MinPQ_t* pq); + +#endif /*INCLUDED_C_MINPQ_H*/ diff --git a/Sort/c_QuickSort.c b/Sort/c_QuickSort.c new file mode 100644 index 0000000..5411fa2 --- /dev/null +++ b/Sort/c_QuickSort.c @@ -0,0 +1,84 @@ +#include + + +/** + * Internal macro helper to swap two arbitrary blocks of memory of given size. + */ +C_STATIC_FORCE_INLINE +void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) { + if (a == b) return; + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +/** + * Chooses the median of left, center, and right elements as the pivot, + * hides it at (right - 1), and returns a pointer to it. + */ +C_STATIC_FORCE_INLINE +void* c_MedianOfThree(char* arr, long long left, long long right, c_size_t size, + void* temp, int (*compar)(const void*, const void*)) { + long long center = left + (right - left) / 2; + + // Order left, center, right + if (compar(arr + (left * size), arr + (center * size)) > 0) { + c_SwapInternal(arr + (left * size), arr + (center * size), size, temp); + } + if (compar(arr + (left * size), arr + (right * size)) > 0) { + c_SwapInternal(arr + (left * size), arr + (right * size), size, temp); + } + if (compar(arr + (center * size), arr + (right * size)) > 0) { + c_SwapInternal(arr + (center * size), arr + (right * size), size, temp); + } + + // Place pivot at position (right - 1) + c_SwapInternal(arr + (center * size), arr + ((right - 1) * size), size, temp); + return arr + ((right - 1) * size); +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_QuickSortSub(char* arr, long long left, long long right, c_size_t size, void* temp, + int (*compar)(const void*, const void*)) { + // Optimization: Fallback to manual insertion sort for tiny arrays to prune deep recursion + if (left + 10 > right) { + // Basic Inline Insertion Sort boundary loop + for (long long i = left + 1; i <= right; i++) { + memcpy(temp, arr + (i * size), size); + long long j = i; + while (j > left && compar(arr + ((j - 1) * size), temp) > 0) { + memcpy(arr + (j * size), arr + ((j - 1) * size), size); + j--; + } + memcpy(arr + (j * size), temp, size); + } + return; + } + + // Retrieve Median Pivot + c_MedianOfThree(arr, left, right, size, temp, compar); + + long long i = left; + long long j = right - 1; + + // Hoare Partitioning Loop + while (1) { + while (compar(arr + ((++i) * size), arr + ((right - 1) * size)) < 0); + while (compar(arr + ((--j) * size), arr + ((right - 1) * size)) > 0); + if (i < j) { + c_SwapInternal(arr + (i * size), arr + (j * size), size, temp); + } else { + break; + } + } + + // Restore pivot to its correct final slot + c_SwapInternal(arr + (i * size), arr + ((right - 1) * size), size, temp); + + // Recursively execute left and right segments + c_QuickSortSub(arr, left, i - 1, size, temp, compar); + c_QuickSortSub(arr, i + 1, right, size, temp, compar); +} diff --git a/Sort/c_QuickSort.h b/Sort/c_QuickSort.h new file mode 100644 index 0000000..1456060 --- /dev/null +++ b/Sort/c_QuickSort.h @@ -0,0 +1,56 @@ +#ifndef INCLUDED_C_QUICKSORT_H +#define INCLUDED_C_QUICKSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Recursive partitioning sub-routine. + */ + +void c_QuickSortSub(char* arr, long long left, long long right, c_size_t size, void* temp, + int (*compar)(const void*, const void*)); + +/** + * Top-Level Framework Entry Point for Quicksort. + * Time Complexity: O(n log n) average | Space Complexity: O(log n) call stack | Stable: No + */ +C_STATIC_FORCE_INLINE +void c_QuickSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // Optimization: Use a local stack buffer if element size fits comfortably + #define QSORT_STACK_LIMIT 128 + char stack_buf[QSORT_STACK_LIMIT]; + void* temp = NULL; + + if (size <= QSORT_STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + // Invoke processing across signed range bounds safely + c_QuickSortSub(arr, 0, (long long)num - 1, size, temp, compar); + + if (size > QSORT_STACK_LIMIT) { + C_FREE(temp); + } + #undef QSORT_STACK_LIMIT +} + + +#endif /*INCLUDED_C_QUICKSORT_H*/ diff --git a/Sort/c_QuickSortIterative.c b/Sort/c_QuickSortIterative.c new file mode 100644 index 0000000..8e050e0 --- /dev/null +++ b/Sort/c_QuickSortIterative.c @@ -0,0 +1 @@ +#include diff --git a/Sort/c_QuickSortIterative.h b/Sort/c_QuickSortIterative.h new file mode 100644 index 0000000..9ddf60c --- /dev/null +++ b/Sort/c_QuickSortIterative.h @@ -0,0 +1,102 @@ +#ifndef INCLUDED_C_QUICKSORTITERATIVE_H +#define INCLUDED_C_QUICKSORTITERATIVE_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Internal macro helper to swap two arbitrary blocks of memory. + */ +C_STATIC_FORCE_INLINE +void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) { + if (a == b) return; + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +/** + * Standard Lomuto or Hoare-style tracking partition loop. + * Uses the rightmost element as the pivot for flat linear execution. + */ +C_STATIC_FORCE_INLINE +long long c_PartitionIterative(char* arr, long long left, long long right, c_size_t size, + void* temp, int (*compar)(const void*, const void*)) { + char* pivot = arr + (right * size); + long long i = left - 1; + + for (long long j = left; j < right; j++) { + if (compar(arr + (j * size), pivot) <= 0) { + i++; + c_SwapInternal(arr + (i * size), arr + (j * size), size, temp); + } + } + c_SwapInternal(arr + ((i + 1) * size), arr + (right * size), size, temp); + return (i + 1); +} + +/** + * Top-Level Custom Framework Entry Point for Non-Recursive Quicksort. + * Time Complexity: O(n log n) average | Space Complexity: O(log n) explicit stack | Stable: No + */ +C_STATIC_FORCE_INLINE +void c_QuickSortIterative(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // Optimization: Stack buffer allocation for pivot element swaps +#define QSORT_STACK_LIMIT 128 + char stack_buf[QSORT_STACK_LIMIT]; + void* temp = (size <= QSORT_STACK_LIMIT) ? stack_buf : C_ALLOC(size); + if (temp == NULL) return; + + // Allocate the explicit partition boundary stack. + // Max required stack depth for range tracking is 2 * ceil(log2(num)) + 2. + // For a 64-bit address space, 128 slots safely handles any possible array size. + long long range_stack[128]; + long long top = -1; + + // Push initial array boundaries onto the tracking stack + range_stack[++top] = 0; + range_stack[++top] = (long long)num - 1; + + // Keep processing partitions until the explicit boundary stack is empty + while (top >= 0) { + // Pop right and left boundaries + long long right = range_stack[top--]; + long long left = range_stack[top--]; + + // Execute linear pivot segmentation + long long p = c_PartitionIterative(arr, left, right, size, temp, compar); + + // If there are elements on the left side of the pivot, push their range to the stack + if (p - 1 > left) { + range_stack[++top] = left; + range_stack[++top] = p - 1; + } + + // If there are elements on the right side of the pivot, push their range to the stack + if (p + 1 < right) { + range_stack[++top] = p + 1; + range_stack[++top] = right; + } + } + + if (size > QSORT_STACK_LIMIT) { + C_FREE(temp); + } +#undef QSORT_STACK_LIMIT +} + + +#endif /*INCLUDED_C_QUICKSORTITERATIVE_H*/ diff --git a/Sort/c_SelectionSort.c b/Sort/c_SelectionSort.c new file mode 100644 index 0000000..33a3cd9 --- /dev/null +++ b/Sort/c_SelectionSort.c @@ -0,0 +1 @@ +#include diff --git a/Sort/c_SelectionSort.h b/Sort/c_SelectionSort.h new file mode 100644 index 0000000..c53c403 --- /dev/null +++ b/Sort/c_SelectionSort.h @@ -0,0 +1,67 @@ +#ifndef INCLUDED_C_SELECTIONSORT_H +#define INCLUDED_C_SELECTIONSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Generic Selection Sort Function + * @param base Pointer to the first element of the array to be sorted + * @param num Number of elements in the array + * @param size Size of each element in bytes + * @param compar Pointer to the comparison function + */ +C_STATIC_FORCE_INLINE +void c_SelectionSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + // Avoid execution if array is empty or has only one element + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // OPTIMIZATION: Use stack allocation for small element sizes + // to bypass heap overhead completely during force-inlining. +#define STACK_LIMIT 128 + char stack_buf[STACK_LIMIT]; + void* temp = NULL; + + if (size <= STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + for (c_size_t i = 0; i < num - 1; i++) { + c_size_t minIndex = i; + + for (c_size_t j = i + 1; j < num; j++) { + if (compar(arr + (j * size), arr + (minIndex * size)) < 0) { + minIndex = j; + } + } + + if (minIndex != i) { + memcpy(temp, arr + (i * size), size); + memcpy(arr + (i * size), arr + (minIndex * size), size); + memcpy(arr + (minIndex * size), temp, size); + } + } + + // Only trigger free if it was actually allocated from the heap + if (size > STACK_LIMIT) { + C_FREE(temp); + } +#undef STACK_LIMIT +} + +#endif /*INCLUDED_C_SELECTIONSORT_H*/ diff --git a/Sort/c_ShellSort.c b/Sort/c_ShellSort.c new file mode 100644 index 0000000..f8dbcc3 --- /dev/null +++ b/Sort/c_ShellSort.c @@ -0,0 +1 @@ +#include diff --git a/Sort/c_ShellSort.h b/Sort/c_ShellSort.h new file mode 100644 index 0000000..4ce5e5b --- /dev/null +++ b/Sort/c_ShellSort.h @@ -0,0 +1,71 @@ +#ifndef INCLUDED_C_SHELLSORT_H +#define INCLUDED_C_SHELLSORT_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +void c_ShellSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // Stack optimization for memory swap space +#define STACK_LIMIT 128 + char stack_buf[STACK_LIMIT]; + void* temp = NULL; + + if (size <= STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + // Using Knuth's gap sequence: h = h * 3 + 1 (1, 4, 13, 40, 121, ...) + c_size_t gap = 1; + while (gap < num / 3) { + gap = gap * 3 + 1; + } + + // Start with the largest gap and work down to a gap of 1 + while (gap > 0) { + for (c_size_t i = gap; i < num; i++) { + // temp = arr[i] + memcpy(temp, arr + (i * size), size); + + c_size_t j = i; + + // Shift elements of the gap-sorted variant until the correct position is found + // Loop guards prevent underflow on unsigned c_size_t subtraction (j >= gap) + while (j >= gap && compar(arr + ((j - gap) * size), temp) > 0) { + // arr[j] = arr[j - gap] + memcpy(arr + (j * size), arr + ((j - gap) * size), size); + j -= gap; + } + + // arr[j] = temp + memcpy(arr + (j * size), temp, size); + } + // Reduce the gap + gap /= 3; + } + + if (size > STACK_LIMIT) { + C_FREE(temp); + } +#undef STACK_LIMIT +} + + +#endif /*INCLUDED_C_SHELLSORT_H*/