Files
cKit/Graph/c_DirectedEulerianPath.c
2026-09-07 18:48:16 +08:00

139 lines
5.3 KiB
C

#include <c_DirectedEulerianPath.h>
#include "c_NonrecursiveDirectedDFS.h"
c_err_t c_DirectedEulerianPath_Init(c_DirectedEulerianPath_t* self, c_Digraph_t* graph, c_Allocator_t* allocator) {
if (!self || !graph) return C_ERR_PARAM;
self->allocator = allocator ? *allocator : c_DefaultAllocator;
c_VertexIdList_Init(&self->path, 0, allocator);
/* Edge case: An empty graph with no edges trivially has an empty path */
if (graph->E == 0) return C_SUCCESS;
/* 1. Degree tracking analysis to identify the unique start node candidate */
c_size_t start_vertex = graph->V;
c_size_t end_vertex = graph->V;
c_size_t start_nodes_count = 0;
c_size_t end_nodes_count = 0;
c_size_t fallback_start = graph->V;
for (c_size_t v = 0; v < graph->V; ++v) {
c_size_t out_deg = c_Digraph_GetOutDegree(graph, v);
c_size_t in_deg = c_Digraph_GetInDegree(graph, v);
if (out_deg > 0 && fallback_start == graph->V) {
fallback_start = v; /* Used if the graph is a full cycle loop */
}
if (out_deg > in_deg) {
if (out_deg - in_deg > 1) return C_SUCCESS; /* Fails condition: open paths allow a max delta of 1 */
start_vertex = v;
start_nodes_count++;
} else if (in_deg > out_deg) {
if (in_deg - out_deg > 1) return C_SUCCESS;
end_vertex = v;
end_nodes_count++;
}
}
/* Validate structural structural configuration cases */
if (start_nodes_count == 0 && end_nodes_count == 0) {
/* Case A: It's an Eulerian Cycle, choose the first non-isolated node as start */
start_vertex = fallback_start;
} else if (start_nodes_count == 1 && end_nodes_count == 1) {
/* Case B: It's an open Eulerian Path from start_vertex to end_vertex */
// start_vertex is already correctly captured here
} else {
return C_SUCCESS; /* Fails degree criteria layout, return with empty path */
}
if (start_vertex == graph->V) return C_SUCCESS;
/* 2. Strong connectivity check over edge elements using NonrecursiveDFS */
c_NonrecursiveDirectedDFS_t dfs;
c_err_t err = c_NonrecursiveDirectedDFS_Init(&dfs, graph, start_vertex, allocator);
if (err != C_SUCCESS) return err;
for (c_size_t v = 0; v < graph->V; ++v) {
if (c_Digraph_GetOutDegree(graph, v) > 0 && !c_NonrecursiveDirectedDFS_HasPathTo(&dfs, v, graph->V)) {
c_NonrecursiveDirectedDFS_Destroy(&dfs);
return C_SUCCESS; /* Edges are fragmented in isolated structures */
}
}
c_NonrecursiveDirectedDFS_Destroy(&dfs);
/* 3. Hierholzer's Iterative Engine Routine */
c_size_t* edge_index_iterator = (c_size_t*)c_Allocator_Calloc(&self->allocator, graph->V, sizeof(c_size_t));
if (!edge_index_iterator) return C_ERR_NOMEM;
c_size_t max_stack_cap = graph->E + 1;
c_size_t* path_stack = (c_size_t*)c_Allocator_Calloc(&self->allocator, max_stack_cap, sizeof(c_size_t));
if (!path_stack) {
c_Allocator_Free(&self->allocator, edge_index_iterator);
return C_ERR_NOMEM;
}
c_size_t* output_stack = (c_size_t*)c_Allocator_Calloc(&self->allocator, max_stack_cap, sizeof(c_size_t));
if (!output_stack) {
c_Allocator_Free(&self->allocator, edge_index_iterator);
c_Allocator_Free(&self->allocator, path_stack);
return C_ERR_NOMEM;
}
c_size_t path_stack_size = 0;
c_size_t output_stack_size = 0;
path_stack[path_stack_size++] = start_vertex;
while (path_stack_size > 0) {
c_size_t curr_v = path_stack[path_stack_size - 1];
c_AdjList_t* adj = &graph->adj_list[curr_v];
c_size_t out_degree = (c_size_t)c_UIntArray_GetSize(adj);
if (edge_index_iterator[curr_v] < out_degree) {
c_uint_t target_w = 0;
err = c_UIntArray_Get(adj, edge_index_iterator[curr_v], &target_w);
edge_index_iterator[curr_v]++; /* Consume edge track */
if (err == C_SUCCESS) {
path_stack[path_stack_size++] = (c_size_t)target_w;
}
} else {
output_stack[output_stack_size++] = curr_v;
path_stack_size--;
}
}
/* Unwind tracking queue directly to the VertexIdList matching forward travel steps */
while (output_stack_size > 0) {
c_VertexIdList_Append(&self->path, (c_uint_t)output_stack[--output_stack_size]);
}
c_Allocator_Free(&self->allocator, edge_index_iterator);
c_Allocator_Free(&self->allocator, path_stack);
c_Allocator_Free(&self->allocator, output_stack);
return C_SUCCESS;
}
void c_DirectedEulerianPath_Destroy(c_DirectedEulerianPath_t* self) {
if (!self) return;
c_VertexIdList_Destroy(&self->path);
}
c_err_t c_DirectedEulerianPath_GetPath(c_DirectedEulerianPath_t* self, c_VertexIdList_t* out_path) {
if (!self || !out_path) return C_ERR_PARAM;
if (!c_DirectedEulerianPath_HasPath(self)) return C_ERR_FAIL;
c_size_t size = (c_size_t)c_VertexIdList_GetSize(&self->path);
for (c_size_t i = 0; i < size; ++i) {
c_uint_t val = 0;
c_err_t err = c_VertexIdList_Get((c_VertexIdList_t*)&self->path, i, &val);
if (err == C_SUCCESS) {
c_err_t app_err = c_VertexIdList_Append(out_path, val);
if (app_err != C_SUCCESS) return app_err;
}
}
return C_SUCCESS;
}