diff --git a/Headers/0003_Graph/0009_SingleSourceShortestPathBellmanFord.h b/Headers/0003_Graph/0009_SingleSourceShortestPathBellmanFord.h new file mode 100644 index 0000000..2c509c9 --- /dev/null +++ b/Headers/0003_Graph/0009_SingleSourceShortestPathBellmanFord.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +using namespace std; + +namespace SingleSourceShortestPathBellmanFord +{ + class Node + { + public: + int data; + int distance; + Node* parent; + Node(int data); + }; + + class Edge + { + public: + Node* nodeU; + Node* nodeV; + int weight; + Edge(Node* nodeU, Node* nodeV, int weight); + }; + + class Graph + { + private: + map> _adjlist; + map _nodeMap; + vector _edgeList; + Node* MakeOrFindNode(int data); + void InitializeSingleSource(Node* sourceNode); + void Relax(Edge* edge); + void GetShortestPath(Node* node, vector& path); + + + public: + void PushDirectedEdge(int valueU, int valueV, int weight); + bool FindSingleSourceShortestPathBellmanFord(int data); + vector GetShortestPathBellmanFord(int data); + }; +} \ No newline at end of file diff --git a/Headers/0003_Graph/0010_DirectedAcyclicGraphShortestPath.h b/Headers/0003_Graph/0010_DirectedAcyclicGraphShortestPath.h new file mode 100644 index 0000000..37f6610 --- /dev/null +++ b/Headers/0003_Graph/0010_DirectedAcyclicGraphShortestPath.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +using namespace std; + +namespace DirectedAcyclicGraphShortestPath +{ + enum color {WHITE, GRAY, BLACK}; + + class Node + { + public: + int data; + int color; + int distance; + Node* parent; + Node(int data); + }; + + class Edge + { + public: + Node* nodeU; + Node* nodeV; + int weight; + Edge(Node* nodeU, Node* nodeV, int weight); + }; + + class Graph + { + private: + map> _adjlist; + map _nodeMap; + map> _edgeMap; + list _topologicalSortedNodeList; + Node* MakeOrFindNode(int data); + void DepthFirstSearch(Node* node); + void TopologicalSort(); + void InitializeSingleSource(Node* sourceNode); + void Relax(Edge* edge); + void GetShortestPath(Node* node, vector& path); + + + public: + void PushDirectedEdge(int valueU, int valueV, int weight); + void FindDAGShortestPath(int data); + vector GetDAGShortestPath(int data); + }; +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0003_TopologicalSort.cc b/SourceCodes/0003_Graph/0003_TopologicalSort.cc index 18dd52a..526456c 100644 --- a/SourceCodes/0003_Graph/0003_TopologicalSort.cc +++ b/SourceCodes/0003_Graph/0003_TopologicalSort.cc @@ -36,7 +36,7 @@ namespace TopologicalSort this->time++; nodeU->discoveryTime = this->time; nodeU->color = GRAY; - for (auto nodeV : this->_adjlist[nodeU]) + for (auto& nodeV : this->_adjlist[nodeU]) { if (nodeV->color == WHITE) { diff --git a/SourceCodes/0003_Graph/0009_SingleSourceShortestPathBellmanFord.cc b/SourceCodes/0003_Graph/0009_SingleSourceShortestPathBellmanFord.cc new file mode 100644 index 0000000..5d2decf --- /dev/null +++ b/SourceCodes/0003_Graph/0009_SingleSourceShortestPathBellmanFord.cc @@ -0,0 +1,108 @@ +#include "../Headers/0003_Graph/0009_SingleSourceShortestPathBellmanFord.h" +#include +#include +using namespace std; + +namespace SingleSourceShortestPathBellmanFord +{ + Node::Node(int data) + { + this->data = data; + this->distance = INT_MAX; + this->parent = nullptr; + } + + 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 :: InitializeSingleSource(Node* sourceNode) + { + for (auto& iterator : this->_nodeMap) + { + iterator.second->distance = INT_MAX; + iterator.second->parent = nullptr; + } + sourceNode->distance = 0; + } + + void Graph::Relax(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; + } + } + + void Graph::GetShortestPath(Node* node, vector& path) + { + path.push_back(node->data); + if (node->parent != nullptr) + { + this->GetShortestPath(node->parent, path); + } + } + + // 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); + this->_edgeList.push_back(new Edge(nodeU, nodeV, weight)); + } + + bool Graph::FindSingleSourceShortestPathBellmanFord(int data) + { + Node* source = this->_nodeMap[data]; + + this->InitializeSingleSource(source); + + for (int i = 0; i < this->_nodeMap.size() - 1; i++) + { + for (auto& edge : this->_edgeList) + { + this->Relax(edge); + } + } + + for (auto& edge : this->_edgeList) + { + if (edge->nodeV->distance > (edge->nodeU->distance + edge->weight)) + { + return false; + } + } + return true; + } + + vector Graph::GetShortestPathBellmanFord(int data) + { + vector path = {}; + Node* node = this->_nodeMap[data]; + this->GetShortestPath(node, path); + reverse(path.begin(), path.end()); + return path; + } +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0010_DirectedAcyclicGraphShortestPath.cc b/SourceCodes/0003_Graph/0010_DirectedAcyclicGraphShortestPath.cc new file mode 100644 index 0000000..da683fb --- /dev/null +++ b/SourceCodes/0003_Graph/0010_DirectedAcyclicGraphShortestPath.cc @@ -0,0 +1,124 @@ +#include "../Headers/0003_Graph/0010_DirectedAcyclicGraphShortestPath.h" +#include +#include +using namespace std; + +namespace DirectedAcyclicGraphShortestPath +{ + Node::Node(int data) + { + this->data = data; + this->color = WHITE; + this->distance = INT_MAX; + this->parent = nullptr; + } + + 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::DepthFirstSearch(Node* nodeU) + { + nodeU->color = GRAY; + for (auto& nodeV : this->_adjlist[nodeU]) + { + if (nodeV->color == WHITE) + { + this->DepthFirstSearch(nodeV); + } + } + nodeU->color = BLACK; + this->_topologicalSortedNodeList.push_front(nodeU); + } + + void Graph::TopologicalSort() + { + for (auto& iterator : this->_nodeMap) + { + if (iterator.second->color == WHITE) + { + this->DepthFirstSearch(iterator.second); + } + } + } + + void Graph::InitializeSingleSource(Node* sourceNode) + { + for (auto& iterator : this->_nodeMap) + { + iterator.second->distance = INT_MAX; + iterator.second->parent = nullptr; + } + sourceNode->distance = 0; + } + + void Graph::Relax(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; + } + } + + void Graph::GetShortestPath(Node* node, vector& path) + { + path.push_back(node->data); + if (node->parent != nullptr) + { + this->GetShortestPath(node->parent, path); + } + } + + // 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); + this->_edgeMap[nodeU].push_back(new Edge(nodeU, nodeV, weight)); + } + + void Graph::FindDAGShortestPath(int data) + { + this->TopologicalSort(); + Node* source = this->_nodeMap[data]; + this->InitializeSingleSource(source); + for (auto& node : this->_topologicalSortedNodeList) + { + for (auto& edge : this->_edgeMap[node]) + { + this->Relax(edge); + } + } + } + + vector Graph::GetDAGShortestPath(int data) + { + vector path = {}; + Node* node = this->_nodeMap[data]; + this->GetShortestPath(node, path); + reverse(path.begin(), path.end()); + return path; + } +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/CMakeLists.txt b/SourceCodes/0003_Graph/CMakeLists.txt index 0837512..5ddfee0 100644 --- a/SourceCodes/0003_Graph/CMakeLists.txt +++ b/SourceCodes/0003_Graph/CMakeLists.txt @@ -8,6 +8,8 @@ set(0003GRAPH_SOURCES 0006_EulerianPathAndCircuit.cc 0007_MinimumSpanningTreeKruskalAlgorithm.cc 0008_MinimumSpanningTreePrimAlgorithm.cc + 0009_SingleSourceShortestPathBellmanFord.cc + 0010_DirectedAcyclicGraphShortestPath.cc ) diff --git a/Tests/0003_Graph/0009_SingleSourceShortestPathBellmanFordTest.cc b/Tests/0003_Graph/0009_SingleSourceShortestPathBellmanFordTest.cc new file mode 100644 index 0000000..f6bc452 --- /dev/null +++ b/Tests/0003_Graph/0009_SingleSourceShortestPathBellmanFordTest.cc @@ -0,0 +1,104 @@ +#include +#include "../Headers/0003_Graph/0009_SingleSourceShortestPathBellmanFord.h" +#include "../0000_CommonUtilities/UnitTestHelper.h" + +namespace SingleSourceShortestPathBellmanFord +{ + UnitTestHelper unitTestHelper; + + // Test for Simple Graph + TEST(BellmanFordTest, SimpleTest) + { + Graph graph; + + graph.PushDirectedEdge(0, 1, 6); + graph.PushDirectedEdge(0, 3, 7); + graph.PushDirectedEdge(1, 2, 5); + graph.PushDirectedEdge(1, 3, 8); + graph.PushDirectedEdge(1, 4, -4); + graph.PushDirectedEdge(2, 1, -2); + graph.PushDirectedEdge(3, 2, -3); + graph.PushDirectedEdge(3, 4, 9); + graph.PushDirectedEdge(3, 4, 9); + graph.PushDirectedEdge(4, 2, 7); + graph.PushDirectedEdge(4, 0, 2); + + string expectedResult = "0 3 2 1 4"; + ASSERT_TRUE(graph.FindSingleSourceShortestPathBellmanFord(0)); + ASSERT_EQ(unitTestHelper.SerializeVectorToString(graph.GetShortestPathBellmanFord(4)), expectedResult); + } + + // Test for Single Node Graph + TEST(BellmanFordTest, SingleNodeTest) + { + Graph graph; + graph.PushDirectedEdge(0, 0, 0); // Self-loop + + string expectedResult = "0"; + ASSERT_TRUE(graph.FindSingleSourceShortestPathBellmanFord(0)); + ASSERT_EQ(unitTestHelper.SerializeVectorToString(graph.GetShortestPathBellmanFord(0)), expectedResult); + } + + // Test for Negative Weight Cycle + TEST(BellmanFordTest, NegativeWeightCycleTest) + { + Graph graph; + graph.PushDirectedEdge(0, 1, 1); + graph.PushDirectedEdge(1, 2, -1); + graph.PushDirectedEdge(2, 0, -1); // Negative weight cycle + + ASSERT_FALSE(graph.FindSingleSourceShortestPathBellmanFord(0)); + } + + // Test for Multiple Shortest Paths + TEST(BellmanFordTest, MultipleShortestPathsTest) + { + Graph graph; + graph.PushDirectedEdge(0, 1, 5); + graph.PushDirectedEdge(0, 2, 5); + graph.PushDirectedEdge(1, 3, 1); + graph.PushDirectedEdge(2, 3, 1); + + string expectedResult = "0 1 3"; + ASSERT_TRUE(graph.FindSingleSourceShortestPathBellmanFord(0)); + ASSERT_EQ(unitTestHelper.SerializeVectorToString(graph.GetShortestPathBellmanFord(3)), expectedResult); + } + + // Test for All Negative Weights + TEST(BellmanFordTest, AllNegativeWeightsTest) + { + Graph graph; + graph.PushDirectedEdge(0, 1, -5); + graph.PushDirectedEdge(1, 2, -3); + graph.PushDirectedEdge(2, 3, -2); + graph.PushDirectedEdge(3, 4, -1); + + string expectedResult = "0 1 2 3 4"; + ASSERT_TRUE(graph.FindSingleSourceShortestPathBellmanFord(0)); + ASSERT_EQ(unitTestHelper.SerializeVectorToString(graph.GetShortestPathBellmanFord(4)), expectedResult); + } + + // Test for Large Graph + TEST(BellmanFordTest, LargeGraphTest) + { + Graph graph; + for (int i = 0; i < 100; ++i) { + graph.PushDirectedEdge(i, i + 1, 1); + } + + string expectedResult = "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20"; + ASSERT_TRUE(graph.FindSingleSourceShortestPathBellmanFord(0)); + ASSERT_EQ(unitTestHelper.SerializeVectorToString(graph.GetShortestPathBellmanFord(20)), expectedResult); + } + + // Test for Self-Loop Edge + TEST(BellmanFordTest, SelfLoopTest) + { + Graph graph; + graph.PushDirectedEdge(0, 0, 10); // Self-loop with weight 10 + + string expectedResult = "0"; + ASSERT_TRUE(graph.FindSingleSourceShortestPathBellmanFord(0)); + ASSERT_EQ(unitTestHelper.SerializeVectorToString(graph.GetShortestPathBellmanFord(0)), expectedResult); + } +} diff --git a/Tests/0003_Graph/0010_DirectedAcyclicGraphShortestPathTest.cc b/Tests/0003_Graph/0010_DirectedAcyclicGraphShortestPathTest.cc new file mode 100644 index 0000000..3a59445 --- /dev/null +++ b/Tests/0003_Graph/0010_DirectedAcyclicGraphShortestPathTest.cc @@ -0,0 +1,29 @@ +#include +#include "../Headers/0003_Graph/0010_DirectedAcyclicGraphShortestPath.h" +#include "../0000_CommonUtilities/UnitTestHelper.h" + +namespace DirectedAcyclicGraphShortestPath +{ + UnitTestHelper unitTestHelper; + + // Test for Simple Graph + TEST(DAGTest, SimpleGraph) + { + Graph graph; + + graph.PushDirectedEdge(0, 1, 5); + graph.PushDirectedEdge(0, 2, 3); + graph.PushDirectedEdge(1, 2, 2); + graph.PushDirectedEdge(1, 3, 6); + graph.PushDirectedEdge(2, 3, 7); + graph.PushDirectedEdge(2, 4, 4); + graph.PushDirectedEdge(2, 5, 2); + graph.PushDirectedEdge(3, 4, -1); + graph.PushDirectedEdge(3, 5, 1); + graph.PushDirectedEdge(4, 5, -2); + + graph.FindDAGShortestPath(1); + string expectedPath = "1 3 4 5"; + ASSERT_EQ(unitTestHelper.SerializeVectorToString(graph.GetDAGShortestPath(5)), expectedPath); + } +} \ No newline at end of file diff --git a/Tests/0003_Graph/CMakeLists.txt b/Tests/0003_Graph/CMakeLists.txt index b5e4144..66a6d5b 100644 --- a/Tests/0003_Graph/CMakeLists.txt +++ b/Tests/0003_Graph/CMakeLists.txt @@ -20,6 +20,8 @@ add_executable( 0006_EulerianPathAndCircuitTest.cc 0007_MinimumSpanningTreeKruskalAlgorithmTest.cc 0008_MinimumSpanningTreePrimAlgorithmTest.cc + 0009_SingleSourceShortestPathBellmanFordTest.cc + 0010_DirectedAcyclicGraphShortestPathTest.cc ) target_link_libraries(