Files
cKit/Graph/c_PrimMST.c
T

139 lines
4.6 KiB
C
Raw Normal View History

2026-09-07 18:48:16 +08:00
#include <c_PrimMST.h>
#include "c_IndexMinPQ.h"
#define PQ_SENTINEL ((c_size_t)-1)
/* ================================================================================================================== */
/* Private Comparison Callback for c_IndexMinPQ_t */
static int c_Prim_WeightCompare(const void* a, const void* b, void* args) {
double w_a = *(const double*)a;
double w_b = *(const double*)b;
(void)args;
return (w_a > w_b) - (w_a < w_b);
}
/* ================================================================================================================== */
/* Eager Prim Scanning Subroutine Processing Functions */
static void c_Prim_Scan(c_PrimMST_t* self, const c_EdgeWeightedGraph_t* graph, c_size_t v, c_IndexMinPQ_t* pq) {
self->marked[v] = C_TRUE;
c_AdjList_t* adj = &graph->adj_list[v];
c_size_t size = (c_size_t)c_AdjList_GetSize(adj);
for (c_size_t i = 0; i < size; ++i) {
c_uint_t generic_edge_id = 0;
c_err_t err = c_AdjList_Get(adj, i, &generic_edge_id);
if (err == C_SUCCESS) {
c_size_t edge_id = (c_size_t)generic_edge_id;
c_Edge_t* edge = &graph->edges_pool[edge_id];
c_size_t w = (edge->v == v) ? edge->w : edge->v;
if (self->marked[w]) continue;
if (edge->weight < self->dist_to[w]) {
self->dist_to[w] = edge->weight;
self->edge_to[w] = edge_id;
/* Leverage clean generic membership checking and modification mutations */
if (c_IndexMinPQ_Contains(pq, w)) {
c_IndexMinPQ_Change(pq, w, &self->dist_to[w]);
} else {
c_IndexMinPQ_Push(pq, w, &self->dist_to[w]);
}
}
}
}
}
c_err_t c_PrimMST_Init(c_PrimMST_t* self, c_EdgeWeightedGraph_t* graph, c_Allocator_t* allocator) {
if (!self || !graph) return C_ERR_PARAM;
self->allocator = allocator ? *allocator : c_DefaultAllocator;
self->weight = 0.0;
self->V = graph->V;
c_VertexIdList_Init(&self->mst_edges, 0, allocator);
if (self->V == 0) return C_SUCCESS;
/* 1. Allocate primary evaluation buffers */
self->marked = (c_bool_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_bool_t));
self->edge_to = (c_size_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t));
self->dist_to = (double*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(double));
if (!self->marked || !self->edge_to || !self->dist_to) {
c_PrimMST_Destroy(self);
return C_ERR_NOMEM;
}
/* Initialize metrics tracking layouts to positive infinity benchmarks */
for (c_size_t v = 0; v < self->V; ++v) {
self->dist_to[v] = DBL_MAX;
self->edge_to[v] = PQ_SENTINEL;
}
/* 2. Configure your formal generic Index Priority Queue sized exactly to V vertices */
c_IndexMinPQ_t pq;
c_err_t err = c_IndexMinPQ_Init(
&pq,
self->V,
sizeof(double),
c_Prim_WeightCompare,
NULL,
allocator
);
if (err != C_SUCCESS) {
c_PrimMST_Destroy(self);
return err;
}
/* 3. Run search loops across vertices to handle potential spanning forests */
for (c_size_t v = 0; v < self->V; ++v) {
if (!self->marked[v]) {
self->dist_to[v] = 0.0;
c_IndexMinPQ_Push(&pq, v, &self->dist_to[v]);
while (pq.size > 0) {
c_size_t curr_v = 0;
c_IndexMinPQ_Pop(&pq, &curr_v);
/* Accumulate tree weights if it is not a structural forest root node */
if (self->edge_to[curr_v] != PQ_SENTINEL) {
c_VertexIdList_Append(&self->mst_edges, (c_uint_t)self->edge_to[curr_v]);
self->weight += self->dist_to[curr_v];
}
c_Prim_Scan(self, graph, curr_v, &pq);
}
}
}
/* Clean up index priority queue allocations */
c_IndexMinPQ_Destroy(&pq);
return C_SUCCESS;
}
void c_PrimMST_Destroy(c_PrimMST_t* self) {
if (!self) return;
if (self->marked) c_Allocator_Free(&self->allocator, self->marked);
if (self->edge_to) c_Allocator_Free(&self->allocator, self->edge_to);
if (self->dist_to) c_Allocator_Free(&self->allocator, self->dist_to);
c_VertexIdList_Destroy(&self->mst_edges);
self->marked = NULL;
self->edge_to = NULL;
self->dist_to = NULL;
self->weight = 0.0;
self->V = 0;
}
c_err_t c_PrimMST_GetEdges(c_PrimMST_t* self, c_VertexIdList_t* out_edges) {
if (!self || !out_edges) return C_ERR_PARAM;
return c_VertexIdList_Copy(out_edges, &self->mst_edges);
}