Graph
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
#include <c_BipartiteBFS.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Internal BFS helper routine to process a single connected component cluster
|
||||
static void c_Bipartite_BFS_Internal(c_BipartiteBFS_t* self, const c_Graph_t* G, c_VertexId_t start) {
|
||||
// We will use your c_VertexIdList_t dynamic array as an explicit FIFO queue block.
|
||||
// To pop, we track a sliding 'head' cursor instead of physically shifting items.
|
||||
c_VertexIdList_t queue;
|
||||
if (c_VertexIdList_Init(&queue, 16, &self->allocator) != C_SUCCESS) return;
|
||||
|
||||
self->marked[start] = C_TRUE;
|
||||
self->color[start] = C_FALSE; // Initialize base level color assignment
|
||||
if (c_VertexIdList_Append(&queue, (c_uint_t)start) != C_SUCCESS) {
|
||||
c_VertexIdList_Destroy(&queue);
|
||||
return;
|
||||
}
|
||||
|
||||
c_size_t queue_head = 0;
|
||||
|
||||
while (queue_head < queue.size) {
|
||||
// Dequeue structural element
|
||||
c_VertexId_t v = (c_VertexId_t)queue.array[queue_head++];
|
||||
|
||||
c_AdjList_t* list = c_Graph_GetAdjList((c_Graph_t*)G, v);
|
||||
if (!list) continue;
|
||||
|
||||
// Cache-friendly sequential sweep over flat neighbor array blocks
|
||||
for (c_size_t i = 0; i < list->size; i++) {
|
||||
c_VertexId_t w = (c_VertexId_t)list->array[i];
|
||||
|
||||
if (!self->marked[w]) {
|
||||
self->marked[w] = C_TRUE;
|
||||
self->edge_to[w] = v;
|
||||
self->color[w] = !self->color[v]; // Assign opposite color inversion mapping
|
||||
if (c_VertexIdList_Append(&queue, (c_uint_t)w) != C_SUCCESS) {
|
||||
c_VertexIdList_Destroy(&queue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If neighbor w is discovered and has the same color, we've found an odd-length cycle!
|
||||
else if (self->color[w] == self->color[v]) {
|
||||
self->is_bipartite = C_FALSE;
|
||||
|
||||
// Reconstruct the shortest odd-length cycle by back-tracing paths from v and w
|
||||
// back to their lowest common ancestor (LCA) using edge_to coordinates.
|
||||
c_VertexIdList_t path_v;
|
||||
c_VertexIdList_t path_w;
|
||||
if (c_VertexIdList_Init(&path_v, 8, &self->allocator) != C_SUCCESS) goto cleanup;
|
||||
if (c_VertexIdList_Init(&path_w, 8, &self->allocator) != C_SUCCESS) {
|
||||
c_VertexIdList_Destroy(&path_v);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Trace back route frames from v
|
||||
c_size_t curr = v;
|
||||
while (curr != G->V) {
|
||||
c_VertexIdList_Append(&path_v, (c_uint_t)curr);
|
||||
curr = self->edge_to[curr];
|
||||
}
|
||||
|
||||
// Trace back route frames from w
|
||||
curr = w;
|
||||
while (curr != G->V) {
|
||||
c_VertexIdList_Append(&path_w, (c_uint_t)curr);
|
||||
curr = self->edge_to[curr];
|
||||
}
|
||||
|
||||
// Find lowest common ancestor intersection boundary index
|
||||
c_size_t p_v = path_v.size - 1;
|
||||
c_size_t p_w = path_w.size - 1;
|
||||
while (p_v > 0 && p_w > 0 && path_v.array[p_v - 1] == path_w.array[p_w - 1]) {
|
||||
p_v--;
|
||||
p_w--;
|
||||
}
|
||||
|
||||
// Build output sequence path layout out to self->cycle container:
|
||||
// Format order flow: v -> ... -> LCA -> ... -> w -> v
|
||||
for (c_size_t j = 0; j <= p_v; j++) {
|
||||
c_VertexIdList_Append(&self->cycle, path_v.array[j]);
|
||||
}
|
||||
for (c_size_t j = p_w; j > 0; j--) {
|
||||
c_VertexIdList_Append(&self->cycle, path_w.array[j - 1]);
|
||||
}
|
||||
c_VertexIdList_Append(&self->cycle, (c_uint_t)v); // Close cycle loop boundary
|
||||
|
||||
c_VertexIdList_Destroy(&path_v);
|
||||
c_VertexIdList_Destroy(&path_w);
|
||||
c_VertexIdList_Destroy(&queue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
c_VertexIdList_Destroy(&queue);
|
||||
}
|
||||
|
||||
c_err_t c_BipartiteBFS_Init(c_BipartiteBFS_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->is_bipartite = C_TRUE;
|
||||
self->V = G->V;
|
||||
self->marked = NULL;
|
||||
self->color = 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(*self->marked));
|
||||
self->color = (c_bool_t*)c_Allocator_Alloc(&self->allocator, G->V * sizeof(*self->color));
|
||||
self->edge_to = (c_size_t*)c_Allocator_Alloc(&self->allocator, G->V * sizeof(*self->edge_to));
|
||||
|
||||
if (!self->marked || !self->color || !self->edge_to) {
|
||||
c_BipartiteBFS_Destroy(self);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
memset(self->marked, 0, G->V * sizeof(*self->marked));
|
||||
memset(self->color, 0, G->V * sizeof(*self->color));
|
||||
for (c_size_t i = 0; i < G->V; i++) self->edge_to[i] = G->V; // Sentinel setting
|
||||
|
||||
// Multi-component partition loop scanner sweeps
|
||||
for (c_VertexId_t v = 0; v < G->V; v++) {
|
||||
if (!self->marked[v]) {
|
||||
c_Bipartite_BFS_Internal(self, G, v);
|
||||
if (!self->is_bipartite) break; // Terminate early on odd cycle detection
|
||||
}
|
||||
}
|
||||
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
void c_BipartiteBFS_Destroy(c_BipartiteBFS_t* self) {
|
||||
if (!self) return;
|
||||
|
||||
if (self->marked) c_Allocator_Free(&self->allocator, self->marked);
|
||||
if (self->color) c_Allocator_Free(&self->allocator, self->color);
|
||||
if (self->edge_to) c_Allocator_Free(&self->allocator, self->edge_to);
|
||||
|
||||
c_VertexIdList_Destroy(&self->cycle);
|
||||
|
||||
self->marked = NULL;
|
||||
self->color = NULL;
|
||||
self->edge_to = NULL;
|
||||
self->is_bipartite = C_FALSE;
|
||||
self->V = 0;
|
||||
}
|
||||
|
||||
c_bool_t c_BipartiteBFS_IsBipartite(const c_BipartiteBFS_t* self) {
|
||||
return self ? self->is_bipartite : C_FALSE;
|
||||
}
|
||||
|
||||
c_bool_t c_BipartiteBFS_Color(const c_BipartiteBFS_t* self, c_VertexId_t v) {
|
||||
if (!self || v >= self->V || !self->color) return C_FALSE;
|
||||
return self->color[v];
|
||||
}
|
||||
|
||||
const c_VertexIdList_t* c_BipartiteBFS_Cycle(const c_BipartiteBFS_t* self) {
|
||||
return self ? &self->cycle : NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user