Files

93 lines
3.4 KiB
C
Raw Permalink Normal View History

2026-09-07 18:48:16 +08:00
#include <c_NonrecursiveTopological.h>
#include "c_NonrecursiveDirectedCycle.h"
c_err_t c_NonrecursiveTopological_Init(c_NonrecursiveTopological_t* self, c_Digraph_t* graph, c_Allocator_t* allocator) {
if (!self || !graph) return C_ERR_PARAM;
self->allocator = allocator ? *allocator : c_DefaultAllocator;
self->has_order = C_FALSE;
c_VertexIdList_Init(&self->order, 0, allocator);
/* 1. Stack-safe Cycle Check: Ensure the graph is a DAG without relying on recursion stack */
c_NonrecursiveDirectedCycle_t cycle_detector;
c_err_t err = c_NonrecursiveDirectedCycle_Init(&cycle_detector, graph, allocator);
if (err != C_SUCCESS) return err;
c_bool_t has_cycle = c_NonrecursiveDirectedCycle_HasCycle(&cycle_detector);
c_NonrecursiveDirectedCycle_Destroy(&cycle_detector);
if (has_cycle) {
return C_SUCCESS; /* Contains a cycle, return gracefully with has_order = C_FALSE */
}
/* 2. Kahn's Algorithm: Allocate a mutable working array copy of in-degrees */
c_size_t* working_indegree = (c_size_t*)c_Allocator_Calloc(&self->allocator, graph->V, sizeof(c_size_t));
if (!working_indegree) return C_ERR_NOMEM;
/* A simple array-based queue bounded tightly by O(V) */
c_size_t* zero_in_degree_queue = (c_size_t*)c_Allocator_Calloc(&self->allocator, graph->V, sizeof(c_size_t));
if (!zero_in_degree_queue) {
c_Allocator_Free(&self->allocator, working_indegree);
return C_ERR_NOMEM;
}
c_size_t head = 0;
c_size_t tail = 0;
/* Initialize working in-degrees and seed the queue with source nodes (in-degree == 0) */
for (c_size_t v = 0; v < graph->V; ++v) {
working_indegree[v] = c_Digraph_GetInDegree(graph, v);
if (working_indegree[v] == 0) {
zero_in_degree_queue[tail++] = v;
}
}
/* 3. Non-recursive processing loop */
while (head < tail) {
c_size_t u = zero_in_degree_queue[head++];
/* Append the processed node to the topological ordering stream */
c_VertexIdList_Append(&self->order, (c_uint_t)u);
c_AdjList_t* adj = &graph->adj_list[u];
c_size_t size = (c_size_t)c_AdjList_GetSize(adj);
/* Decrement in-degree for all downstream neighbors */
for (c_size_t i = 0; i < size; ++i) {
c_uint_t target_value = 0;
err = c_AdjList_Get(adj, i, &target_value);
if (err == C_SUCCESS) {
c_size_t w = (c_size_t)target_value;
working_indegree[w]--;
/* If all parent dependencies are cleared, enqueue the neighbor */
if (working_indegree[w] == 0) {
zero_in_degree_queue[tail++] = w;
}
}
}
}
/* Clean up local working tracking allocations */
c_Allocator_Free(&self->allocator, working_indegree);
c_Allocator_Free(&self->allocator, zero_in_degree_queue);
self->has_order = C_TRUE;
return C_SUCCESS;
}
void c_NonrecursiveTopological_Destroy(c_NonrecursiveTopological_t* self) {
if (!self) return;
c_VertexIdList_Destroy(&self->order);
self->has_order = C_FALSE;
}
c_err_t c_NonrecursiveTopological_GetOrder(c_NonrecursiveTopological_t* self, c_VertexIdList_t* out_order) {
if (!self || !out_order) return C_ERR_PARAM;
if (!self->has_order) return C_ERR_FAIL;
/* High-speed block copy optimization */
return c_VertexIdList_Copy(out_order, &self->order);
}