Graph Start

This commit is contained in:
2026-09-05 13:36:43 +08:00
parent 4e5ae52e54
commit 1564716731
9 changed files with 844 additions and 0 deletions
+209
View File
@@ -0,0 +1,209 @@
#include <c_Graph.h>
c_err_t c_Graph_Init(c_Graph_t* self, c_size_t V, c_Allocator_t* allocator) {
if (!self || V==0) return C_ERR_PARAM;
self->allocator = (allocator!=NULL)?*allocator:c_DefaultAllocator;
self->V = V;
self->E = 0;
self->adj_list = c_Allocator_Alloc(&self->allocator,V * sizeof(*self->adj_list));
if (!self->adj_list) {
return C_ERR_NOMEM;
}
for (c_size_t v = 0; v<V; v++) {
c_AdjList_Init(&self->adj_list[v], 0, allocator);
}
return C_ERR_OK;
}
void c_Graph_Destroy(c_Graph_t* self) {
if (!self) return;
if (self->adj_list) {
for (c_size_t v = 0; v<self->V; v++) {
c_AdjList_Destroy(&self->adj_list[v]);
}
c_Allocator_Free(&self->allocator,self->adj_list);
self->adj_list = NULL;
}
self->V = 0;
self->E = 0;
}
c_err_t c_Graph_AddEdge(c_Graph_t* self, c_size_t v, c_size_t w) {
if (!self || v >= self->V || w >= self->V) {
return C_ERR_PARAM;
}
// Step 1: Duplicate validation check.
// If the edge already exists, we return success without duplicate entries.
c_AdjList_t* list_v = &self->adj_list[v];
for (c_size_t i = 0; i < list_v->size; i++) {
if (list_v->array[i] == w) {
return C_SUCCESS;
}
}
// Step 2: Add edge path v -> w
c_err_t err = c_AdjList_Append(list_v, w);
if (err != C_SUCCESS) {
return err;
}
// Step 3: Handle Self-Loops.
// If a node links to itself (v == w), appending it once is sufficient.
if (v == w) {
self->E++;
return C_SUCCESS;
}
// Step 4: Add symmetric edge path w -> v (Undirected Graph Invariant)
c_AdjList_t* list_w = &self->adj_list[w];
err = c_AdjList_Append(list_w, v);
if (err != C_SUCCESS) {
// Rollback step: Remove the appended 'w' from 'v' if 'w' allocation fails
c_AdjList_Remove(list_v, list_v->size - 1);
return err;
}
self->E++;
return C_SUCCESS;
}
c_err_t c_Graph_RemoveEdge(c_Graph_t* self, c_size_t v, c_size_t w) {
if (!self || v >= self->V || w >= self->V) {
return C_ERR_PARAM;
}
c_AdjList_t* list_v = &self->adj_list[v];
c_AdjList_t* list_w = &self->adj_list[w];
// Step 1: Locate the target index within v's array
c_size_t index_in_v = self->V; // Use self->V as a sentinel for "not found"
for (c_size_t i = 0; i < list_v->size; i++) {
if (list_v->array[i] == w) {
index_in_v = i;
break;
}
}
// If edge v -> w doesn't exist, the edge isn't in the graph
if (index_in_v == self->V) {
return C_ERR_FAIL;
}
// Step 2: Handle Self-Loops.
// If it's a self-loop (v == w), removing it once from its own list is sufficient.
if (v == w) {
c_AdjList_Remove(list_v, index_in_v);
self->E--;
return C_SUCCESS;
}
// Step 3: Locate the target index within w's array
c_size_t index_in_w = self->V;
for (c_size_t i = 0; i < list_w->size; i++) {
if (list_w->array[i] == v) {
index_in_w = i;
break;
}
}
// Structural integrity guard: in an undirected graph, if v has w, w must have v.
// If it's missing, the graph's internal symmetry invariant is broken.
if (index_in_w == self->V) {
return C_ERR_FAIL;
}
// Step 4: Perform the actual removals (shifts memory elements leftward)
c_AdjList_Remove(list_v, index_in_v);
c_AdjList_Remove(list_w, index_in_w);
self->E--;
return C_SUCCESS;
}
c_bool_t c_Graph_HasEdge(const c_Graph_t* self, c_size_t v, c_size_t w) {
// Return false immediately if the graph is NULL or if indices are out of bounds
if (!self || v >= self->V || w >= self->V) {
return C_FALSE;
}
const c_AdjList_t* list_v = &self->adj_list[v];
// High-efficiency linear scan over flat contiguous primitive integer array
for (c_size_t i = 0; i < list_v->size; i++) {
if (list_v->array[i] == w) {
return C_TRUE;
}
}
return C_FALSE;
}
c_size_t c_Graph_Degree(c_Graph_t* self, c_size_t v) {
if (!self || v >=self->V) return 0;
return self->adj_list[v].size;
}
c_AdjList_t* c_Graph_GetAdjList(c_Graph_t* self, c_size_t v) {
if (!self || v >= self->V) {
return NULL;
}
return &self->adj_list[v];
}
c_err_t c_Graph_Copy(c_Graph_t* self, const c_Graph_t* src, c_Allocator_t* allocator) {
if (!self || !src) return C_ERR_PARAM;
// Step 1: Initialize top-level boundaries and fallbacks safely
self->allocator = allocator ? *allocator : c_DefaultAllocator;
self->V = src->V;
self->E = src->E;
self->adj_list = NULL;
if (src->V == 0) {
return C_SUCCESS;
}
// Step 2: Allocate the master pointer container track array block
self->adj_list = (c_AdjList_t*)c_Allocator_Alloc(&self->allocator, src->V * sizeof(*self->adj_list));
if (!self->adj_list) {
self->V = 0;
self->E = 0;
return C_ERR_NOMEM;
}
// Step 3: Deep copy individual contiguous internal buffers
for (c_size_t i = 0; i < src->V; i++) {
const c_AdjList_t* src_list = &src->adj_list[i];
c_AdjList_t* dst_list = &self->adj_list[i];
// Initialize the tracking container with matching capacity constraints
if (c_AdjList_Init(dst_list, src_list->size, &self->allocator) != C_SUCCESS) {
// CRITICAL BUG FIX: Rollback strategy to eliminate memory leakage
for (c_size_t j = 0; j < i; j++) {
c_AdjList_Destroy(&self->adj_list[j]);
}
c_Allocator_Free(&self->allocator, self->adj_list);
self->adj_list = NULL;
self->V = 0;
self->E = 0;
return C_ERR_NOMEM;
}
// CRITICAL BUG FIX: Sync size invariant because Init sets active size to 0
dst_list->size = src_list->size;
// High-performance block copy via consecutive primitive mapping
if (dst_list->size > 0) {
memcpy(dst_list->array, src_list->array, dst_list->size * sizeof(c_uint_t));
}
}
return C_SUCCESS;
}