mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 08:46:42 +08:00
1328 lines
38 KiB
C
1328 lines
38 KiB
C
/*********************************************************************************************************
|
|
* ------------------------------------------------------------------------------------------------------
|
|
* file description
|
|
* ------------------------------------------------------------------------------------------------------
|
|
* \file graph.c
|
|
* \unit graph
|
|
* \brief This is a C language graph
|
|
* \author Lamdonn
|
|
* \version v1.0.0
|
|
* \license GPL-2.0
|
|
* \copyright Copyright (C) 2023 Lamdonn.
|
|
********************************************************************************************************/
|
|
#include "graph.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
/* vertex data flags */
|
|
#define FLAG_VISITED 0x01
|
|
|
|
/* The macro definition method for traversal */
|
|
#define for_each(graph, i) for (int i = 0, f = 0; i < (graph)->max && f < (graph)->cvertex; i++) if ((graph)->vertices[i] && ++f > 0)
|
|
|
|
typedef struct EDGE {
|
|
struct EDGE* next; /**< The pointer to the next neighbor */
|
|
int index; /**< The indices of the adjacent vertices */
|
|
int weight; /**< Edge weight */
|
|
} EDGE, *edge_t;
|
|
|
|
typedef struct {
|
|
edge_t firstedge; /**< The list that stores vertex edges */
|
|
int flag; /**< Vertex data flags */
|
|
void *data; /**< Address of data */
|
|
int size; /**< Size of data */
|
|
} VERTEX, *vertex_t;
|
|
|
|
typedef struct GRAPH {
|
|
vertex_t *vertices;
|
|
int max; /**< Maximum number of vertices can be stored */
|
|
int cvertex; /**< The count of vertices in the graph */
|
|
int cedge; /**< The count of edges in the graph */
|
|
int directed; /**< Mark whether it is a directed graph */
|
|
} GRAPH;
|
|
|
|
static int alloc_vertex_index(graph_t graph)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < graph->max; i++)
|
|
{
|
|
if (!graph->vertices[i])
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int vertex_set(vertex_t vectex, void* data, int size)
|
|
{
|
|
void* d = NULL;
|
|
|
|
/* Input value validity check */
|
|
if (!vectex) return 0;
|
|
if (size < 0) return 0;
|
|
|
|
/* If the incoming data size is 0, set the air sensitive data directly */
|
|
if (size == 0)
|
|
{
|
|
if (vectex->data) free(vectex->data);
|
|
vectex->data = NULL;
|
|
vectex->size = 0;
|
|
return 1;
|
|
}
|
|
|
|
/* If the data size is inconsistent, update the data storage space */
|
|
if (size != vectex->size)
|
|
{
|
|
d = realloc(vectex->data, size);
|
|
if (!d) return 0;
|
|
vectex->data = d;
|
|
}
|
|
|
|
/* Data assignment */
|
|
if (data) memcpy(vectex->data, data, size);
|
|
|
|
/* Update data size */
|
|
vectex->size = size;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates and initializes a vertex
|
|
* \param[in] data: pointer to the data to be associated with the vertex
|
|
* \param[in] size: size of the data to be copied
|
|
* \return A pointer to the newly created vertex, or NULL if the creation fails
|
|
*/
|
|
static vertex_t vertex_create(void *data, int size)
|
|
{
|
|
vertex_t vertex;
|
|
|
|
// Allocate memory for the vertex structure
|
|
vertex = (vertex_t)malloc(sizeof(VERTEX));
|
|
if (!vertex)
|
|
{
|
|
return NULL; // Return NULL if memory allocation fails
|
|
}
|
|
|
|
// Initialize the vertex memory to zero
|
|
memset(vertex, 0, sizeof(VERTEX));
|
|
|
|
// Set the data for the vertex, return NULL if it fails
|
|
if (!vertex_set(vertex, data, size))
|
|
{
|
|
free(vertex); // Free the allocated memory if setting data fails
|
|
return NULL;
|
|
}
|
|
|
|
return vertex; // Return the created vertex
|
|
}
|
|
|
|
/**
|
|
* \brief Destroys a vertex and frees associated resources
|
|
* \param[in] vertex: pointer to the vertex to be destroyed
|
|
* \return none
|
|
*/
|
|
static void vertex_destroy(vertex_t vertex)
|
|
{
|
|
// Check if the vertex has associated data and free it
|
|
if (vertex->data)
|
|
{
|
|
free(vertex->data); // Free the allocated memory for the data
|
|
vertex->data = NULL; // Set the pointer to NULL to avoid dangling reference
|
|
}
|
|
|
|
// Free the vertex structure itself
|
|
free(vertex);
|
|
}
|
|
|
|
/**
|
|
* \brief Creates and initializes an edge
|
|
* \param[in] index: index of the edge
|
|
* \param[in] weight: weight of the edge
|
|
* \return A pointer to the newly created edge, or NULL if the creation fails
|
|
*/
|
|
static edge_t edge_create(int index, int weight)
|
|
{
|
|
edge_t edge;
|
|
|
|
// Allocate memory for the edge structure
|
|
edge = (edge_t)malloc(sizeof(EDGE));
|
|
if (!edge)
|
|
{
|
|
return NULL; // Return NULL if memory allocation fails
|
|
}
|
|
|
|
// Initialize the edge properties
|
|
edge->index = index; // Set the index of the edge
|
|
edge->weight = weight; // Set the weight of the edge
|
|
edge->next = NULL; // Initialize the next pointer to NULL
|
|
|
|
return edge; // Return the created edge
|
|
}
|
|
|
|
/**
|
|
* \brief Destroys an edge and frees associated resources
|
|
* \param[in] edge: pointer to the edge to be destroyed
|
|
* \return none
|
|
*/
|
|
static void edge_destroy(edge_t edge)
|
|
{
|
|
// Free the memory allocated for the edge structure
|
|
free(edge);
|
|
}
|
|
|
|
graph_t graph_create(int max, int directed)
|
|
{
|
|
graph_t graph;
|
|
|
|
// Check if the maximum number of vertices is valid
|
|
if (max <= 0)
|
|
{
|
|
return NULL; // Return NULL if max is non-positive
|
|
}
|
|
|
|
// Allocate memory for the graph structure
|
|
graph = (graph_t)malloc(sizeof(GRAPH));
|
|
if (!graph)
|
|
{
|
|
return NULL; // Return NULL if memory allocation fails
|
|
}
|
|
|
|
// Allocate memory for the vertex array
|
|
graph->vertices = (vertex_t *)malloc(sizeof(vertex_t) * max);
|
|
if (!graph->vertices)
|
|
{
|
|
free(graph); // Free the graph memory if vertex allocation fails
|
|
return NULL;
|
|
}
|
|
|
|
// Initialize graph parameters
|
|
graph->max = max; // Set the maximum number of vertices
|
|
graph->cvertex = 0; // Initialize current vertex count
|
|
graph->cedge = 0; // Initialize current edge count
|
|
graph->directed = directed; // Set the directed flag
|
|
memset(graph->vertices, 0, sizeof(vertex_t) * max); // Initialize vertex pointers to NULL
|
|
|
|
return graph; // Return the created graph
|
|
}
|
|
|
|
void graph_delete(graph_t graph)
|
|
{
|
|
edge_t edge, temp;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return;
|
|
|
|
// Iterate through each vertex in the graph
|
|
for_each(graph, i)
|
|
{
|
|
edge = graph->vertices[i]->firstedge; // Get the first edge of the vertex
|
|
|
|
// Free all edges associated with the vertex
|
|
while (edge)
|
|
{
|
|
temp = edge; // Store the current edge
|
|
edge = edge->next; // Move to the next edge
|
|
edge_destroy(temp); // Free the current edge
|
|
}
|
|
|
|
// Destroy the vertex and set its pointer to NULL
|
|
vertex_destroy(graph->vertices[i]);
|
|
graph->vertices[i] = NULL;
|
|
}
|
|
|
|
// Free the vertex array if it was allocated
|
|
if (graph->vertices) free(graph->vertices);
|
|
|
|
// Free the graph structure itself
|
|
free(graph);
|
|
}
|
|
|
|
int graph_add_vertex(graph_t graph, void *data, int size)
|
|
{
|
|
vertex_t vertex;
|
|
int index = -1;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return -1;
|
|
|
|
// Allocate an index for the new vertex
|
|
index = alloc_vertex_index(graph);
|
|
if (index < 0) return -1; // Return -1 if no available index
|
|
|
|
// Create and initialize the new vertex
|
|
vertex = vertex_create(data, size);
|
|
if (!vertex)
|
|
{
|
|
return -1; // Return -1 if vertex creation fails
|
|
}
|
|
|
|
// Add the vertex to the graph and update vertex count
|
|
graph->vertices[index] = vertex;
|
|
graph->cvertex++;
|
|
|
|
return index; // Return the index of the newly added vertex
|
|
}
|
|
|
|
/**
|
|
* \brief Adds an edge to the graph
|
|
* \param[in] graph: pointer to the graph where the edge will be added
|
|
* \param[in] start: index of the starting vertex
|
|
* \param[in] end: index of the ending vertex
|
|
* \param[in] weight: weight of the edge
|
|
* \return 1 if the edge was added successfully, 0 if the addition fails
|
|
*/
|
|
int graph_add_edge(graph_t graph, int start, int end, int weight)
|
|
{
|
|
edge_t edge;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
// Validate indices for the vertices
|
|
if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return 0;
|
|
// Ensure both vertices exist
|
|
if (!graph->vertices[start] || !graph->vertices[end]) return 0;
|
|
|
|
// Create a new edge from start to end
|
|
edge = edge_create(end, weight);
|
|
if (!edge)
|
|
{
|
|
return 0; // Return 0 if edge creation fails
|
|
}
|
|
|
|
/* Head insert: insert the edge at the beginning of the adjacency list */
|
|
edge->next = graph->vertices[start]->firstedge;
|
|
graph->vertices[start]->firstedge = edge;
|
|
graph->cedge++;
|
|
|
|
// If the graph is undirected, add a reverse edge
|
|
if (!graph->directed)
|
|
{
|
|
edge = edge_create(start, weight);
|
|
if (!edge)
|
|
{
|
|
// If reverse edge creation fails, remove the original edge
|
|
edge = graph->vertices[start]->firstedge;
|
|
graph->vertices[start]->firstedge = edge->next; // Remove the edge
|
|
edge_destroy(edge); // Free the edge
|
|
graph->cedge--; // Decrement edge count
|
|
return 0; // Return 0 on failure
|
|
}
|
|
|
|
// Insert the reverse edge at the beginning of the end vertex's list
|
|
edge->next = graph->vertices[end]->firstedge;
|
|
graph->vertices[end]->firstedge = edge;
|
|
graph->cedge++;
|
|
}
|
|
|
|
return 1; // Return 1 to indicate successful addition of the edge
|
|
}
|
|
|
|
int graph_remove_vertex(graph_t graph, int index)
|
|
{
|
|
edge_t prev, curr, temp;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
// Validate the vertex index
|
|
if (index < 0 || index >= graph->max) return 0;
|
|
// Ensure the vertex exists
|
|
if (!graph->vertices[index]) return 0;
|
|
|
|
// Delete edges that are incident to the vertex from neighboring vertices
|
|
for_each(graph, i)
|
|
{
|
|
prev = NULL; // Previous edge pointer
|
|
curr = graph->vertices[i]->firstedge;
|
|
|
|
// Traverse the adjacency list of the neighboring vertex
|
|
while (curr)
|
|
{
|
|
if (curr->index == index)
|
|
{
|
|
// If the edge to be removed is the first edge
|
|
if (!prev)
|
|
{
|
|
graph->vertices[i]->firstedge = curr->next; // Remove from head
|
|
}
|
|
else
|
|
{
|
|
prev->next = curr->next; // Bypass the current edge
|
|
}
|
|
|
|
temp = curr; // Save the edge to be deleted
|
|
curr = curr->next; // Move to the next edge
|
|
|
|
edge_destroy(temp); // Free the edge
|
|
graph->cedge--; // Decrement edge count
|
|
}
|
|
else
|
|
{
|
|
prev = curr; // Move previous pointer forward
|
|
curr = curr->next; // Move to next edge
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete all edges associated with the vertex being removed
|
|
curr = graph->vertices[index]->firstedge;
|
|
while (curr)
|
|
{
|
|
temp = curr; // Save current edge
|
|
curr = curr->next; // Move to next edge
|
|
edge_destroy(temp); // Free the edge
|
|
graph->cedge--; // Decrement edge count
|
|
}
|
|
|
|
// Delete the vertex itself
|
|
vertex_destroy(graph->vertices[index]);
|
|
graph->vertices[index] = NULL; // Nullify the vertex pointer
|
|
|
|
// Update the count of vertices
|
|
graph->cvertex--;
|
|
|
|
return 1; // Return 1 to indicate successful removal
|
|
}
|
|
|
|
int graph_remove_edge(graph_t graph, int start, int end)
|
|
{
|
|
edge_t prev = NULL, curr;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
// Validate vertex indices
|
|
if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return 0;
|
|
// Ensure both vertices exist
|
|
if (!graph->vertices[start] || !graph->vertices[end]) return 0;
|
|
|
|
// Traverse the adjacency list of the starting vertex
|
|
curr = graph->vertices[start]->firstedge;
|
|
|
|
while (curr)
|
|
{
|
|
// Check if the current edge points to the ending vertex
|
|
if (curr->index == end)
|
|
{
|
|
// Remove the edge from the adjacency list
|
|
if (!prev)
|
|
{
|
|
graph->vertices[start]->firstedge = curr->next; // Remove from head
|
|
}
|
|
else
|
|
{
|
|
prev->next = curr->next; // Bypass the current edge
|
|
}
|
|
|
|
edge_destroy(curr); // Free the edge
|
|
graph->cedge--; // Decrement edge count
|
|
|
|
break; // Exit loop after removing the edge
|
|
}
|
|
else
|
|
{
|
|
prev = curr; // Move previous pointer forward
|
|
curr = curr->next; // Move to next edge
|
|
}
|
|
}
|
|
|
|
// If the graph is undirected, also remove the reverse edge
|
|
if (!graph->directed)
|
|
{
|
|
prev = NULL;
|
|
curr = graph->vertices[end]->firstedge;
|
|
|
|
while (curr)
|
|
{
|
|
// Check if the current edge points to the starting vertex
|
|
if (curr->index == start)
|
|
{
|
|
// Remove the edge from the adjacency list
|
|
if (!prev)
|
|
{
|
|
graph->vertices[end]->firstedge = curr->next; // Remove from head
|
|
}
|
|
else
|
|
{
|
|
prev->next = curr->next; // Bypass the current edge
|
|
}
|
|
|
|
edge_destroy(curr); // Free the edge
|
|
graph->cedge--; // Decrement edge count
|
|
|
|
break; // Exit loop after removing the edge
|
|
}
|
|
else
|
|
{
|
|
prev = curr; // Move previous pointer forward
|
|
curr = curr->next; // Move to next edge
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1; // Return 1 to indicate successful removal
|
|
}
|
|
|
|
void graph_ls(graph_t graph, graph_traverse_t func)
|
|
{
|
|
vertex_t vertex;
|
|
|
|
// Validate input
|
|
if (!graph) return; // Check if the graph is valid
|
|
if (!func) return; // Check if the function is valid
|
|
|
|
// Iterate over all vertices in the graph
|
|
for_each(graph, i)
|
|
{
|
|
vertex = graph->vertices[i]; // Get the current vertex
|
|
// Apply the provided function to the vertex
|
|
func(i, vertex->data, vertex->size);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Depth-First Search (DFS) traversal of a graph
|
|
* \param[in] graph: pointer to the graph to be traversed
|
|
* \param[in] index: index of the current vertex being visited
|
|
* \param[in] func: callback function to be executed for each visited vertex
|
|
* \return none
|
|
*/
|
|
static void dfs(graph_t graph, int index, graph_traverse_t func)
|
|
{
|
|
vertex_t vertex;
|
|
edge_t curr;
|
|
|
|
// Check if the vertex at the given index exists
|
|
if (graph->vertices[index])
|
|
{
|
|
vertex = graph->vertices[index];
|
|
// Execute the callback function with the vertex data
|
|
if (func) func(index, vertex->data, vertex->size);
|
|
// Mark the vertex as visited
|
|
vertex->flag |= FLAG_VISITED;
|
|
curr = vertex->firstedge;
|
|
|
|
// Traverse all adjacent edges
|
|
while (curr)
|
|
{
|
|
// If the adjacent vertex has not been visited, continue DFS on it
|
|
if (!(graph->vertices[curr->index]->flag & FLAG_VISITED))
|
|
{
|
|
dfs(graph, curr->index, func);
|
|
}
|
|
|
|
curr = curr->next; // Move to the next edge
|
|
}
|
|
}
|
|
}
|
|
|
|
void graph_dfs(graph_t graph, int start, graph_traverse_t func)
|
|
{
|
|
// Check if the graph exists
|
|
if (!graph) return;
|
|
// Validate the starting vertex index
|
|
if (start < 0 || start >= graph->max) return;
|
|
// Ensure the starting vertex exists
|
|
if (!graph->vertices[start]) return;
|
|
|
|
// Reset the visited flag for all vertices
|
|
for_each(graph, i)
|
|
{
|
|
graph->vertices[i]->flag &= ~FLAG_VISITED;
|
|
}
|
|
|
|
// Start the DFS from the specified starting vertex
|
|
dfs(graph, start, func);
|
|
}
|
|
|
|
/**
|
|
* \brief Breadth-First Search (BFS) traversal of a graph
|
|
* \param[in] graph: pointer to the graph to be traversed
|
|
* \param[in] index: index of the current vertex being visited
|
|
* \param[in] func: callback function to be executed for each visited vertex
|
|
* \param[in] queue: array used for queueing vertices during traversal
|
|
* \return none
|
|
*/
|
|
static void bfs(graph_t graph, int index, graph_traverse_t func, int *queue)
|
|
{
|
|
int front = 0, rear = 0, nindex;
|
|
vertex_t vertex, nvertex;
|
|
edge_t curr;
|
|
|
|
// Get the starting vertex
|
|
vertex = graph->vertices[index];
|
|
|
|
// Execute the callback function for the starting vertex
|
|
func(index, vertex->data, vertex->size);
|
|
vertex->flag |= FLAG_VISITED; // Mark the starting vertex as visited
|
|
|
|
// Enqueue the starting vertex
|
|
queue[rear++] = index;
|
|
|
|
// Process the queue until it's empty
|
|
while (front != rear)
|
|
{
|
|
index = queue[front++]; // Dequeue the front vertex
|
|
curr = graph->vertices[index]->firstedge; // Get its adjacency list
|
|
|
|
// Traverse all adjacent edges
|
|
while (curr)
|
|
{
|
|
nindex = curr->index; // Index of the adjacent vertex
|
|
|
|
// If the adjacent vertex has not been visited
|
|
if (!(graph->vertices[nindex]->flag & FLAG_VISITED))
|
|
{
|
|
nvertex = graph->vertices[nindex]; // Get the adjacent vertex
|
|
|
|
// Execute the callback function for the adjacent vertex
|
|
func(nindex, nvertex->data, nvertex->size);
|
|
nvertex->flag |= FLAG_VISITED; // Mark it as visited
|
|
|
|
// Enqueue the adjacent vertex
|
|
queue[rear++] = nindex;
|
|
}
|
|
|
|
curr = curr->next; // Move to the next edge
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Initializes BFS traversal with a queue of a given maximum size
|
|
* \param[in] graph: pointer to the graph to be traversed
|
|
* \param[in] start: index of the starting vertex
|
|
* \param[in] func: callback function to be executed for each visited vertex
|
|
* \param[in] max: maximum size of the queue
|
|
* \return none
|
|
*/
|
|
static void t_bfs(graph_t graph, int start, graph_traverse_t func, const int max)
|
|
{
|
|
int queue[max]; // Create a queue array
|
|
|
|
memset(queue, 0, sizeof(int) * max); // Initialize the queue
|
|
|
|
bfs(graph, start, func, queue); // Start BFS traversal
|
|
}
|
|
|
|
void graph_bfs(graph_t graph, int start, graph_traverse_t func)
|
|
{
|
|
// Check if the graph exists
|
|
if (!graph) return;
|
|
// Validate the starting vertex index
|
|
if (start < 0 || start >= graph->max) return;
|
|
// Ensure the starting vertex exists
|
|
if (!graph->vertices[start]) return;
|
|
// Ensure the callback function is not NULL
|
|
if (!func) return;
|
|
|
|
// Reset the visited flag for all vertices
|
|
for_each(graph, i)
|
|
{
|
|
graph->vertices[i]->flag &= ~FLAG_VISITED;
|
|
}
|
|
|
|
// Start BFS from the specified starting vertex
|
|
t_bfs(graph, start, func, graph->max);
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Retrieves the number of vertices in the graph
|
|
* \param[in] graph: pointer to the graph
|
|
* \return The number of vertices in the graph, or 0 if the graph is NULL
|
|
*/
|
|
int graph_vertex_count(graph_t graph)
|
|
{
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
|
|
return graph->cvertex; // Return the count of vertices
|
|
}
|
|
|
|
/**
|
|
* \brief Retrieves the number of edges in the graph
|
|
* \param[in] graph: pointer to the graph
|
|
* \return The number of edges in the graph, or 0 if the graph is NULL
|
|
*/
|
|
int graph_edge_count(graph_t graph)
|
|
{
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
|
|
return graph->cedge; // Return the count of edges
|
|
}
|
|
|
|
int graph_vertex_set_data(graph_t graph, int index, void *data, int size)
|
|
{
|
|
vertex_t vertex;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
// Validate the vertex index
|
|
if (index < 0 || index >= graph->max) return 0;
|
|
// Ensure the vertex exists
|
|
if (!graph->vertices[index]) return 0;
|
|
// Validate the data size
|
|
if (size < 0) return 0;
|
|
|
|
vertex = graph->vertices[index];
|
|
|
|
// Set the vertex data; return 0 if it fails
|
|
if (!vertex_set(vertex, data, size)) return 0;
|
|
|
|
return 1; // Return 1 to indicate success
|
|
}
|
|
|
|
int graph_vertex_get_data(graph_t graph, int index, void *data, int size)
|
|
{
|
|
vertex_t vertex;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
// Validate the vertex index
|
|
if (index < 0 || index >= graph->max) return 0;
|
|
// Ensure the vertex exists
|
|
if (!graph->vertices[index]) return 0;
|
|
// Ensure the data pointer is not NULL
|
|
if (!data) return 0;
|
|
|
|
vertex = graph->vertices[index];
|
|
|
|
// Validate the size of the buffer
|
|
if (size < vertex->size) return 0;
|
|
|
|
// Data assignment using memcpy
|
|
memcpy(data, vertex->data, vertex->size);
|
|
|
|
return 1; // Return 1 to indicate success
|
|
}
|
|
|
|
void *graph_vertex_data(graph_t graph, int index, int *size)
|
|
{
|
|
vertex_t vertex;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return NULL;
|
|
// Validate the vertex index
|
|
if (index < 0 || index >= graph->max) return NULL;
|
|
// Ensure the vertex exists
|
|
if (!graph->vertices[index]) return NULL;
|
|
|
|
vertex = graph->vertices[index];
|
|
|
|
// If size pointer is provided, store the size of the vertex data
|
|
if (size) *size = vertex->size;
|
|
|
|
return vertex->data; // Return the vertex data
|
|
}
|
|
|
|
int graph_out_degree(graph_t graph, int index)
|
|
{
|
|
edge_t edge;
|
|
vertex_t vertex;
|
|
int degree = 0;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return -1;
|
|
// Validate the vertex index
|
|
if (index < 0 || index >= graph->max) return -1;
|
|
// Ensure the vertex exists
|
|
if (!graph->vertices[index]) return -1;
|
|
|
|
vertex = graph->vertices[index];
|
|
edge = vertex->firstedge;
|
|
|
|
// Count the edges originating from the vertex
|
|
while (edge)
|
|
{
|
|
degree++;
|
|
edge = edge->next; // Move to the next edge
|
|
}
|
|
|
|
return degree; // Return the out-degree
|
|
}
|
|
|
|
int graph_in_degree(graph_t graph, int index)
|
|
{
|
|
edge_t edge;
|
|
vertex_t vertex;
|
|
int degree = 0;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return -1;
|
|
// Validate the vertex index
|
|
if (index < 0 || index >= graph->max) return -1;
|
|
// Ensure the vertex exists
|
|
if (!graph->vertices[index]) return -1;
|
|
|
|
// Iterate over all vertices to count edges pointing to the specified vertex
|
|
for_each(graph, i)
|
|
{
|
|
vertex = graph->vertices[i];
|
|
edge = vertex->firstedge;
|
|
|
|
while (edge)
|
|
{
|
|
// Check if the edge points to the specified vertex
|
|
if (edge->index == index)
|
|
{
|
|
degree++; // Increment in-degree
|
|
}
|
|
|
|
edge = edge->next; // Move to the next edge
|
|
}
|
|
}
|
|
|
|
return degree; // Return the in-degree
|
|
}
|
|
|
|
int graph_is_adjacent(graph_t graph, int start, int end)
|
|
{
|
|
edge_t edge;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
// Validate the vertex indices
|
|
if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return 0;
|
|
// Ensure both vertices exist
|
|
if (!graph->vertices[start] || !graph->vertices[end]) return 0;
|
|
|
|
// Iterate through the edges of the starting vertex
|
|
edge = graph->vertices[start]->firstedge;
|
|
while (edge)
|
|
{
|
|
// Check if the current edge points to the ending vertex
|
|
if (edge->index == end)
|
|
{
|
|
return 1; // Vertices are adjacent
|
|
}
|
|
edge = edge->next; // Move to the next edge
|
|
}
|
|
|
|
return 0; // Vertices are not adjacent
|
|
}
|
|
|
|
int graph_get_edge_weight(graph_t graph, int start, int end)
|
|
{
|
|
edge_t edge;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return INT_MAX;
|
|
// Validate the vertex indices
|
|
if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return INT_MAX;
|
|
// Ensure both vertices exist
|
|
if (!graph->vertices[start] || !graph->vertices[end]) return INT_MAX;
|
|
|
|
// Iterate through the edges of the starting vertex
|
|
edge = graph->vertices[start]->firstedge;
|
|
while (edge)
|
|
{
|
|
// Check if the current edge points to the ending vertex
|
|
if (edge->index == end)
|
|
{
|
|
return edge->weight; // Return the edge weight
|
|
}
|
|
|
|
edge = edge->next; // Move to the next edge
|
|
}
|
|
|
|
return INT_MAX; // Return INT_MAX if no edge exists between the vertices
|
|
}
|
|
|
|
int graph_set_edge_weight(graph_t graph, int start, int end, int weight)
|
|
{
|
|
edge_t edge;
|
|
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
// Validate the vertex indices
|
|
if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return 0;
|
|
// Ensure both vertices exist
|
|
if (!graph->vertices[start] || !graph->vertices[end]) return 0;
|
|
|
|
// Iterate through the edges of the starting vertex
|
|
edge = graph->vertices[start]->firstedge;
|
|
while (edge)
|
|
{
|
|
// Check if the current edge points to the ending vertex
|
|
if (edge->index == end)
|
|
{
|
|
edge->weight = weight; // Set the new weight
|
|
return 1; // Return 1 to indicate success
|
|
}
|
|
|
|
edge = edge->next; // Move to the next edge
|
|
}
|
|
|
|
return 0; // Return 0 if no edge exists between the vertices
|
|
}
|
|
|
|
int graph_contains_vertex(graph_t graph, int index)
|
|
{
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
// Validate the vertex index
|
|
if (index < 0 || index >= graph->max) return 0;
|
|
// Ensure the vertex exists
|
|
if (!graph->vertices[index]) return 0;
|
|
|
|
return 1; // Return 1 if the vertex exists
|
|
}
|
|
|
|
/**
|
|
* \brief Performs a topological sort on a directed acyclic graph (DAG)
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[in] indegree: array to store the in-degrees of vertices
|
|
* \param[in] queue: array to use as a queue for vertices with zero in-degree
|
|
*/
|
|
static void topological_sort(graph_t graph, int *indegree, int *queue)
|
|
{
|
|
int i, f = 0;
|
|
int front = 0, rear = 0;
|
|
int index;
|
|
edge_t curr;
|
|
|
|
// Calculate the in-degree for each vertex
|
|
for_each(graph, i)
|
|
{
|
|
curr = graph->vertices[i]->firstedge;
|
|
|
|
while (curr)
|
|
{
|
|
indegree[curr->index]++;
|
|
curr = curr->next;
|
|
}
|
|
}
|
|
|
|
// Add vertices with zero in-degree to the queue
|
|
for_each(graph, i)
|
|
{
|
|
if (indegree[i] == 0)
|
|
{
|
|
queue[rear++] = i;
|
|
}
|
|
}
|
|
|
|
// Process the queue
|
|
while (front < rear)
|
|
{
|
|
index = queue[front++];
|
|
printf("%d ", index); // Print the vertex in sorted order
|
|
curr = graph->vertices[index]->firstedge;
|
|
|
|
while (curr)
|
|
{
|
|
// Decrease the in-degree of the neighboring vertex
|
|
if (--indegree[curr->index] == 0)
|
|
{
|
|
queue[rear++] = curr->index; // Add to queue if in-degree becomes zero
|
|
}
|
|
curr = curr->next;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/**
|
|
* \brief Initializes the queue and in-degree array, then calls the topological sort function
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[in] max: maximum number of vertices
|
|
*/
|
|
static void t_topological_sort(graph_t graph, const int max)
|
|
{
|
|
int queue[max]; // Queue for vertices with zero in-degree
|
|
int indegree[max]; // Array to hold in-degrees of vertices
|
|
|
|
memset(queue, 0, sizeof(int) * max); // Initialize queue
|
|
memset(indegree, 0, sizeof(int) * max); // Initialize in-degree counts
|
|
|
|
topological_sort(graph, indegree, queue); // Perform the topological sort
|
|
}
|
|
|
|
void graph_topological_sort(graph_t graph)
|
|
{
|
|
if (!graph) return; // Check if the graph is NULL
|
|
|
|
t_topological_sort(graph, graph->max); // Call the helper function
|
|
}
|
|
|
|
/**
|
|
* \brief Computes the shortest path from a starting vertex to all other vertices in a weighted graph
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[in] start: index of the starting vertex
|
|
* \param[out] dist: array to store the shortest distances from the start vertex
|
|
* \param[out] prev: array to store the previous vertex in the shortest path
|
|
* \param[out] path: array to store the vertices in the shortest path for printing
|
|
*/
|
|
static void shortest_path(graph_t graph, int start, int *dist, int *prev, int *path)
|
|
{
|
|
int j;
|
|
edge_t curr;
|
|
int min_dist, min_index;
|
|
int alt;
|
|
int path_count;
|
|
int prev_index;
|
|
|
|
// Initialize distances and previous vertices
|
|
for_each(graph, i)
|
|
{
|
|
dist[i] = INT_MAX; // Set all distances to infinity
|
|
prev[i] = -1; // Set previous vertices to -1
|
|
}
|
|
|
|
dist[start] = 0; // Distance from start to itself is zero
|
|
|
|
while (1)
|
|
{
|
|
min_dist = INT_MAX;
|
|
min_index = -1;
|
|
|
|
// Find the unvisited vertex with the smallest distance
|
|
for_each(graph, i)
|
|
{
|
|
if (!(graph->vertices[i]->flag & FLAG_VISITED) && dist[i] < min_dist)
|
|
{
|
|
min_dist = dist[i];
|
|
min_index = i;
|
|
}
|
|
}
|
|
|
|
// If no unvisited vertex is found, exit the loop
|
|
if (min_index == -1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Mark the vertex as visited
|
|
graph->vertices[min_index]->flag |= FLAG_VISITED;
|
|
curr = graph->vertices[min_index]->firstedge;
|
|
|
|
// Relaxation step
|
|
while (curr)
|
|
{
|
|
alt = dist[min_index] + graph_get_edge_weight(graph, min_index, curr->index);
|
|
|
|
// Update distance and previous vertex if a shorter path is found
|
|
if (alt < dist[curr->index])
|
|
{
|
|
dist[curr->index] = alt;
|
|
prev[curr->index] = min_index;
|
|
}
|
|
|
|
curr = curr->next;
|
|
}
|
|
}
|
|
|
|
// Print the shortest paths from the start vertex to all other vertices
|
|
for_each(graph, i)
|
|
{
|
|
printf("Shortest path from %d to %d: ", start, i);
|
|
if (dist[i] == INT_MAX)
|
|
{
|
|
printf("not reachable\n");
|
|
}
|
|
else
|
|
{
|
|
printf("%d (", dist[i]);
|
|
|
|
// Build the path from end to start
|
|
path_count = 0;
|
|
prev_index = i;
|
|
|
|
while (prev_index != -1)
|
|
{
|
|
path[path_count++] = prev_index;
|
|
prev_index = prev[prev_index];
|
|
}
|
|
|
|
// Print the path in the correct order
|
|
for (j = path_count - 1; j >= 0; j--)
|
|
{
|
|
printf("%d", path[j]);
|
|
|
|
if (j != 0)
|
|
{
|
|
printf(" -> ");
|
|
}
|
|
}
|
|
|
|
printf(")\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Initializes arrays for distances, previous vertices, and paths, then calls the shortest_path function
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[in] start: index of the starting vertex
|
|
* \param[in] max: maximum number of vertices
|
|
*/
|
|
static void t_shortest_path(graph_t graph, int start, int max)
|
|
{
|
|
int dist[max], prev[max], path[max];
|
|
|
|
memset(dist, 0, sizeof(int) * max); // Initialize distance array
|
|
memset(prev, 0, sizeof(int) * max); // Initialize previous array
|
|
memset(path, 0, sizeof(int) * max); // Initialize path array
|
|
|
|
shortest_path(graph, start, dist, prev, path); // Call the shortest path function
|
|
}
|
|
|
|
void graph_shortest_path(graph_t graph, int start)
|
|
{
|
|
// Validate input
|
|
if (!graph) return;
|
|
if (start < 0 || start >= graph->max) return;
|
|
if (!graph->vertices[start]) return;
|
|
|
|
t_shortest_path(graph, start, graph->max); // Call the helper function
|
|
}
|
|
|
|
int graph_is_connected(graph_t graph)
|
|
{
|
|
// Check if the graph is NULL
|
|
if (!graph) return 0;
|
|
|
|
// Perform a depth-first search starting from the first vertex
|
|
graph_dfs(graph, 0, NULL);
|
|
|
|
// Check if all vertices were visited
|
|
for_each(graph, i)
|
|
{
|
|
if (!(graph->vertices[i]->flag & FLAG_VISITED))
|
|
{
|
|
return 0; // If any vertex is not visited, the graph is not connected
|
|
}
|
|
}
|
|
|
|
return 1; // All vertices were visited, the graph is connected
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if the graph is a complete graph
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[out] degree: array to hold the degree of each vertex
|
|
* \return 1 if the graph is complete, 0 if it is not
|
|
*/
|
|
static int is_complete(graph_t graph, int *degree)
|
|
{
|
|
// For undirected graphs, each vertex's degree should be n-1 (where n is the number of vertices)
|
|
// For directed graphs, the sum of in-degree and out-degree of each vertex should be n-1
|
|
for_each(graph, i)
|
|
{
|
|
if (graph->directed)
|
|
{
|
|
// Calculate degree for directed graph
|
|
degree[i] = graph_in_degree(graph, i) + graph_out_degree(graph, i);
|
|
}
|
|
else
|
|
{
|
|
// Calculate degree for undirected graph
|
|
degree[i] = graph_out_degree(graph, i);
|
|
}
|
|
}
|
|
|
|
// Check if all vertices have the required degree for a complete graph
|
|
for_each(graph, i)
|
|
{
|
|
if (degree[i] != graph->cvertex - 1)
|
|
{
|
|
return 0; // Not a complete graph
|
|
}
|
|
}
|
|
|
|
return 1; // It is a complete graph
|
|
}
|
|
|
|
/**
|
|
* \brief Helper function to check if the graph is complete, initializes the degree array
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[in] max: maximum number of vertices
|
|
* \return 1 if the graph is complete, 0 if it is not
|
|
*/
|
|
static int t_is_complete(graph_t graph, const int max)
|
|
{
|
|
int degree[max]; // Array to hold degrees of vertices
|
|
|
|
memset(degree, 0, sizeof(int) * max); // Initialize degree array
|
|
|
|
return is_complete(graph, degree); // Call the complete check function
|
|
}
|
|
|
|
int graph_is_complete(graph_t graph)
|
|
{
|
|
// Validate input
|
|
if (!graph) return 0;
|
|
|
|
return t_is_complete(graph, graph->max); // Call the helper function
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if the graph is bipartite using BFS
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[out] color: array to hold colors of vertices (1 or 0)
|
|
* \param[out] queue: array to use as a queue for BFS
|
|
* \return 1 if the graph is bipartite, 0 if it is not
|
|
*/
|
|
static int is_bipartite(graph_t graph, int *color, int *queue)
|
|
{
|
|
vertex_t vertex;
|
|
edge_t curr;
|
|
int index, next_index;
|
|
int front = 0, rear = 0;
|
|
|
|
// Start coloring the first vertex
|
|
color[0] = 1; // Color the first vertex with color 1
|
|
queue[rear++] = 0; // Enqueue the first vertex
|
|
|
|
// BFS loop
|
|
while (front < rear)
|
|
{
|
|
index = queue[front++]; // Dequeue a vertex
|
|
vertex = graph->vertices[index];
|
|
curr = vertex->firstedge;
|
|
|
|
// Explore all adjacent vertices
|
|
while (curr)
|
|
{
|
|
next_index = curr->index;
|
|
|
|
// If the adjacent vertex has not been colored
|
|
if (color[next_index] == -1)
|
|
{
|
|
color[next_index] = 1 - color[index]; // Assign opposite color
|
|
queue[rear++] = next_index; // Enqueue the adjacent vertex
|
|
}
|
|
// If the adjacent vertex has the same color, the graph is not bipartite
|
|
else if (color[next_index] == color[index])
|
|
{
|
|
return 0; // Not bipartite
|
|
}
|
|
|
|
curr = curr->next; // Move to the next edge
|
|
}
|
|
}
|
|
|
|
return 1; // Graph is bipartite
|
|
}
|
|
|
|
/**
|
|
* \brief Helper function to initialize color and queue arrays and check bipartiteness
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[in] max: maximum number of vertices
|
|
* \return 1 if the graph is bipartite, 0 if it is not
|
|
*/
|
|
static int t_is_bipartite(graph_t graph, const int max)
|
|
{
|
|
int color[max]; // Array to store colors of vertices
|
|
int queue[max]; // Queue for BFS
|
|
|
|
memset(color, -1, sizeof(int) * max); // Initialize colors to -1 (uncolored)
|
|
memset(queue, 0, sizeof(int) * max); // Initialize queue
|
|
|
|
return is_bipartite(graph, color, queue); // Call bipartite check function
|
|
}
|
|
|
|
int graph_is_bipartite(graph_t graph)
|
|
{
|
|
// Validate input
|
|
if (!graph) return 0;
|
|
|
|
return t_is_bipartite(graph, graph->max); // Call the helper function
|
|
}
|
|
|
|
int graph_is_eulerian(graph_t graph)
|
|
{
|
|
// Validate input
|
|
if (!graph) return 0;
|
|
|
|
// For undirected graphs, each vertex's degree must be even
|
|
// For directed graphs, in-degree must equal out-degree for each vertex
|
|
for_each(graph, i)
|
|
{
|
|
if ((graph->directed && graph_in_degree(graph, i) != graph_out_degree(graph, i)) ||
|
|
(!graph->directed && graph_out_degree(graph, i) % 2 != 0))
|
|
{
|
|
return 0; // Not Eulerian
|
|
}
|
|
}
|
|
|
|
return 1; // The graph is Eulerian
|
|
}
|
|
|
|
/**
|
|
* \brief Finds a minimum vertex cover for the graph
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[out] flag: array to track whether a vertex is included in the cover
|
|
*/
|
|
static void min_vertex_cover(graph_t graph, int *flag)
|
|
{
|
|
edge_t curr;
|
|
int v;
|
|
|
|
// Iterate over all vertices
|
|
for_each(graph, u)
|
|
{
|
|
// If the vertex is not already included in the cover
|
|
if (!flag[u])
|
|
{
|
|
// Include the vertex in the cover
|
|
flag[u] = 1;
|
|
curr = graph->vertices[u]->firstedge;
|
|
|
|
// Traverse the edges of the vertex
|
|
while (curr)
|
|
{
|
|
v = curr->index;
|
|
// If the adjacent vertex is not included in the cover
|
|
if (!flag[v])
|
|
{
|
|
// Include the adjacent vertex as well
|
|
flag[v] = 1;
|
|
// Print the edge in the vertex cover
|
|
printf("%d ", u);
|
|
printf("%d ", v);
|
|
break; // Stop after adding one edge
|
|
}
|
|
curr = curr->next; // Move to the next edge
|
|
}
|
|
}
|
|
}
|
|
printf("\n"); // Print a newline after all edges are processed
|
|
}
|
|
|
|
/**
|
|
* \brief Helper function to initialize the flag array and call the min_vertex_cover function
|
|
* \param[in] graph: pointer to the graph
|
|
* \param[in] max: maximum number of vertices
|
|
*/
|
|
static int t_min_vertex_cover(graph_t graph, const int max)
|
|
{
|
|
int flag[max]; // Array to indicate if a vertex is included in the cover
|
|
|
|
memset(flag, 0, sizeof(int) * max); // Initialize all flags to 0
|
|
|
|
min_vertex_cover(graph, flag); // Call the function to find the vertex cover
|
|
}
|
|
|
|
void graph_min_vertex_cover(graph_t graph)
|
|
{
|
|
// Check if the graph is valid
|
|
if (!graph) return;
|
|
|
|
t_min_vertex_cover(graph, graph->max); // Call the helper function
|
|
}
|
|
|
|
// TODO:
|
|
// void graph_minimum_spanning_tree(graph_t graph);
|
|
// int graph_max_flow(graph_t graph, int source, int sink);
|
|
// void graph_articulation_points(graph_t graph);
|
|
// void graph_bridges(graph_t graph);
|
|
// void graph_minimum_spanning_tree(graph_t graph);
|
|
// void graph_topological_sort(graph_t graph);
|