#include 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; vadj_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; vV; 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_VertexId_t v, c_VertexId_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_VertexId_t v, c_VertexId_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_VertexId_t v, c_VertexId_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_VertexId_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_VertexId_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; }