From 5c53d9af75bd93d3e9a42ed56f8e0947ebb253be Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Sat, 25 Jan 2025 01:37:14 +0530 Subject: [PATCH 1/2] feature: johnson algo initial draft --- Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h | 10 ++++++++++ .../0003_Graph/0014_AllPairsShortestPathsJohnson.cc | 0 SourceCodes/0003_Graph/CMakeLists.txt | 1 + .../0014_AllPairsShortestPathsJohnsonTest.cc | 0 Tests/0003_Graph/CMakeLists.txt | 1 + 5 files changed, 12 insertions(+) create mode 100644 Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h create mode 100644 SourceCodes/0003_Graph/0014_AllPairsShortestPathsJohnson.cc create mode 100644 Tests/0003_Graph/0014_AllPairsShortestPathsJohnsonTest.cc diff --git a/Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h b/Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h new file mode 100644 index 0000000..c2eaaa1 --- /dev/null +++ b/Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +using namespace std; + +namespace AllPairsShortestPathsJohnson +{ + +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0014_AllPairsShortestPathsJohnson.cc b/SourceCodes/0003_Graph/0014_AllPairsShortestPathsJohnson.cc new file mode 100644 index 0000000..e69de29 diff --git a/SourceCodes/0003_Graph/CMakeLists.txt b/SourceCodes/0003_Graph/CMakeLists.txt index 4bfca4c..23daf43 100644 --- a/SourceCodes/0003_Graph/CMakeLists.txt +++ b/SourceCodes/0003_Graph/CMakeLists.txt @@ -13,6 +13,7 @@ set(0003GRAPH_SOURCES 0011_SingleSourceShortestPathDijkstra.cc 0012_DifferenceConstraintsShortestPaths.cc 0013_AllPairsShortestPathsFloydWarshall.cc + 0014_AllPairsShortestPathsJohnson.cc ) diff --git a/Tests/0003_Graph/0014_AllPairsShortestPathsJohnsonTest.cc b/Tests/0003_Graph/0014_AllPairsShortestPathsJohnsonTest.cc new file mode 100644 index 0000000..e69de29 diff --git a/Tests/0003_Graph/CMakeLists.txt b/Tests/0003_Graph/CMakeLists.txt index 3ec92b1..79b0409 100644 --- a/Tests/0003_Graph/CMakeLists.txt +++ b/Tests/0003_Graph/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable( 0011_SingleSourceShortestPathDijkstraTest.cc 0012_DifferenceConstraintsShortestPathsTest.cc 0013_AllPairsShortestPathsFloydWarshallTest.cc + 0014_AllPairsShortestPathsJohnsonTest.cc ) target_link_libraries( From ee712db7886e99e51bdb559764b3b5e94908ce95 Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Sun, 26 Jan 2025 22:28:42 +0530 Subject: [PATCH 2/2] feature-test: johnson algo logic, test added --- .../0014_AllPairsShortestPathsJohnson.h | 56 +++++ .../0014_AllPairsShortestPathsJohnson.cc | 213 ++++++++++++++++++ .../0014_AllPairsShortestPathsJohnsonTest.cc | 44 ++++ 3 files changed, 313 insertions(+) diff --git a/Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h b/Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h index c2eaaa1..388f727 100644 --- a/Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h +++ b/Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h @@ -2,9 +2,65 @@ #include #include +#include using namespace std; namespace AllPairsShortestPathsJohnson { + class Node + { + public: + int data; + int distance; + Node* parent; + int potentialWeight; + Node(int data); + }; + class Edge + { + public: + Node* nodeU; + Node* nodeV; + int weight; + Edge(Node* nodeU, Node* nodeV, int weight); + }; + + class CompareNodeDistance + { + public: + bool operator()(const Node* nodeU, const Node* nodeV) const + { + return nodeU->distance < nodeV->distance; + } + }; + + class Graph + { + private: + int _noOfVertices=0; + map> _adjlist; + map _nodeMap; + vector _edgeList; + map> _edgeMap; + map> _augmentedAdjlist; + vector _augmentedEdgeList; + multiset _operationalSet; + vector> _shortestPathMatrix; + vector> _predecessorMatrix; + Node* MakeOrFindNode(int data); + void PushAugmentedDirectedEdges(Node* sourceNode, Node* nodeV, int weight); + void InitializeSingleSource(Node* sourceNode); + void RelaxBellmanFord(Edge* edge); + bool BellmanFord(Node* source); + void RelaxDijkstra(Edge* edge); + void Dijkstra(Node* source); + void GetShortestPath(int source, int destination, vector& path); + + public: + void PushDirectedEdge(int dataU, int dataV, int weight); + bool FindAllPairsShortestPathsJohnsonAlgorithm(); + vector> GetAllPairsShortestPathsDistanceMatrix(); + vector> GetAllPairsShortestPathsPathMatrix(); + }; } \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0014_AllPairsShortestPathsJohnson.cc b/SourceCodes/0003_Graph/0014_AllPairsShortestPathsJohnson.cc index e69de29..3da8a1f 100644 --- a/SourceCodes/0003_Graph/0014_AllPairsShortestPathsJohnson.cc +++ b/SourceCodes/0003_Graph/0014_AllPairsShortestPathsJohnson.cc @@ -0,0 +1,213 @@ +#include "../Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h" +#include +using namespace std; + +namespace AllPairsShortestPathsJohnson +{ + Node::Node(int data) + { + this->data = data; + this->distance = INT_MAX; + this->parent = nullptr; + this->potentialWeight = 0; + } + + Edge::Edge(Node* nodeU, Node* nodeV, int weight) + { + this->nodeU = nodeU; + this->nodeV = nodeV; + this->weight = weight; + } + + // 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; + } + + void Graph::PushAugmentedDirectedEdges(Node* sourceNode, Node* nodeV, int weight) + { + this->_augmentedAdjlist[sourceNode].push_back(nodeV); + this->_augmentedEdgeList.push_back(new Edge(sourceNode, nodeV, weight)); + } + + void Graph::InitializeSingleSource(Node* sourceNode) + { + for (auto& iterator : this->_nodeMap) + { + iterator.second->distance = INT_MAX; + iterator.second->parent = nullptr; + } + sourceNode->distance = 0; + } + + void Graph::RelaxBellmanFord(Edge* edge) + { + if (edge->nodeU->distance != INT_MAX && (edge->nodeV->distance > (edge->nodeU->distance + edge->weight))) + { + edge->nodeV->distance = edge->nodeU->distance + edge->weight; + edge->nodeV->parent = edge->nodeU; + } + } + + bool Graph::BellmanFord(Node* source) + { + this->InitializeSingleSource(source); + + for (int i = 0; i < this->_nodeMap.size() - 1; i++) + { + for (auto& edge : this->_augmentedEdgeList) + { + this->RelaxBellmanFord(edge); + } + } + + for (auto& edge : this->_augmentedEdgeList) + { + if (edge->nodeV->distance > (edge->nodeU->distance + edge->weight)) + { + return false; + } + } + return true; + } + + void Graph::RelaxDijkstra(Edge* edge) + { + if (edge->nodeU->distance != INT_MAX && (edge->nodeV->distance > (edge->nodeU->distance + edge->weight))) + { + this->_operationalSet.erase(edge->nodeV); + edge->nodeV->distance = edge->nodeU->distance + edge->weight; + edge->nodeV->parent = edge->nodeU; + this->_operationalSet.insert(edge->nodeV); + } + } + + void Graph::Dijkstra(Node* source) + { + this->InitializeSingleSource(source); + + for (auto& node : this->_nodeMap) + { + this->_operationalSet.insert(node.second); + } + + while (!this->_operationalSet.empty()) + { + Node* nodeU = *(this->_operationalSet.begin()); + this->_operationalSet.erase(nodeU); + + for (auto& edge : this->_edgeMap[nodeU]) + { + this->RelaxDijkstra(edge); + } + } + } + + void Graph::GetShortestPath(int source, int destination, vector& path) + { + if (this->_predecessorMatrix[source - 1][destination - 1] != source) + { + int predecessor = this->_predecessorMatrix[source - 1][destination - 1]; + this->GetShortestPath(source, predecessor, path); + path.push_back(predecessor); + } + } + + // Graph Public Member Methods + void Graph::PushDirectedEdge(int dataU, int dataV, int weight) + { + Node* nodeU = this->MakeOrFindNode(dataU); + Node* nodeV = this->MakeOrFindNode(dataV); + + this->_adjlist[nodeU].push_back(nodeV); + Edge* edge = new Edge(nodeU, nodeV, weight); + this->_edgeMap[nodeU].push_back(edge); + this->_edgeList.push_back(edge); + } + + bool Graph::FindAllPairsShortestPathsJohnsonAlgorithm() + { + // Creating the graph G' + this->_augmentedAdjlist = this->_adjlist; + this->_augmentedEdgeList = this->_edgeList; + Node* source = new Node(0); + this->_nodeMap[0] = source; + for (auto& node : this->_nodeMap) + { + if (node.second != source) + { + this->PushAugmentedDirectedEdges(source, node.second, 0); + } + } + + if (this->BellmanFord(source) == false) + { + return false; + } + else + { + this->_nodeMap.erase(0); + for (auto& node : this->_nodeMap) + { + node.second->potentialWeight = node.second->distance; + } + + for (auto& edge : this->_edgeList) + { + edge->weight = edge->weight + edge->nodeU->potentialWeight - edge->nodeV->potentialWeight; + } + + this->_noOfVertices = (int)this->_nodeMap.size(); + this->_shortestPathMatrix = vector>(this->_noOfVertices, vector(this->_noOfVertices, -1)); + this->_predecessorMatrix = vector>(this->_noOfVertices, vector(this->_noOfVertices, -1)); + for (auto& iteratorU : this->_nodeMap) + { + Node* nodeU = iteratorU.second; + this->Dijkstra(nodeU); + for (auto& iteratorV : this->_nodeMap) + { + Node* nodeV = iteratorV.second; + this->_shortestPathMatrix[nodeU->data - 1][nodeV->data - 1] = nodeV->distance + nodeV->potentialWeight - nodeU->potentialWeight; + this->_predecessorMatrix[nodeU->data - 1][nodeV->data - 1] = nodeV->parent != nullptr ? nodeV->parent->data : -1; + } + } + return true; + } + } + + vector> Graph::GetAllPairsShortestPathsDistanceMatrix() + { + return this->_shortestPathMatrix; + } + + vector> Graph::GetAllPairsShortestPathsPathMatrix() + { + vector> result; + for (int i = 0; i < this->_noOfVertices; i++) + { + for (int j = 0; j < this->_noOfVertices; j++) + { + if (i != j) + { + vector path = {}; + path.push_back(i + 1); + this->GetShortestPath(i + 1, j + 1, path); + path.push_back(j + 1); + result.push_back(path); + } + } + } + return result; + } +} \ No newline at end of file diff --git a/Tests/0003_Graph/0014_AllPairsShortestPathsJohnsonTest.cc b/Tests/0003_Graph/0014_AllPairsShortestPathsJohnsonTest.cc index e69de29..55610f2 100644 --- a/Tests/0003_Graph/0014_AllPairsShortestPathsJohnsonTest.cc +++ b/Tests/0003_Graph/0014_AllPairsShortestPathsJohnsonTest.cc @@ -0,0 +1,44 @@ +#include +#include"../Headers/0003_Graph/0014_AllPairsShortestPathsJohnson.h" +#include"../0000_CommonUtilities/UnitTestHelper.h" +using namespace std; + +namespace AllPairsShortestPathsJohnson +{ + UnitTestHelper unitTestHelper; + + TEST(JohnsonAlgorithm, SimpleGraph) + { + // Arrange + Graph graph; + vector> expectedDistanceMatrix = + { + {0, 1, -3, 2, -4}, + {3, 0, -4, 1, -1}, + {7, 4, 0, 5, 3}, + {2, -1, -5, 0, -2}, + {8, 5, 1, 6, 0}, + }; + string expectedPredecessorMatrixesult = "[1 5 4 3 2][1 5 4 3][1 5 4][1 5][2 4 1][2 4 3][2 4][2 4 1 5][3 2 4 1][3 2][3 2 4][3 2 4 1 5][4 1][4 3 2][4 3][4 1 5][5 4 1][5 4 3 2][5 4 3][5 4]"; + + // Act + graph.PushDirectedEdge(1, 2, 3); + graph.PushDirectedEdge(1, 3, 8); + graph.PushDirectedEdge(1, 5, -4); + graph.PushDirectedEdge(2, 4, 1); + graph.PushDirectedEdge(2, 5, 7); + graph.PushDirectedEdge(3, 2, 4); + graph.PushDirectedEdge(4, 3, -5); + graph.PushDirectedEdge(4, 1, 2); + graph.PushDirectedEdge(5, 4, 6); + + bool result = graph.FindAllPairsShortestPathsJohnsonAlgorithm(); + vector> actualDistanceMatrix = graph.GetAllPairsShortestPathsDistanceMatrix(); + string actualPredecessorMatrix = unitTestHelper.SerializeVectorToString(graph.GetAllPairsShortestPathsPathMatrix()); + + // Assert + ASSERT_TRUE(result); + ASSERT_EQ(expectedDistanceMatrix, actualDistanceMatrix); + ASSERT_EQ(expectedPredecessorMatrixesult, actualPredecessorMatrix); + } +} \ No newline at end of file