Files
cKit/Graph/c_FloydWarshall.c
T

140 lines
5.2 KiB
C
Raw Normal View History

2026-09-07 18:48:16 +08:00
#include "c_FloydWarshall.h"
#define FW_SENTINEL ((c_size_t)-1)
static void c_FloydWarshall_FreeMatrices(c_FloydWarshall_t* self) {
if (self->dist_to) {
for (c_size_t i = 0; i < self->V; ++i) {
if (self->dist_to[i]) c_Allocator_Free(&self->allocator, self->dist_to[i]);
}
c_Allocator_Free(&self->allocator, self->dist_to);
self->dist_to = NULL;
}
if (self->next_vertex) {
for (c_size_t i = 0; i < self->V; ++i) {
if (self->next_vertex[i]) c_Allocator_Free(&self->allocator, self->next_vertex[i]);
}
c_Allocator_Free(&self->allocator, self->next_vertex);
self->next_vertex = NULL;
}
if (self->edge_to) {
for (c_size_t i = 0; i < self->V; ++i) {
if (self->edge_to[i]) c_Allocator_Free(&self->allocator, self->edge_to[i]);
}
c_Allocator_Free(&self->allocator, self->edge_to);
self->edge_to = NULL;
}
}
c_err_t c_FloydWarshall_Init(c_FloydWarshall_t* self, c_EdgeWeightedDigraph_t* graph, c_Allocator_t* allocator) {
if (!self || !graph) return C_ERR_PARAM;
self->allocator = allocator ? *allocator : c_DefaultAllocator;
self->V = graph->V;
self->has_neg_cycle = C_FALSE;
self->dist_to = (double**)c_Allocator_Calloc(&self->allocator, self->V, sizeof(double*));
self->next_vertex = (c_size_t**)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t*));
self->edge_to = (c_size_t**)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t*));
if (!self->dist_to || !self->next_vertex || !self->edge_to) {
c_FloydWarshall_FreeMatrices(self);
return C_ERR_NOMEM;
}
for (c_size_t i = 0; i < self->V; ++i) {
self->dist_to[i] = (double*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(double));
self->next_vertex[i] = (c_size_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t));
self->edge_to[i] = (c_size_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t));
if (!self->dist_to[i] || !self->next_vertex[i] || !self->edge_to[i]) {
c_FloydWarshall_FreeMatrices(self);
return C_ERR_NOMEM;
}
for (c_size_t j = 0; j < self->V; ++j) {
self->dist_to[i][j] = (i == j) ? 0.0 : DBL_MAX;
self->next_vertex[i][j] = FW_SENTINEL;
self->edge_to[i][j] = FW_SENTINEL;
}
}
/* Step 1: Base adjacency mapping from graph edges pool */
for (c_size_t e = 0; e < graph->E; ++e) {
c_DirectedEdge_t* edge = &graph->edges_pool[e];
c_size_t u = edge->from;
c_size_t v = edge->to;
if (edge->weight < self->dist_to[u][v]) {
self->dist_to[u][v] = edge->weight;
self->next_vertex[u][v] = v;
self->edge_to[u][v] = e;
}
}
/* Step 2: Floyd-Warshall dynamic programming loops pass */
for (c_size_t k = 0; k < self->V; ++k) {
for (c_size_t i = 0; i < self->V; ++i) {
if (self->dist_to[i][k] == DBL_MAX) continue;
for (c_size_t j = 0; j < self->V; ++j) {
if (self->dist_to[k][j] == DBL_MAX) continue;
if (self->dist_to[i][j] > self->dist_to[i][k] + self->dist_to[k][j]) {
self->dist_to[i][j] = self->dist_to[i][k] + self->dist_to[k][j];
self->next_vertex[i][j] = self->next_vertex[i][k];
self->edge_to[i][j] = self->edge_to[i][k];
}
}
/* Step 3: Negative cycle interception */
if (self->dist_to[i][i] < 0.0) {
self->has_neg_cycle = C_TRUE;
return C_SUCCESS;
}
}
}
return C_SUCCESS;
}
void c_FloydWarshall_Destroy(c_FloydWarshall_t* self) {
if (!self) return;
c_FloydWarshall_FreeMatrices(self);
self->V = 0;
self->has_neg_cycle = C_FALSE;
}
/* ================================================================================================================== */
/* Exact Signature Re-implementation Match */
c_err_t c_FloydWarshall_Path(c_FloydWarshall_t* self, c_size_t u, c_size_t v, c_EdgeIdList_t* out_path) {
if (!self || !out_path || u >= self->V || v >= self->V) return C_ERR_PARAM;
if (self->has_neg_cycle || !c_FloydWarshall_HasPath(self, u, v)) return C_ERR_FAIL;
/* If origin equals target, the shortest path requires 0 edge steps */
if (u == v) return C_SUCCESS;
c_size_t curr_u = u;
c_err_t err = C_SUCCESS;
/* Step forward from origin node 'u' using the pre-computed next_vertex mappings */
while (curr_u != v) {
c_size_t next = self->next_vertex[curr_u][v];
if (next == FW_SENTINEL) return C_ERR_FAIL; /* Structural anomaly anchor guard */
c_size_t edge_id = self->edge_to[curr_u][v];
if (edge_id == FW_SENTINEL) return C_ERR_FAIL;
/* Append the resolved edge token into your sequential vertex list wrapper */
err = c_EdgeIdList_Append(out_path, (c_uint_t)edge_id);
if (err != C_SUCCESS) return err;
/* Move the sliding window origin forward to transition the trace */
curr_u = next;
}
return C_SUCCESS;
}