Graph
This commit is contained in:
+112
@@ -0,0 +1,112 @@
|
||||
#include <c_Cycle.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
// Internal deep structural trace walker looking for loops
|
||||
static void c_Cycle_DFS_Internal(c_Cycle_t* self, const c_Graph_t* G, c_VertexId_t v, c_VertexId_t u) {
|
||||
self->marked[v] = C_TRUE;
|
||||
|
||||
c_AdjList_t* list = c_Graph_GetAdjList((c_Graph_t*)G, v);
|
||||
if (!list) return;
|
||||
|
||||
// Cache-friendly sequential sweep over flat neighbor array blocks
|
||||
for (c_size_t i = 0; i < list->size; i++) {
|
||||
const c_VertexId_t w = (c_VertexId_t)list->array[i];
|
||||
|
||||
// Short-circuit search operations if a cycle has already been populated
|
||||
if (self->cycle.size > 0) return;
|
||||
|
||||
if (!self->marked[w]) {
|
||||
self->edge_to[w] = v;
|
||||
c_Cycle_DFS_Internal(self, G, w, v);
|
||||
}
|
||||
// Undirected graph cycle criteria: w is visited AND w is not the direct parent u
|
||||
else if (w != u) {
|
||||
self->has_cycle = C_TRUE;
|
||||
|
||||
// Reconstruct the cycle path back through the path logs
|
||||
c_VertexIdList_t temp_stack;
|
||||
if (c_VertexIdList_Init(&temp_stack, 8, &self->allocator) != C_SUCCESS) return;
|
||||
|
||||
c_VertexId_t x = v;
|
||||
while (x != w && x != G->V) {
|
||||
c_VertexIdList_Append(&temp_stack, (c_uint_t)x);
|
||||
x = self->edge_to[x];
|
||||
}
|
||||
c_VertexIdList_Append(&temp_stack, (c_uint_t)w);
|
||||
c_VertexIdList_Append(&temp_stack, (c_uint_t)v); // Close cycle track frame
|
||||
|
||||
// Invert elements to maintain chronological loop sequence (w -> ... -> v -> w)
|
||||
for (c_size_t j = temp_stack.size; j > 0; j--) {
|
||||
c_VertexId_t val;
|
||||
c_VertexIdList_Get(&temp_stack, j - 1, &val);
|
||||
c_VertexIdList_Append(&self->cycle, val);
|
||||
}
|
||||
|
||||
c_VertexIdList_Destroy(&temp_stack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c_err_t c_Cycle_Init(c_Cycle_t* self, const c_Graph_t* G, c_Allocator_t* allocator) {
|
||||
if (!self || !G ) return C_ERR_PARAM;
|
||||
|
||||
self->allocator = allocator?*allocator:c_DefaultAllocator;
|
||||
self->has_cycle = C_FALSE;
|
||||
self->V = G->V;
|
||||
self->marked = NULL;
|
||||
self->edge_to = NULL;
|
||||
|
||||
if (c_VertexIdList_Init(&self->cycle, 0, &self->allocator) != C_SUCCESS) {
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if (G->V == 0) return C_SUCCESS;
|
||||
|
||||
self->marked = (c_bool_t*)c_Allocator_Alloc(&self->allocator, G->V * sizeof(c_bool_t));
|
||||
self->edge_to = (c_size_t*)c_Allocator_Alloc(&self->allocator, G->V * sizeof(c_size_t));
|
||||
|
||||
if (!self->marked || !self->edge_to) {
|
||||
c_Cycle_Destroy(self);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
memset(self->marked, 0, G->V * sizeof(c_bool_t));
|
||||
for (c_size_t i = 0; i < G->V; i++) self->edge_to[i] = G->V; // Sentinel setup
|
||||
|
||||
// Multi-component partition loop scans
|
||||
for (c_VertexId_t v = 0; v < G->V; v++) {
|
||||
if (!self->marked[v]) {
|
||||
c_Cycle_DFS_Internal(self, G, v, G->V); // Pass sentinel as initial parent
|
||||
if (self->has_cycle) break;
|
||||
}
|
||||
}
|
||||
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
void c_Cycle_Destroy(c_Cycle_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);
|
||||
|
||||
c_VertexIdList_Destroy(&self->cycle);
|
||||
|
||||
self->marked = NULL;
|
||||
self->edge_to = NULL;
|
||||
self->has_cycle = C_FALSE;
|
||||
self->V = 0;
|
||||
}
|
||||
|
||||
c_bool_t c_Cycle_HasCycle(const c_Cycle_t* self) {
|
||||
return self ? self->has_cycle : C_FALSE;
|
||||
}
|
||||
|
||||
const c_VertexIdList_t* c_Cycle_Path(const c_Cycle_t* self) {
|
||||
return self ? &self->cycle : NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user