199 lines
5.4 KiB
C
199 lines
5.4 KiB
C
#include <c_MinPQ.h>
|
|
|
|
/**
|
|
* @brief 内部原子操作:泛型就地物理内存块高效率互换
|
|
*/
|
|
C_STATIC_FORCE_INLINE
|
|
void c_MinPQ_InternalSwap(void* a, void* b, c_size_t size) {
|
|
if (a == b) return;
|
|
char* p1 = (char*)a;
|
|
char* p2 = (char*)b;
|
|
char temp_buf[256];
|
|
c_size_t bytes_left = size;
|
|
while (bytes_left > 0) {
|
|
c_size_t chunk = (bytes_left < sizeof(temp_buf)) ? bytes_left : sizeof(temp_buf);
|
|
memcpy(temp_buf, p1, chunk);
|
|
memcpy(p1, p2, chunk);
|
|
memcpy(p2, temp_buf, chunk);
|
|
p1 += chunk;
|
|
p2 += chunk;
|
|
bytes_left -= chunk;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 核心堆序自适应上浮调整(Sift Up)
|
|
*/
|
|
static void c_MinPQ_SiftUp(c_MinPQ_t* pq, c_size_t child) {
|
|
char* array = pq->data;
|
|
c_size_t es = pq->elem_size;
|
|
|
|
// 严格限制 child > 0 边界,斩断减法下溢
|
|
while (child > 0) {
|
|
c_size_t parent = (child - 1) >> 1;
|
|
|
|
char* p_child = array + (child * es);
|
|
char* p_parent = array + (parent * es);
|
|
|
|
// 🌟【最小堆核心调整】:若子节点的值“小于”父节点的值,向上置换顶推
|
|
if (pq->cmp(p_child, p_parent, pq->args) < 0) {
|
|
c_MinPQ_InternalSwap(p_child, p_parent, es);
|
|
child = parent;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief 核心堆序自适应下沉调整(Sift Down)
|
|
*/
|
|
static void c_MinPQ_SiftDown(c_MinPQ_t* pq, c_size_t parent) {
|
|
char* array = pq->data;
|
|
c_size_t es = pq->elem_size;
|
|
c_size_t num = pq->size;
|
|
|
|
while (1) {
|
|
c_size_t left_child = (parent << 1) + 1;
|
|
|
|
if (left_child >= num) {
|
|
break; // 越界前置拦截,防止后面加法导致的整数溢出
|
|
}
|
|
|
|
c_size_t smaller_child = left_child;
|
|
c_size_t right_child = left_child + 1;
|
|
|
|
if (right_child < num) {
|
|
char* p_left = array + (left_child * es);
|
|
char* p_right = array + (right_child * es);
|
|
// 🌟【最小堆核心调整】:若右子节点的值比左子节点还“小”,切换最小目标到右半区
|
|
if (pq->cmp(p_right, p_left, pq->args) < 0) {
|
|
smaller_child = right_child;
|
|
}
|
|
}
|
|
|
|
char* p_parent = array + (parent * es);
|
|
char* p_target = array + (smaller_child * es);
|
|
|
|
// 🌟【最小堆核心调整】:若最小子节点依然“小于”当前的父节点,下沉对调
|
|
if (pq->cmp(p_target, p_parent, pq->args) < 0) {
|
|
c_MinPQ_InternalSwap(p_parent, p_target, es);
|
|
parent = smaller_child;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
/* */
|
|
|
|
|
|
/**
|
|
* @brief 就地初始化最小堆优先队列
|
|
*/
|
|
c_err_t c_MinPQ_Init(c_MinPQ_t* self, c_size_t initial_capacity, c_size_t elem_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator) {
|
|
if (!self || elem_size == 0 || !cmp) {
|
|
return C_ERR_PARAM;
|
|
}
|
|
|
|
c_size_t cap = (initial_capacity > 0) ? initial_capacity : 4;
|
|
|
|
// 自适应缺省降级安全播种
|
|
if (allocator) {
|
|
self->allocator = *allocator;
|
|
} else {
|
|
self->allocator = c_DefaultAllocator;
|
|
}
|
|
|
|
self->data = (char*)c_Allocator_Alloc(&self->allocator, cap * elem_size);
|
|
if (!self->data) {
|
|
return C_ERR_NOMEM;
|
|
}
|
|
|
|
self->capacity = cap;
|
|
self->size = 0;
|
|
self->elem_size = elem_size;
|
|
self->cmp = cmp;
|
|
self->args = args;
|
|
|
|
return C_ERR_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief 向最小堆中压入一个新元素(自适应双倍托管 Realloc 动态扩容)
|
|
*/
|
|
c_err_t c_MinPQ_Push(c_MinPQ_t* pq, const void* item) {
|
|
if (!pq || !item) return C_ERR_PARAM;
|
|
|
|
if (pq->size >= pq->capacity) {
|
|
c_size_t old_cap = pq->capacity;
|
|
c_size_t new_cap = old_cap << 1;
|
|
|
|
c_size_t old_bytes = old_cap * pq->elem_size;
|
|
c_size_t new_bytes = new_cap * pq->elem_size;
|
|
|
|
char* new_data = (char*)c_Allocator_Realloc(&pq->allocator, pq->data, old_bytes, new_bytes);
|
|
if (!new_data) return C_ERR_NOMEM;
|
|
|
|
pq->data = new_data;
|
|
pq->capacity = new_cap;
|
|
}
|
|
|
|
char* target_slot = pq->data + (pq->size * pq->elem_size);
|
|
memcpy(target_slot, item, pq->elem_size);
|
|
|
|
c_MinPQ_SiftUp(pq, pq->size);
|
|
pq->size++;
|
|
return C_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief 弹出并捕获当前最小堆中的绝对最小值元素(堆顶)
|
|
*/
|
|
c_err_t c_MinPQ_Pop(c_MinPQ_t* pq, void* out_item) {
|
|
if (!pq ) return C_ERR_PARAM;
|
|
if (pq->size == 0) {
|
|
return C_ERR_EMPTY;
|
|
}
|
|
|
|
char* array = pq->data;
|
|
c_size_t es = pq->elem_size;
|
|
|
|
if (out_item) {
|
|
memcpy(out_item, array, es);
|
|
}
|
|
|
|
pq->size--;
|
|
|
|
if (pq->size > 0) {
|
|
memcpy(array, array + (pq->size * es), es);
|
|
c_MinPQ_SiftDown(pq, 0);
|
|
}
|
|
|
|
return C_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief 观察读取但不弹出最小堆堆顶绝对最小值元素
|
|
*/
|
|
c_err_t c_MinPQ_Peek(const c_MinPQ_t* pq, void* out_item) {
|
|
if (!pq || !out_item) return C_ERR_PARAM;
|
|
if (pq->size == 0) return C_ERR_EMPTY;
|
|
memcpy(out_item, pq->data, pq->elem_size);
|
|
return C_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief 资源回收反初始化
|
|
*/
|
|
void c_MinPQ_Destroy(c_MinPQ_t* pq) {
|
|
if (pq && pq->data) {
|
|
c_Allocator_Free(&pq->allocator, pq->data);
|
|
pq->data = NULL;
|
|
pq->size = 0;
|
|
pq->capacity = 0;
|
|
}
|
|
} |