Refactored for a flat buffer friendly, and O(1) or O(n) time complexities.
This commit is contained in:
201
Graph.cpp
201
Graph.cpp
@@ -1,96 +1,149 @@
|
||||
#include "Graph.h"
|
||||
|
||||
// Begin : Construction & Deconstruction
|
||||
Graph::Graph(size_t max_points, size_t max_edges) noexcept
|
||||
{
|
||||
all_points = new(std::nothrow) Point[max_points] {};
|
||||
all_edges = new(std::nothrow) Edge[max_edges] {};
|
||||
|
||||
/** Constructor
|
||||
* Creates a new Graph object
|
||||
*
|
||||
* @param max_points : Maximum number of points in the Graph
|
||||
* @param max_edges : Maximum number of edges in the GRAPH (not the point)
|
||||
*/
|
||||
Graph::Graph(size_t max_points, size_t max_edges) noexcept
|
||||
if(all_points && all_edges)
|
||||
{
|
||||
all_points = new(std::nothrow) Point[max_points] {};
|
||||
all_edges = new(std::nothrow) Edge[max_edges] {};
|
||||
|
||||
if(all_points && all_edges)
|
||||
{
|
||||
p_capacity = max_points;
|
||||
e_capacity = max_edges;
|
||||
}
|
||||
p_capacity = max_points;
|
||||
e_capacity = max_edges;
|
||||
}
|
||||
}
|
||||
|
||||
/**Deconstruction
|
||||
* NOTE on the '~Graph()':
|
||||
* The ~ flags this block as a deconstructor. It makes it so this runs before the object is removed from memory preventing memory leaks
|
||||
* Find the memory buffers pointed to by all_points and all_edges, and mark that memory as available for other programs to use.
|
||||
*
|
||||
*/
|
||||
Graph::~Graph()
|
||||
{
|
||||
delete[] all_points;
|
||||
delete[] all_edges;
|
||||
}
|
||||
Graph::~Graph()
|
||||
{
|
||||
delete[] all_points;
|
||||
delete[] all_edges;
|
||||
}
|
||||
|
||||
/** Move Constructor
|
||||
* Instructions to move the current graph object from one memory location to another without copying
|
||||
*/
|
||||
Graph::Graph(Graph&& other) noexcept
|
||||
: all_points(std::exchange(other.all_points, nullptr)),
|
||||
all_edges(std::exchange(other.all_edges, nullptr)),
|
||||
p_capacity(std::exchange(other.p_capacity, 0)),
|
||||
e_capacity(std::exchange(other.e_capacity, 0)) {}
|
||||
Graph::Graph(Graph&& new_location) noexcept
|
||||
: all_points(std::exchange(new_location.all_points, nullptr)),
|
||||
all_edges(std::exchange(new_location.all_edges, nullptr)),
|
||||
p_capacity(std::exchange(new_location.p_capacity, 0)),
|
||||
e_capacity(std::exchange(new_location.e_capacity, 0)) {}
|
||||
// End : Construction & Deconstruction
|
||||
|
||||
|
||||
// Begin : Getters
|
||||
// ----------------------- BEGIN : Getters -----------------------
|
||||
|
||||
/** Getter Function: get_p_edges
|
||||
* Gets the address of the first edge for Point P at index p_index
|
||||
*
|
||||
* @param p_index : The index (int) of the point
|
||||
* @returns Edge* : the address of the first edge for p_index
|
||||
* */
|
||||
Edge* Graph::get_p_edges(int p_index) noexcept
|
||||
{
|
||||
//Checking for for index validity and that the point has edges
|
||||
if(p_index < 0 || (size_t)p_index >= p_capacity) return nullptr;
|
||||
Point* Graph::get_points() noexcept
|
||||
{
|
||||
return all_points;
|
||||
}
|
||||
|
||||
//assigning the start_point_index to be the first_edge in the points array of edges
|
||||
//if it's negative one, there are not edges assigned
|
||||
int start_point_index = all_points[p_index].first_edge;
|
||||
if(start_point_index == -1) return nullptr;
|
||||
Edge* Graph::get_p_edges(int p_index) noexcept
|
||||
{
|
||||
if(p_index < 0 || (size_t)p_index >= p_capacity) return nullptr;
|
||||
|
||||
//if there are edges it returns the address of the first edge so that the user can iterate through them like an array
|
||||
return &all_edges[start_point_index];
|
||||
}
|
||||
int head_idx = all_points[p_index].first_edge;
|
||||
if(head_idx == -1) return nullptr;
|
||||
|
||||
return &all_edges[head_idx];
|
||||
}
|
||||
// ----------------------- END : Getters -----------------------
|
||||
|
||||
|
||||
// ----------------------- BEGIN : Mutators -----------------------
|
||||
|
||||
// Begin : Setters
|
||||
/** add_edge
|
||||
* Adds an edge between two points
|
||||
*
|
||||
* @param start_point_index : the index of the starting point
|
||||
* @param end_point_index : the index of the ending point
|
||||
*
|
||||
**/
|
||||
void Graph::add_edge(int start_point_index, int end_point_index) noexcept
|
||||
{
|
||||
if (next_possible_edge >= e_capacity) return; // Buffer full
|
||||
Point* Graph::add_point(std::string name, float x, float y, float z) noexcept {
|
||||
int target_idx = -1;
|
||||
|
||||
// 1. If this is the first edge for this point, record the start index
|
||||
if (all_points[start_point_index].e_count == 0)
|
||||
{
|
||||
all_points[end_point_index].first_edge = next_possible_edge;
|
||||
}
|
||||
if (next_free_point != -1) {
|
||||
target_idx = next_free_point;
|
||||
// The hijacked 'first_edge' tells us where the NEXT hole is
|
||||
next_free_point = all_points[target_idx].first_edge;
|
||||
}
|
||||
else if (num_points < p_capacity) {
|
||||
target_idx = (int)num_points;
|
||||
num_points++;
|
||||
}
|
||||
else return nullptr;
|
||||
|
||||
// 2. Place the edge in the flat buffer
|
||||
all_edges[next_possible_edge].target_point = end_point_index;
|
||||
Point* p = &all_points[target_idx];
|
||||
p->name = name;
|
||||
p->x = x; p->y = y; p->z = z;
|
||||
p->first_edge = -1;
|
||||
p->e_count = 0;
|
||||
|
||||
// 3. Update counters
|
||||
all_points[start_point_index].e_count++;
|
||||
next_possible_edge++;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
bool Graph::delete_point(int p_index) {
|
||||
if (p_index < 0 || (size_t)p_index >= p_capacity) return false;
|
||||
|
||||
// 1. IMPORTANT: Clean up all edges belonging to this point first!
|
||||
// While the point has edges, delete the 'end_point' of the first edge.
|
||||
while (all_points[p_index].first_edge != -1) {
|
||||
int target_end = all_edges[all_points[p_index].first_edge].end_point;
|
||||
delete_edge(p_index, target_end);
|
||||
}
|
||||
|
||||
// End : Setters
|
||||
// 2. Hijack point for Free List
|
||||
all_points[p_index].first_edge = next_free_point;
|
||||
all_points[p_index].e_count = -1;
|
||||
next_free_point = p_index;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Edge* Graph::add_edge(int e_start, int e_end, float weight) noexcept {
|
||||
// Validation: Ensure indices are within bounds
|
||||
if (e_start < 0 || (size_t)e_start >= p_capacity ||
|
||||
e_end < 0 || (size_t)e_end >= p_capacity) return nullptr;
|
||||
|
||||
int target_edge = -1;
|
||||
|
||||
if (next_free_edge != -1) {
|
||||
target_edge = next_free_edge;
|
||||
next_free_edge = all_edges[target_edge].next_edge;
|
||||
}
|
||||
else if (num_edges < e_capacity) {
|
||||
target_edge = (int)num_edges;
|
||||
num_edges++;
|
||||
}
|
||||
else return nullptr;
|
||||
|
||||
// Initialize and Link
|
||||
all_edges[target_edge].end_point = e_end;
|
||||
all_edges[target_edge].e_weight = weight;
|
||||
|
||||
// Link to Point's head
|
||||
all_edges[target_edge].next_edge = all_points[e_start].first_edge;
|
||||
all_points[e_start].first_edge = target_edge;
|
||||
all_points[e_start].e_count++;
|
||||
|
||||
return &all_edges[target_edge];
|
||||
}
|
||||
|
||||
bool Graph::delete_edge(int e_start, int e_end) noexcept {
|
||||
Point& p = all_points[e_start];
|
||||
int current = p.first_edge;
|
||||
int prev = -1;
|
||||
|
||||
// 1. Traverse the linked list of edges for this point
|
||||
while (current != -1) {
|
||||
if (all_edges[current].end_point == e_end) {
|
||||
// 2. Unhook the node
|
||||
if (prev == -1) p.first_edge = all_edges[current].next_edge;
|
||||
else all_edges[prev].next_edge = all_edges[current].next_edge;
|
||||
|
||||
// 3. Add the edge slot back to the global free list
|
||||
all_edges[current].next_edge = next_free_edge;
|
||||
next_free_edge = current;
|
||||
|
||||
p.e_count--;
|
||||
return true;
|
||||
}
|
||||
prev = current;
|
||||
current = all_edges[current].next_edge;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------- END : Mutators -----------------------
|
||||
Reference in New Issue
Block a user