diff --git a/cpp/algorithms/graphs/object/lib-graph.hpp b/cpp/algorithms/graphs/object/lib-graph.hpp index e93d2ba..9dbc439 100644 --- a/cpp/algorithms/graphs/object/lib-graph.hpp +++ b/cpp/algorithms/graphs/object/lib-graph.hpp @@ -63,8 +63,8 @@ enum Color { Black }; -// Information used in all searches -struct SearchInfo { +// Information used in all searches tracked for each node +struct NodeInfo { // Coloring of the nodes is used in both DFS and BFS Color discovered = White; }; @@ -73,8 +73,8 @@ struct SearchInfo { /******************************************************************************/ // BFS search information struct -// Information that is only used in BFS -struct BFS : SearchInfo { +// Node information that is only used in BFS +struct BFS : NodeInfo { // Used to represent distance from start node int distance = 0; // Used to represent the parent node that discovered this node @@ -90,8 +90,8 @@ using InfoBFS = std::unordered_map; /******************************************************************************/ // DFS search information struct -// Information that is only used in DFS -struct DFS : SearchInfo { +// Node information that is only used in DFS +struct DFS : NodeInfo { // Create a pair to track discovery / finish time // + Discovery time is the iteration the node is first discovered // + Finish time is the iteration the node has been checked completely @@ -119,7 +119,7 @@ public: // An alternate DFS that checks each node of the graph beginning at startNode InfoDFS DFS(const Node &startNode) const; // Visit function is used in both versions of DFS - void DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const; + void DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const; // Topological sort, using DFS std::vector TopologicalSort(const Node &startNode) const; diff --git a/cpp/algorithms/graphs/templated/lib-graph.hpp b/cpp/algorithms/graphs/templated/lib-graph.hpp index 2d4b843..d86114c 100644 --- a/cpp/algorithms/graphs/templated/lib-graph.hpp +++ b/cpp/algorithms/graphs/templated/lib-graph.hpp @@ -79,8 +79,8 @@ enum Color { Black }; -// Information used in all searches -struct SearchInfo { +// Information used in all searches tracked for each node +struct NodeInfo { // Coloring of the nodes is used in both DFS and BFS Color discovered = White; }; @@ -89,9 +89,9 @@ struct SearchInfo { /******************************************************************************/ // BFS search information struct -// Information that is only used in BFS +// Node information that is only used in BFS template -struct BFS : SearchInfo { +struct BFS : NodeInfo { // Used to represent distance from start node int distance = 0; // Used to represent the parent node that discovered this node @@ -107,8 +107,8 @@ template using InfoBFS = std::unordered_map>; /******************************************************************************/ // DFS search information struct -// Information that is only used in DFS -struct DFS : SearchInfo { +// Node information that is only used in DFS +struct DFS : NodeInfo { // Create a pair to track discovery / finish time // + Discovery time is the iteration the node is first discovered // + Finish time is the iteration the node has been checked completely @@ -125,7 +125,7 @@ template using InfoDFS = std::unordered_map; // Edges stored as multimap> template using Edges = std::multimap>; -struct MST : SearchInfo { +struct MST : NodeInfo { int32_t parent = INT32_MIN; int rank = 0; }; diff --git a/cpp/algorithms/graphs/weighted/graph.cpp b/cpp/algorithms/graphs/weighted/graph.cpp index f31679d..141eab5 100644 --- a/cpp/algorithms/graphs/weighted/graph.cpp +++ b/cpp/algorithms/graphs/weighted/graph.cpp @@ -156,11 +156,19 @@ int main (const int argc, const char * argv[]) {9, {{3, 2}, {7, 6}}} } ); + std::cout << "\nChecking weight traversing graph from node 1 using DFS...\n"; + InfoDFS resultDFS = graphMST.DFS(graphMST.GetNodeCopy(1)); + std::cout << "DFS total weight traversed: " << resultDFS.totalWeight << std::endl; + + std::cout << "\nChecking weight traversing graph from node 1 using BFS...\n"; + InfoBFS resultBFS = graphMST.BFS(graphMST.GetNodeCopy(1)); + std::cout << "BFS total weight traversed: " << resultBFS.totalWeight << std::endl; + InfoMST resultMST = graphMST.KruskalMST(); - std::cout << "Finding MST using Kruskal's...\n\nMST result: \n"; + std::cout << "\n\nFinding MST using Kruskal's...\n\nMST result: \n"; for (const auto &edge : resultMST.edgesMST) { std::cout << "Connected nodes: " << edge.second.first << "->" << edge.second.second << " with weight of " << edge.first << "\n"; } - std::cout << "Total MST weight: " << resultMST.weightMST << std::endl; + std::cout << "Total MST weight: " << resultMST.totalWeight << std::endl; } diff --git a/cpp/algorithms/graphs/weighted/lib-graph.cpp b/cpp/algorithms/graphs/weighted/lib-graph.cpp index f04d78b..f5d857a 100644 --- a/cpp/algorithms/graphs/weighted/lib-graph.cpp +++ b/cpp/algorithms/graphs/weighted/lib-graph.cpp @@ -14,13 +14,13 @@ InfoBFS Graph::BFS(const Node& startNode) const { // Create local object to track the information gathered during traversal - InfoBFS searchInfo; + InfoBFS bfs; // 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 - searchInfo[startNode.number].discovered = Gray; + bfs.nodeInfo[startNode.number].discovered = Gray; // Visit the startNode visitQueue.push(&startNode); @@ -31,17 +31,17 @@ InfoBFS Graph::BFS(const Node& startNode) const const 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 (searchInfo[adjacent.first].discovered == White) { + if (bfs.nodeInfo[adjacent.first].discovered == White) { std::cout << "Found undiscovered adjacentNode: " << adjacent.first << "\n"; + bfs.totalWeight += adjacent.second; // Mark the adjacent node as in progress - searchInfo[adjacent.first].discovered = Gray; - searchInfo[adjacent.first].distance = - searchInfo[thisNode->number].distance + 1; - searchInfo[adjacent.first].predecessor = + bfs.nodeInfo[adjacent.first].discovered = Gray; + bfs.nodeInfo[adjacent.first].distance = + bfs.nodeInfo[thisNode->number].distance + 1; + bfs.nodeInfo[adjacent.first].predecessor = &GetNode(thisNode->number); // Add the discovered node the the visitQueue @@ -49,11 +49,11 @@ InfoBFS Graph::BFS(const Node& startNode) const } } // We are finished with this node and the adjacent nodes; Mark it discovered - searchInfo[thisNode->number].discovered = Black; + bfs.nodeInfo[thisNode->number].discovered = Black; } // Return the information gathered from this search, JIC caller needs it - return searchInfo; + return bfs; } std::deque Graph::PathBFS(const Node &start, const Node &finish) const @@ -62,8 +62,8 @@ std::deque Graph::PathBFS(const Node &start, const Node &finish) const // + If the caller modifies these, it will not impact the graph's data std::deque path; - InfoBFS searchInfo = BFS(start); - const Node * next = searchInfo[finish.number].predecessor; + InfoBFS bfs = BFS(start); + const Node * next = bfs.nodeInfo[finish.number].predecessor; bool isValid = false; do { // If we have reached the start node, we have found a valid path @@ -74,7 +74,7 @@ std::deque Graph::PathBFS(const Node &start, const Node &finish) const path.emplace_front(*next); // Move to the next node - next = searchInfo[next->number].predecessor; + next = bfs.nodeInfo[next->number].predecessor; } while (next != nullptr); // Use emplace_back to call Node copy constructor path.emplace_back(finish); @@ -89,85 +89,83 @@ std::deque Graph::PathBFS(const Node &start, const Node &finish) const InfoDFS Graph::DFS() const { // Track the nodes we have discovered - InfoDFS searchInfo; + InfoDFS dfs; int time = 0; // Visit each node in the graph - for (const auto& node : nodes_) { + for (const auto & node : nodes_) { std::cout << "Visiting node " << node.number << std::endl; // If the node is undiscovered, visit it - if (searchInfo[node.number].discovered == White) { + if (dfs.nodeInfo[node.number].discovered == White) { std::cout << "Found undiscovered node: " << node.number << std::endl; // Visiting the undiscovered node will check it's adjacent nodes - DFSVisit(time, node, searchInfo); + DFSVisit(time, node, dfs); } } - return searchInfo; + return dfs; } InfoDFS Graph::DFS(const Node &startNode) const { // Track the nodes we have discovered - InfoDFS searchInfo; + InfoDFS dfs; int time = 0; - auto startIter = std::find(nodes_.begin(), nodes_.end(), - Node(startNode.number, {}) - ); - + auto startIter = + std::find(nodes_.begin(), nodes_.end(), Node(startNode.number, { })); // beginning at startNode, visit each node in the graph until we reach the end while (startIter != nodes_.end()) { std::cout << "Visiting node " << startIter->number << std::endl; // If the startIter is undiscovered, visit it - if (searchInfo[startIter->number].discovered == White) { - std::cout << "Found undiscovered node: " << startIter->number << std::endl; + if (dfs.nodeInfo[startIter->number].discovered == White) { + std::cout << "Found undiscovered node: " << startIter->number + << std::endl; // Visiting the undiscovered node will check it's adjacent nodes - DFSVisit(time, *startIter, searchInfo); + DFSVisit(time, *startIter, dfs); } startIter++; } // Once we reach the last node, check the beginning for unchecked nodes startIter = nodes_.begin(); - // Once we reach the initial startNode, we have checked all nodes while (*startIter != startNode) { std::cout << "Visiting node " << startIter->number << std::endl; // If the startIter is undiscovered, visit it - if (searchInfo[startIter->number].discovered == White) { + if (dfs.nodeInfo[startIter->number].discovered == White) { std::cout << "Found undiscovered node: " << startIter->number << std::endl; // Visiting the undiscovered node will check it's adjacent nodes - DFSVisit(time, *startIter, searchInfo); + DFSVisit(time, *startIter, dfs); } startIter++; } - return searchInfo; + return dfs; } -void Graph::DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const +void Graph::DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const { - searchInfo[startNode.number].discovered = Gray; + dfs.nodeInfo[startNode.number].discovered = Gray; time++; - searchInfo[startNode.number].discoveryFinish.first = time; + dfs.nodeInfo[startNode.number].discoveryFinish.first = time; // Check the adjacent nodes of the startNode - for (const auto &adjacent : startNode.adjacent) { - auto iter = std::find(nodes_.begin(), nodes_.end(), - Node(adjacent.first, {})); + for (const auto & adjacent : startNode.adjacent) { + const auto node = GetNode(adjacent.first); // If the adjacentNode is undiscovered, visit it // + Offset by 1 to account for 0 index of discovered vector - if (searchInfo[iter->number].discovered == White) { - std::cout << "Found undiscovered adjacentNode: " - << GetNode(adjacent.first).number << std::endl; + if (dfs.nodeInfo[node.number].discovered == White) { + std::cout << "Found undiscovered adjacentNode: " << adjacent.first + << " with weight of " << adjacent.second << std::endl; // Visiting the undiscovered node will check it's adjacent nodes - DFSVisit(time, *iter, searchInfo); + dfs.totalWeight += adjacent.second; + DFSVisit(time, node, dfs); } } - searchInfo[startNode.number].discovered = Black; + dfs.nodeInfo[startNode.number].discovered = Black; time++; - searchInfo[startNode.number].discoveryFinish.second = time; + dfs.nodeInfo[startNode.number].discoveryFinish.second = time; } std::vector Graph::TopologicalSort(const Node &startNode) const @@ -177,8 +175,8 @@ std::vector Graph::TopologicalSort(const Node &startNode) const std::vector order(nodes_); auto comp = [&topological](const Node &a, const Node &b) { - return (topological[a.number].discoveryFinish.second < - topological[b.number].discoveryFinish.second); + return (topological.nodeInfo[a.number].discoveryFinish.second < + topological.nodeInfo[b.number].discoveryFinish.second); }; std::sort(order.begin(), order.end(), comp); @@ -190,26 +188,26 @@ std::vector Graph::TopologicalSort(const Node &startNode) const InfoMST Graph::KruskalMST() const { - InfoMST searchInfo(nodes_); + InfoMST mst(nodes_); // The ctor for InfoMST initializes all edges within the graph into a multimap // + Key for multimap is edge weight, so they're already sorted in ascending // For each edge in the graph, check if they are part of the same tree // + Since we do not want to create a cycle in the MST forest - // + we don't connect nodes that are part of the same tree - for (const auto &edge : searchInfo.edges) { + for (const auto &edge : mst.edges) { // Two integers representing the node.number for the connected nodes const int u = edge.second.first; const int v = edge.second.second; // Check if the nodes are of the same tree - if (searchInfo.FindSet(u) != searchInfo.FindSet(v)) { + if (mst.FindSet(u) != mst.FindSet(v)) { // If they are not, add the edge to our MST - searchInfo.edgesMST.emplace(edge); - searchInfo.weightMST += edge.first; + mst.edgesMST.emplace(edge); + mst.totalWeight += edge.first; // Update the forest to reflect this change - searchInfo.Union(u, v); + mst.Union(u, v); } } - return searchInfo; + return mst; } diff --git a/cpp/algorithms/graphs/weighted/lib-graph.hpp b/cpp/algorithms/graphs/weighted/lib-graph.hpp index 334075c..26fa665 100644 --- a/cpp/algorithms/graphs/weighted/lib-graph.hpp +++ b/cpp/algorithms/graphs/weighted/lib-graph.hpp @@ -69,18 +69,29 @@ enum Color { Black }; -// Information used in all searches -struct SearchInfo { +// Information used in all searches tracked for each node +struct NodeInfo { // Coloring of the nodes is used in both DFS and BFS Color discovered = White; }; +// Template for tracking graph information gathered during traversals +// + Used for DFS, BFS, and MST +template +struct GraphInfo { + // Store search information in unordered_maps so we can pass it around easily + // + Allows each node to store relative information on the traversal + std::unordered_map nodeInfo; + // Track total weight for all traversals + int totalWeight = 0; +}; + /******************************************************************************/ // BFS search information struct -// Information that is only used in BFS -struct BFS : SearchInfo { +// Node search information that is only used in BFS +struct BFS : NodeInfo { // Used to represent distance from start node int distance = 0; // Used to represent the parent node that discovered this node @@ -88,16 +99,14 @@ struct BFS : SearchInfo { const Node *predecessor = nullptr; }; -// Store search information in unordered_maps so we can pass it around easily -// + Allows each node to store relative information on the traversal -using InfoBFS = std::unordered_map; +struct InfoBFS : GraphInfo {/* Members inherited from GraphInfo */}; /******************************************************************************/ // DFS search information struct // Information that is only used in DFS -struct DFS : SearchInfo { +struct DFS : NodeInfo { // Create a pair to track discovery / finish time // + Discovery time is the iteration the node is first discovered // + Finish time is the iteration the node has been checked completely @@ -105,18 +114,19 @@ struct DFS : SearchInfo { std::pair discoveryFinish; }; +struct InfoDFS : GraphInfo {/* Members inherited from GraphInfo */}; + /******************************************************************************/ // MST search information struct -struct MST : SearchInfo { +struct MST : NodeInfo { int32_t parent = INT32_MIN; int rank = 0; }; -using InfoDFS = std::unordered_map; using Edges = std::multimap>; -struct InfoMST { +struct InfoMST : GraphInfo{ explicit InfoMST(const std::vector &nodes) { for (const auto &node : nodes) { @@ -134,20 +144,17 @@ struct InfoMST { } } - std::unordered_map searchInfo; // All of the edges within our graph // + Since each node stores its own edges, this is initialized in InfoMST ctor Edges edges; // A multimap of the edges found for our MST Edges edgesMST; - // The total weight of our resulting MST - int weightMST = 0; void MakeSet(int x) { - searchInfo[x].parent = x; - searchInfo[x].rank = 0; + nodeInfo[x].parent = x; + nodeInfo[x].rank = 0; } void Union(int x, int y) @@ -157,23 +164,23 @@ struct InfoMST { void Link(int x, int y) { - if (searchInfo[x].rank > searchInfo[y].rank) { - searchInfo[y].parent = x; + if (nodeInfo[x].rank > nodeInfo[y].rank) { + nodeInfo[y].parent = x; } else { - searchInfo[x].parent = y; - if (searchInfo[x].rank == searchInfo[y].rank) { - searchInfo[y].rank += 1; + nodeInfo[x].parent = y; + if (nodeInfo[x].rank == nodeInfo[y].rank) { + nodeInfo[y].rank += 1; } } } int FindSet(int x) { - if (x != searchInfo[x].parent) { - searchInfo[x].parent = FindSet(searchInfo[x].parent); + if (x != nodeInfo[x].parent) { + nodeInfo[x].parent = FindSet(nodeInfo[x].parent); } - return searchInfo[x].parent; + return nodeInfo[x].parent; } }; @@ -195,7 +202,7 @@ public: // An alternate DFS that checks each node of the graph beginning at startNode InfoDFS DFS(const Node &startNode) const; // Visit function is used in both versions of DFS - void DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const; + void DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const; // Topological sort, using DFS std::vector TopologicalSort(const Node &startNode) const; // Kruskal's MST