Files
cKit/Sort/c_IndexMinPQ.c
T
2026-08-29 01:58:00 +08:00

192 lines
6.4 KiB
C

#include <c_IndexMinPQ.h>
#include <c_Memory.h>
/**
* 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);
}