diff --git a/cpp/algorithms/graphs/object/CMakeLists.txt b/cpp/algorithms/graphs/object/CMakeLists.txt new file mode 100644 index 0000000..ac639a3 --- /dev/null +++ b/cpp/algorithms/graphs/object/CMakeLists.txt @@ -0,0 +1,22 @@ +################################################################################ +## Author: Shaun Reed ## +## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## +## About: A basic CMakeLists configuration to test RBT implementation ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +# + +cmake_minimum_required(VERSION 3.15) + +project( + #[[NAME]] ObjectGraph + VERSION 1.0 + DESCRIPTION "Practice implementing and using object graphs in C++" + LANGUAGES CXX +) + +add_library(lib-graph-object "lib-graph.cpp") + +add_executable(graph-test-object "graph.cpp") +target_link_libraries(graph-test-object lib-graph-object) diff --git a/cpp/algorithms/graphs/object/graph.cpp b/cpp/algorithms/graphs/object/graph.cpp new file mode 100644 index 0000000..4e03a6e --- /dev/null +++ b/cpp/algorithms/graphs/object/graph.cpp @@ -0,0 +1,101 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## +## About: An example of an object graph implementation ## +## Algorithms in this example are found in MIT Intro to Algorithms ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#include "lib-graph.hpp" + + +int main (const int argc, const char * argv[]) +{ + // We could initialize the graph with some localNodes... + std::map> localNodes{ + {1, {2, 5}}, // Node 1 + {2, {1, 6}}, // Node 2 + {3, {4, 6, 7}}, + {4, {3, 7, 8}}, + {5, {1}}, + {6, {2, 3, 7}}, + {7, {3, 4, 6, 8}}, + {8, {4, 6}}, + }; +// Graph bfsGraph(localNodes); + +// Graph testGraph( +// { +// {Node(1, {2, 5})}, +//// {Node(1, {2, 5})}, +// } +// ) + + + std::cout << "\n\n##### Breadth First Search #####\n"; + // Or we could use an initializer list... + // Initialize a example graph for Breadth First Search + Graph bfsGraph ( + { + {Node(1, {2, 5})}, // Node 1 + {Node(2, {1, 6})}, // Node 2... + {Node(3, {4, 6, 7})}, + {Node(4, {3, 7, 8})}, + {Node(5, {1})}, + {Node(6, {2, 3, 7})}, + {Node(7, {3, 4, 6, 8})}, + {Node(8, {4, 6})}, + } + ); + // The graph traversed in this example is seen in MIT Intro to Algorithms + // + Chapter 22, Figure 22.3 on BFS + auto iter = bfsGraph.nodes_.begin(); + std::advance(iter, 1); + bfsGraph.BFS(*iter); + + + std::cout << "\n\n##### Depth First Search #####\n"; + // Initialize an example graph for Depth First Search + Graph dfsGraph ( + { + {1, {2, 4}}, + {2, {5}}, + {3, {5, 6}}, + {4, {2}}, + {5, {4}}, + {6, {6}}, + } + ); + // The graph traversed in this example is seen in MIT Intro to Algorithms + // + Chapter 22, Figure 22.4 on DFS + dfsGraph.DFS(); + + + std::cout << "\n\n##### Topological Sort #####\n"; + // Initialize an example graph for Depth First Search + Graph topologicalGraph ( + { + {1, {4, 5}}, + {2, {5}}, + {3, {}}, + {4, {5, 7}}, + {5, {}}, + {6, {7, 8}}, + {7, {9}}, + {8, {9}}, + {9, {}}, + } + ); + // The graph traversed in this example is seen in MIT Intro to Algorithms + // + Chapter 22, Figure 22.4 on DFS + std::vector order = topologicalGraph.TopologicalSort(); + std::cout << "\n\nTopological order: "; + while (!order.empty()) { + std::cout << order.back().number << " "; + order.pop_back(); + } + std::cout << std::endl; + +} diff --git a/cpp/algorithms/graphs/object/lib-graph.cpp b/cpp/algorithms/graphs/object/lib-graph.cpp new file mode 100644 index 0000000..50866ef --- /dev/null +++ b/cpp/algorithms/graphs/object/lib-graph.cpp @@ -0,0 +1,128 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## +## About: Driver program to test object graph implementation ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ + +#include "lib-graph.hpp" + + +void Graph::BFS(const Node& startNode) const +{ + // Track the nodes we have discovered + // TODO: Do this at the end to maintain the state instead of at beginning? + for (const auto &node : nodes_) node.color = White; + + // Create a queue to visit discovered nodes in FIFO order + std::queue visitQueue; + + // Mark the startNode as in progress until we finish checking adjacent nodes + startNode.color = Gray; + + // Visit the startNode + visitQueue.push(startNode); + + // Continue to visit nodes until there are none left in the graph + while (!visitQueue.empty()) { + // Remove thisNode from the visitQueue, storing its vertex locally + Node thisNode = visitQueue.front(); + visitQueue.pop(); + std::cout << "Visiting node " << thisNode.number << std::endl; + + // Check if we have already discovered all the adjacentNodes to thisNode + for (const auto &adjacent : thisNode.adjacent) { + if (nodes_[adjacent - 1].color == White) { + std::cout << "Found undiscovered adjacentNode: " << adjacent << "\n"; + // Mark the adjacent node as in progress + nodes_[adjacent - 1].color = Gray; + // Add the discovered node the the visitQueue + visitQueue.push(nodes_[adjacent - 1]); + } + } + // We are finished with this node and the adjacent nodes; Mark it discovered + thisNode.color = Black; + } + +} + +void Graph::DFS() const +{ + // Track the nodes we have discovered + for (const auto &node : nodes_) node.color = White; + + // Visit each node in the graph + for (const auto& node : nodes_) { + std::cout << "Visiting node " << node.number << std::endl; + // If the node is undiscovered, visit it + if (node.color == White) { + std::cout << "Found undiscovered node: " << node.number << std::endl; + // Visiting the undiscovered node will check it's adjacent nodes + DFSVisit(node); + } + } + +} + +void Graph::DFSVisit(const Node& startNode) const +{ + startNode.color = Gray; + // Check the adjacent nodes of the startNode + for (const auto &adjacent : startNode.adjacent) { + // If the adjacentNode is undiscovered, visit it + // + Offset by 1 to account for 0 index of discovered vector + if (nodes_[adjacent - 1].color == White) { + std::cout << "Found undiscovered adjacentNode: " + << nodes_[adjacent - 1].number << std::endl; + // Visiting the undiscovered node will check it's adjacent nodes + DFSVisit(nodes_[adjacent - 1]); + } + } + startNode.color = Black; +} + +std::vector Graph::TopologicalSort() const +{ + std::vector topologicalOrder; + + // Track the nodes we have discovered + for (const auto &node : nodes_) node.color = White; + + // Visit each node in the graph + for (const auto &node : nodes_) { + std::cout << "Visiting node " << node.number << std::endl; + // If the node is undiscovered, visit it + if (node.color == White) { + std::cout << "Found undiscovered node: " << node.number << std::endl; + // Visiting the undiscovered node will check it's adjacent nodes + TopologicalVisit(node, topologicalOrder); + } + } + + // The topologicalOrder is read right-to-left in the final result + // + Output is handled in main as FILO, similar to a stack + return topologicalOrder; +} + +void Graph::TopologicalVisit(const Node &startNode, + std::vector &order) const +{ + // Mark the node as visited so we don't visit it twice + startNode.color = Gray; + + // Check the adjacent nodes of the startNode + for (const auto& adjacent : startNode.adjacent) { + // If the adjacentNode is undiscovered, visit it + if (nodes_[adjacent - 1].color == White) { + std::cout << "Found undiscovered adjacentNode: " << adjacent << std::endl; + // Visiting the undiscovered node will check it's adjacent nodes + TopologicalVisit(nodes_[adjacent - 1], order); + } + } + startNode.color = Black; + + // Add startNode to the topologicalOrder + order.push_back(startNode); +} diff --git a/cpp/algorithms/graphs/object/lib-graph.hpp b/cpp/algorithms/graphs/object/lib-graph.hpp new file mode 100644 index 0000000..965ca0a --- /dev/null +++ b/cpp/algorithms/graphs/object/lib-graph.hpp @@ -0,0 +1,58 @@ +/*############################################################################## +## Author: Shaun Reed ## +## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## +## About: An example of an object graph implementation ## +## Algorithms in this example are found in MIT Intro to Algorithms ## +## ## +## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## +################################################################################ +*/ +#ifndef LIB_GRAPH_HPP +#define LIB_GRAPH_HPP + +#include +#include +#include +#include +#include +#include +#include + +// A vertex can also be referred to as a node +// + ... Unless you are a mathematician ^.^ +struct Vertex { + // This vertex's number + int number; + // A set of all vertices adjacent to this vertex + std::set adjacent; +}; + +enum Color {White, Gray, Black}; + +struct Node { +public: + Node(int num, std::set adj) : number(num), adjacent(std::move(adj)) {} + int number; + std::set adjacent; + // Mutable so we can update the color of the nodes during traversal + mutable Color color = White; + std::vector predecessors; + +// bool operator<(const Node &node1) const { return number < node1.number;} + inline void setColor(Color newColor) const { color = newColor;} +}; + +class Graph { +public: + explicit Graph(std::vector nodes) : nodes_(std::move(nodes)) {} + std::vector nodes_; + + void BFS(const Node& startNode) const; + void DFS() const; + void DFSVisit(const Node& startNode) const; + std::vector TopologicalSort() const; + void TopologicalVisit(const Node &startNode, std::vector &order) const; + +}; + +#endif // LIB_GRAPH_HPP