Skip to content

Commit 1c047e1

Browse files
authored
Added BFS algorithm. Tests on BFS. Added Test and some extreme cases correction in the Dijkstra algorithm. Added the funtion that return the node set from the graph and tested.
Signed-off-by: GitHub <[email protected]>
1 parent 42f3cbe commit 1c047e1

File tree

6 files changed

+342
-37
lines changed

6 files changed

+342
-37
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.9)
22

33
# set the project name and version
4-
project(CXXGraph VERSION 0.0.2)
4+
project(CXXGraph VERSION 0.0.3)
55

66
configure_file(CXXGraphConfig.h.in ${PROJECT_SOURCE_DIR}/include/CXXGraphConfig.h)
77

@@ -29,6 +29,7 @@ add_executable(test_exe test/main.cpp
2929
test/UndirectedWeightedEdgeTest.cpp
3030
test/GraphTest.cpp
3131
test/DijkstraTest.cpp
32+
test/BFSTest.cpp
3233
)
3334
target_include_directories(test_exe PUBLIC
3435
"${PROJECT_SOURCE_DIR}/include"

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,23 @@
1313

1414
## Algorithm Explanation
1515

16+
#### Dijkstra
17+
[Graph Dijkstras Shortest Path Algorithm(Dijkstra's Shortest Path)](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)
18+
**Dijkstra's Algorithm** is used to find the shortest path from a source node to all other reachable nodes in the graph. The algorithm initially assumes all the nodes are unreachable from the given source node so we mark the distances of all nodes as infinity.
19+
(infinity) from source node (INF / infinity denotes unable to reach).
20+
21+
#### BFS (Breadth First Search)
22+
[Breadth First Search Algorithm(Breadth First Search)](https://en.wikipedia.org/wiki/Breadth-first_search)
23+
**Breadth First Search**, also quoted as **BFS**, is a Graph Traversal Algorithm. Time Complexity O(|V| + |E|) where V are the number of vertices and E are the number of edges in the graph.
24+
Applications of Breadth First Search are :
25+
26+
1. Finding shortest path between two vertices say u and v, with path length measured by number of edges (an advantage over depth first search algorithm)
27+
2. Ford-Fulkerson Method for computing the maximum flow in a flow network.
28+
3. Testing bipartiteness of a graph.
29+
4. Cheney's Algorithm, Copying garbage collection.
30+
31+
And there are many more...
32+
1633
WORK IN PROGRESS
1734

1835
## Classes Explanation
@@ -52,3 +69,8 @@ E-Mail : [email protected]
5269
To support me just add ***Star*** the project [![GitHub stars](https://img.shields.io/github/stars/ZigRazor/CXXGraph.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/ZigRazor/CXXGraph/stargazers/) or ***follow me*** [![GitHub followers](https://img.shields.io/github/followers/ZigRazor.svg?style=social&label=Follow&maxAge=2592000)](https://github.com/ZigRazor?tab=followers)
5370

5471
To get updated ***watch*** the project [![GitHub watchers](https://img.shields.io/github/watchers/ZigRazor/CXXGraph.svg?style=social&label=Watch&maxAge=2592000)](https://GitHub.com/ZigRazor/CXXGraph/watchers/)
72+
73+
74+
## Credits
75+
76+
Thanks to the community of [TheAlgorithms](https://github.com/TheAlgorithms) for some algorithm ispiration.

include/Graph.hpp

Lines changed: 115 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace CXXGRAPH
1818
constexpr char ERR_NO_DIR_OR_UNDIR_EDGE[] = "Edge are neither Directed neither Undirected";
1919
constexpr char ERR_NO_WEIGHTED_EDGE[] = "Edge are not Weighted";
2020
constexpr char ERR_DIJ_TARGET_NODE_NOT_REACHABLE[] = "Target Node not Reachable";
21+
constexpr char ERR_DIJ_TARGET_NODE_NOT_IN_GRAPH[] = "Target Node not inside Graph";
22+
constexpr char ERR_DIJ_SOURCE_NODE_NOT_IN_GRAPH[] = "Source Node not inside Graph";
2123
///////////////////////////////
2224
constexpr double INF_DOUBLE = std::numeric_limits<double>::max();
2325
template <typename T>
@@ -38,7 +40,7 @@ namespace CXXGRAPH
3840
class Weighted;
3941

4042
template <typename T>
41-
using AdjacencyMatrix = std::map<const Node<T>*, std::vector<std::pair<const Node<T>*, const Edge<T>*>>>;
43+
using AdjacencyMatrix = std::map<const Node<T> *, std::vector<std::pair<const Node<T> *, const Edge<T> *>>>;
4244

4345
template <typename T>
4446
std::ostream &operator<<(std::ostream &o, const Node<T> &node);
@@ -57,11 +59,12 @@ namespace CXXGRAPH
5759
template <typename T>
5860
std::ostream &operator<<(std::ostream &o, const AdjacencyMatrix<T> &adj);
5961

60-
typedef struct DijkstraResult_struct{
61-
bool success; // TRUE if the function does not return error, FALSE otherwise
62+
typedef struct DijkstraResult_struct
63+
{
64+
bool success; // TRUE if the function does not return error, FALSE otherwise
6265
std::string errorMessage; //message of error
63-
double result; //result (valid only if success is TRUE)
64-
}DijkstraResult;
66+
double result; //result (valid only if success is TRUE)
67+
} DijkstraResult;
6568

6669
template <typename T>
6770
class Node
@@ -456,7 +459,7 @@ namespace CXXGRAPH
456459
{
457460
private:
458461
std::set<const Edge<T> *> edgeSet;
459-
void addElementToAdjMatrix(AdjacencyMatrix<T> &adjMatrix, const Node<T>* nodeFrom, const Node<T>* nodeTo, const Edge<T>* edge) const;
462+
void addElementToAdjMatrix(AdjacencyMatrix<T> &adjMatrix, const Node<T> *nodeFrom, const Node<T> *nodeTo, const Edge<T> *edge) const;
460463

461464
public:
462465
Graph() = default;
@@ -466,6 +469,7 @@ namespace CXXGRAPH
466469
void setEdgeSet(std::set<const Edge<T> *> &edgeSet);
467470
void addEdge(const Edge<T> &edge);
468471
void removeEdge(unsigned long edgeId);
472+
const std::set<const Node<T> *> getNodeSet() const;
469473
const std::optional<const Edge<T> *> getEdge(unsigned long edgeId) const;
470474
/*This function generate a list of adjacency matrix with every element of the matrix
471475
***contain the node where is directed the link and the Edge corrispondent to the link
@@ -483,6 +487,16 @@ namespace CXXGRAPH
483487
* case if target is not reachable from source or there is error in the computation.
484488
*/
485489
const DijkstraResult dijkstra(const Node<T> &source, const Node<T> &target) const;
490+
/**
491+
* \brief
492+
* Function performs the breadth first search algorithm over the graph
493+
*
494+
* @param start Node from where traversing starts
495+
* @returns a vector of Node indicating which Node were visited during the
496+
* search.
497+
*
498+
*/
499+
const std::vector<Node<T>> breadth_first_search(const Node<T> &start) const;
486500

487501
friend std::ostream &operator<<<>(std::ostream &os, const Graph<T> &graph);
488502
friend std::ostream &operator<<<>(std::ostream &os, const AdjacencyMatrix<T> &adj);
@@ -521,6 +535,17 @@ namespace CXXGRAPH
521535
edgeSet.erase(edgeSet.find(edgeOpt.value()));
522536
}
523537
}
538+
539+
template<typename T>
540+
const std::set<const Node<T> *> Graph<T>::getNodeSet() const
541+
{
542+
std::set<const Node<T> *> nodeSet;
543+
for (auto edge : edgeSet){
544+
nodeSet.insert(edge->getNodePair().first);
545+
nodeSet.insert(edge->getNodePair().second);
546+
}
547+
return nodeSet;
548+
}
524549

525550
template <typename T>
526551
const std::optional<const Edge<T> *> Graph<T>::getEdge(unsigned long edgeId) const
@@ -539,9 +564,9 @@ namespace CXXGRAPH
539564
}
540565

541566
template <typename T>
542-
void Graph<T>::addElementToAdjMatrix(AdjacencyMatrix<T> &adjMatrix, const Node<T>* nodeFrom, const Node<T>* nodeTo, const Edge<T>* edge) const
567+
void Graph<T>::addElementToAdjMatrix(AdjacencyMatrix<T> &adjMatrix, const Node<T> *nodeFrom, const Node<T> *nodeTo, const Edge<T> *edge) const
543568
{
544-
std::pair<const Node<T>*, const Edge<T>*> elem = {nodeTo, edge};
569+
std::pair<const Node<T> *, const Edge<T> *> elem = {nodeTo, edge};
545570
adjMatrix[nodeFrom].push_back(elem);
546571

547572
//adjMatrix[nodeFrom.getId()].push_back(std::make_pair<const Node<T>,const Edge<T>>(nodeTo, edge));
@@ -556,12 +581,13 @@ namespace CXXGRAPH
556581
{
557582
if ((*edgeSetIt)->isDirected().has_value() && (*edgeSetIt)->isDirected().value())
558583
{
559-
const DirectedEdge<T>* d_edge = dynamic_cast<const DirectedEdge<T>*>(*edgeSetIt);
584+
const DirectedEdge<T> *d_edge = dynamic_cast<const DirectedEdge<T> *>(*edgeSetIt);
560585
addElementToAdjMatrix(adj, &(d_edge->getFrom()), &(d_edge->getTo()), d_edge);
561586
}
562587
else if ((*edgeSetIt)->isDirected().has_value() && !(*edgeSetIt)->isDirected().value())
563588
{
564-
const UndirectedEdge<T>* ud_edge = dynamic_cast<const UndirectedEdge<T>*>(*edgeSetIt);;
589+
const UndirectedEdge<T> *ud_edge = dynamic_cast<const UndirectedEdge<T> *>(*edgeSetIt);
590+
;
565591
addElementToAdjMatrix(adj, &(ud_edge->getNode1()), &(ud_edge->getNode2()), ud_edge);
566592
addElementToAdjMatrix(adj, &(ud_edge->getNode2()), &(ud_edge->getNode1()), ud_edge);
567593
}
@@ -580,12 +606,21 @@ namespace CXXGRAPH
580606
result.success = false;
581607
result.errorMessage = "";
582608
result.result = INF_DOUBLE;
609+
auto nodeSet = getNodeSet();
610+
if(nodeSet.find(&source) == nodeSet.end()){ // check if source node exist in the graph
611+
result.errorMessage = ERR_DIJ_SOURCE_NODE_NOT_IN_GRAPH;
612+
return result;
613+
}
614+
if(nodeSet.find(&target) == nodeSet.end()){ // check if target node exist in the graph
615+
result.errorMessage = ERR_DIJ_TARGET_NODE_NOT_IN_GRAPH;
616+
return result;
617+
}
583618
const AdjacencyMatrix<T> adj = getAdjMatrix();
584619
/// n denotes the number of vertices in graph
585620
int n = adj.size();
586621

587622
/// setting all the distances initially to INF_DOUBLE
588-
std::map<const Node<T>*, double> dist;
623+
std::map<const Node<T> *, double> dist;
589624

590625
for (auto elem : adj)
591626
{
@@ -595,8 +630,8 @@ namespace CXXGRAPH
595630
/// creating a min heap using priority queue
596631
/// first element of pair contains the distance
597632
/// second element of pair contains the vertex
598-
std::priority_queue<std::pair<double,const Node<T>*>, std::vector<std::pair<double,const Node<T>*>>,
599-
std::greater<std::pair<double,const Node<T>*>>>
633+
std::priority_queue<std::pair<double, const Node<T> *>, std::vector<std::pair<double, const Node<T> *>>,
634+
std::greater<std::pair<double, const Node<T> *>>>
600635
pq;
601636

602637
/// pushing the source vertex 's' with 0 distance in min heap
@@ -608,7 +643,7 @@ namespace CXXGRAPH
608643
while (!pq.empty())
609644
{
610645
/// second element of pair denotes the node / vertex
611-
const Node<T>* currentNode = pq.top().second;
646+
const Node<T> *currentNode = pq.top().second;
612647

613648
/// first element of pair denotes the distance
614649
double currentDist = pq.top().first;
@@ -617,36 +652,44 @@ namespace CXXGRAPH
617652

618653
/// for all the reachable vertex from the currently exploring vertex
619654
/// we will try to minimize the distance
620-
for (std::pair<const Node<T>*, const Edge<T>*> elem : adj.at(currentNode))
655+
if (adj.find(currentNode) != adj.end())
621656
{
622-
/// minimizing distances
623-
if (elem.second->isWeighted().has_value() && elem.second->isWeighted().value())
657+
for (std::pair<const Node<T> *, const Edge<T> *> elem : adj.at(currentNode))
624658
{
625-
if(elem.second->isDirected().has_value() && elem.second->isDirected().value()){
626-
const DirectedWeightedEdge<T> *dw_edge = dynamic_cast<const DirectedWeightedEdge<T> *>(elem.second);
627-
if (currentDist +dw_edge->getWeight() < dist[elem.first])
659+
/// minimizing distances
660+
if (elem.second->isWeighted().has_value() && elem.second->isWeighted().value())
661+
{
662+
if (elem.second->isDirected().has_value() && elem.second->isDirected().value())
628663
{
629-
dist[elem.first] = currentDist + dw_edge->getWeight();
630-
pq.push(std::make_pair(dist[elem.first], elem.first));
664+
const DirectedWeightedEdge<T> *dw_edge = dynamic_cast<const DirectedWeightedEdge<T> *>(elem.second);
665+
if (currentDist + dw_edge->getWeight() < dist[elem.first])
666+
{
667+
dist[elem.first] = currentDist + dw_edge->getWeight();
668+
pq.push(std::make_pair(dist[elem.first], elem.first));
669+
}
631670
}
632-
}else if (elem.second->isDirected().has_value() && !elem.second->isDirected().value()){
633-
const UndirectedWeightedEdge<T> *udw_edge = dynamic_cast<const UndirectedWeightedEdge<T> *>(elem.second);
634-
if (currentDist + udw_edge->getWeight() < dist[elem.first])
671+
else if (elem.second->isDirected().has_value() && !elem.second->isDirected().value())
635672
{
636-
dist[elem.first] = currentDist + udw_edge->getWeight();
637-
pq.push(std::make_pair(dist[elem.first], elem.first));
673+
const UndirectedWeightedEdge<T> *udw_edge = dynamic_cast<const UndirectedWeightedEdge<T> *>(elem.second);
674+
if (currentDist + udw_edge->getWeight() < dist[elem.first])
675+
{
676+
dist[elem.first] = currentDist + udw_edge->getWeight();
677+
pq.push(std::make_pair(dist[elem.first], elem.first));
678+
}
638679
}
639-
}else{
640-
//ERROR it shouldn't never returned ( does not exist a Node Weighted and not Directed/Undirected)
641-
result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE;
680+
else
681+
{
682+
//ERROR it shouldn't never returned ( does not exist a Node Weighted and not Directed/Undirected)
683+
result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE;
684+
return result;
685+
}
686+
}
687+
else
688+
{ // No Weighted Edge
689+
result.errorMessage = ERR_NO_WEIGHTED_EDGE;
642690
return result;
643691
}
644692
}
645-
else
646-
{ // No Weighted Edge
647-
result.errorMessage = ERR_NO_WEIGHTED_EDGE;
648-
return result;
649-
}
650693
}
651694
}
652695
if (dist[&target] != INF_DOUBLE)
@@ -660,7 +703,43 @@ namespace CXXGRAPH
660703
result.result = -1;
661704
return result;
662705
}
663-
706+
707+
template <typename T>
708+
const std::vector<Node<T>> Graph<T>::breadth_first_search(const Node<T> &start) const
709+
{
710+
const AdjacencyMatrix<T> adj = getAdjMatrix();
711+
/// vector to keep track of visited nodes
712+
std::vector<Node<T>> visited;
713+
/// queue that stores vertices that need to be further explored
714+
std::queue<const Node<T> *> tracker;
715+
auto nodeSet = getNodeSet();
716+
if (nodeSet.find(&start) != nodeSet.end()) //check is exist node in the graph
717+
{
718+
/// mark the starting node as visited
719+
visited.push_back(start);
720+
tracker.push(&start);
721+
while (!tracker.empty())
722+
{
723+
const Node<T> *node = tracker.front();
724+
tracker.pop();
725+
if (adj.find(node) != adj.end())
726+
{
727+
for (auto elem : adj.at(node))
728+
{
729+
/// if the node is not visited then mark it as visited
730+
/// and push it to the queue
731+
if (std::find(visited.begin(), visited.end(), *(elem.first)) == visited.end())
732+
{
733+
visited.push_back(*(elem.first));
734+
tracker.push(elem.first);
735+
}
736+
}
737+
}
738+
}
739+
}
740+
return visited;
741+
}
742+
664743
//ostream overload
665744
template <typename T>
666745
std::ostream &operator<<(std::ostream &os, const Node<T> &node)

0 commit comments

Comments
 (0)