diff --git a/Headers/0003_Graph/0008_MinimumSpanningTreePrimAlgorithm.h b/Headers/0003_Graph/0008_MinimumSpanningTreePrimAlgorithm.h new file mode 100644 index 0000000..2f335ac --- /dev/null +++ b/Headers/0003_Graph/0008_MinimumSpanningTreePrimAlgorithm.h @@ -0,0 +1,42 @@ +# pragma once + +#include +#include +#include +using namespace std; + +namespace MinimumSpanningTreePrimAlgorithm +{ + class Node + { + public: + int data; + Node* parent; + int key; + bool isInOperationalSet; + Node(int data); + }; + + class CompareNodeKey + { + public: + bool operator()(const Node* nodeU, const Node* nodeV) const + { + return nodeU->key < nodeV->key; + } + }; + + class Graph + { + private: + map>> _adjlist; + map _nodeMap; + vector, int>> _minimumSpanningTree; + multiset _operationalSet; + Node* MakeOrFindNode(int data); + public: + void PushUndirectedEdge(int valueU, int valueV, int weight); + void FindMinimumSpanningTreePrimAlgorithm(); + vector, int>> GetMinimumSpanningTree(); + }; +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0008_MinimumSpanningTreePrimAlgorithm.cc b/SourceCodes/0003_Graph/0008_MinimumSpanningTreePrimAlgorithm.cc new file mode 100644 index 0000000..ce56ee1 --- /dev/null +++ b/SourceCodes/0003_Graph/0008_MinimumSpanningTreePrimAlgorithm.cc @@ -0,0 +1,85 @@ +#include "../Headers/0003_Graph/0008_MinimumSpanningTreePrimAlgorithm.h" +#include +using namespace std; + + +namespace MinimumSpanningTreePrimAlgorithm +{ + Node::Node(int data) + { + this->data = data; + this->parent = nullptr; + this->key = INT_MAX; + this->isInOperationalSet = false; + } + + // Graph Private Member Methods + Node* Graph::MakeOrFindNode(int data) + { + Node* node = nullptr; + if (this->_nodeMap.find(data) == this->_nodeMap.end()) + { + node = new Node(data); + this->_nodeMap[data] = node; + } + else + { + node = this->_nodeMap[data]; + } + return node; + } + + // Graph Public Member Methods + void Graph::PushUndirectedEdge(int dataU, int dataV, int weight) + { + Node* nodeU = this->MakeOrFindNode(dataU); + Node* nodeV = this->MakeOrFindNode(dataV); + + this->_adjlist[nodeU].push_back({nodeV, weight}); + this->_adjlist[nodeV].push_back({nodeU, weight}); + } + + void Graph::FindMinimumSpanningTreePrimAlgorithm() + { + Node* root = this->_nodeMap.begin()->second; + + root->key = 0; + for (auto& iterator : this->_nodeMap) + { + iterator.second->isInOperationalSet = true; + this->_operationalSet.insert(iterator.second); + } + + while (!this->_operationalSet.empty()) + { + Node* nodeU = *(this->_operationalSet.begin()); + this->_operationalSet.erase(nodeU); + nodeU->isInOperationalSet = false; + + for (auto& iterator : this->_adjlist[nodeU]) + { + Node* nodeV = iterator.first; + int weight = iterator.second; + + if (nodeV->isInOperationalSet && weight < nodeV->key) + { + this->_operationalSet.erase(nodeV); + nodeV->key = weight; + nodeV->parent = nodeU; + this->_operationalSet.insert(nodeV); + + } + } + + if (nodeU->parent != nullptr) + { + this->_minimumSpanningTree.push_back({ {nodeU->parent->data, nodeU->data}, nodeU->key }); + } + } + } + + vector, int>> Graph::GetMinimumSpanningTree() + { + return this->_minimumSpanningTree; + } +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/CMakeLists.txt b/SourceCodes/0003_Graph/CMakeLists.txt index 68612e3..0837512 100644 --- a/SourceCodes/0003_Graph/CMakeLists.txt +++ b/SourceCodes/0003_Graph/CMakeLists.txt @@ -7,6 +7,7 @@ set(0003GRAPH_SOURCES 0005_HamiltonianPathAndCycle.cc 0006_EulerianPathAndCircuit.cc 0007_MinimumSpanningTreeKruskalAlgorithm.cc + 0008_MinimumSpanningTreePrimAlgorithm.cc ) diff --git a/Tests/0000_CommonUtilities/UnitTestHelper.h b/Tests/0000_CommonUtilities/UnitTestHelper.h index eae749a..c903d36 100644 --- a/Tests/0000_CommonUtilities/UnitTestHelper.h +++ b/Tests/0000_CommonUtilities/UnitTestHelper.h @@ -150,6 +150,10 @@ class UnitTestHelper sort(data.begin(), data.end(), [](const pair, T>& pairA, const pair, T>& pairB) { + if (pairA.second == pairB.second) + { + return pairA.first.first < pairB.first.first; + } return pairA.second < pairB.second; }); return data; diff --git a/Tests/0003_Graph/0008_MinimumSpanningTreePrimAlgorithmTest.cc b/Tests/0003_Graph/0008_MinimumSpanningTreePrimAlgorithmTest.cc new file mode 100644 index 0000000..2016d04 --- /dev/null +++ b/Tests/0003_Graph/0008_MinimumSpanningTreePrimAlgorithmTest.cc @@ -0,0 +1,45 @@ +#include +#include "../Headers/0003_Graph/0008_MinimumSpanningTreePrimAlgorithm.h" +#include "../0000_CommonUtilities/UnitTestHelper.h" + +namespace MinimumSpanningTreePrimAlgorithm +{ + UnitTestHelper unitTestHelper; + + TEST(MST, Prim) + { + Graph graph; + + graph.PushUndirectedEdge(1, 2, 4); + graph.PushUndirectedEdge(1, 8, 8); + graph.PushUndirectedEdge(2, 8, 11); + graph.PushUndirectedEdge(2, 3, 8); + graph.PushUndirectedEdge(3, 4, 7); + graph.PushUndirectedEdge(3, 9, 2); + graph.PushUndirectedEdge(3, 6, 4); + graph.PushUndirectedEdge(4, 5, 9); + graph.PushUndirectedEdge(4, 6, 14); + graph.PushUndirectedEdge(5, 6, 10); + graph.PushUndirectedEdge(6, 7, 2); + graph.PushUndirectedEdge(7, 8, 1); + graph.PushUndirectedEdge(7, 9, 6); + graph.PushUndirectedEdge(8, 9, 7); + + graph.FindMinimumSpanningTreePrimAlgorithm(); + + vector, int>> actualMST = graph.GetMinimumSpanningTree(); + vector, int>> expectedMST = + { + {{7, 8}, 1}, + {{3, 9}, 2}, + {{6, 7}, 2}, + {{2, 1}, 4}, + {{3, 6}, 4}, + {{3, 4}, 7}, + {{8, 1}, 8}, + {{4, 5}, 9} + }; + + ASSERT_EQ(unitTestHelper.SortVectorOfPair(actualMST), unitTestHelper.SortVectorOfPair(expectedMST)); + } +} \ No newline at end of file diff --git a/Tests/0003_Graph/CMakeLists.txt b/Tests/0003_Graph/CMakeLists.txt index b355b1e..b5e4144 100644 --- a/Tests/0003_Graph/CMakeLists.txt +++ b/Tests/0003_Graph/CMakeLists.txt @@ -19,6 +19,7 @@ add_executable( 0005_HamiltonianPathAndCycleTest.cc 0006_EulerianPathAndCircuitTest.cc 0007_MinimumSpanningTreeKruskalAlgorithmTest.cc + 0008_MinimumSpanningTreePrimAlgorithmTest.cc ) target_link_libraries(