From 7adb0d8d5e534324f9f713b3f0dd19823b6ed768 Mon Sep 17 00:00:00 2001 From: Ayaan Khan Date: Thu, 16 Jul 2020 21:04:29 +0530 Subject: [PATCH 01/90] improved connected components --- graph/connected_components.cpp | 174 ++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 77 deletions(-) diff --git a/graph/connected_components.cpp b/graph/connected_components.cpp index e53dbf424..8b2ce9944 100644 --- a/graph/connected_components.cpp +++ b/graph/connected_components.cpp @@ -15,9 +15,9 @@ *
  * Example - Here is graph with 3 connected components
  *
- *      3   9           6               8
+ *      1   4           5               8
  *     / \ /           / \             / \
- *    2---4           2   7           3   7
+ *    2---3           6   7           9   10
  *
  *    first          second           third
  *    component      component        component
@@ -28,95 +28,115 @@
 #include 
 #include 
 #include 
-
-using std::vector;
+#include 
 
 /**
- * Class for representing graph as a adjacency list.
+ * @namespace graph
+ * @brief Graph Algorithms
  */
-class graph {
- private:
-    /** \brief adj stores adjacency list representation of graph */
-    vector> adj;
 
-    /** \brief keep track of connected components */
-    int connected_components;
+namespace graph {
+  /**
+   * @brief Function that add edge between two nodes or vertices of graph
+   *
+   * @param u any node or vertex of graph
+   * @param v any node or vertex of graph
+   */
+  void addEdge(std::vector> *adj, int u, int v) {
+    (*adj)[u - 1].push_back(v - 1);
+    (*adj)[v - 1].push_back(u - 1);
+  }
 
-    void depth_first_search();
-    void explore(int, vector &);
-
- public:
-    /**
-     * \brief Constructor that intiliazes the graph on creation and set
-     * the connected components to 0
-     */
-    explicit graph(int n) : adj(n, vector()) { connected_components = 0; }
-
-    void addEdge(int, int);
-
-    /**
-     * \brief Function the calculates the connected compoents in the graph
-     * by performing the depth first search on graph
-     *
-     * @return connected_components total connected components in graph
-     */
-    int getConnectedComponents() {
-        depth_first_search();
-        return connected_components;
+  /**
+   * @brief Utility function for depth first seach algorithm
+   * this function explores the vertex which is passed into.
+   *
+   * @param u vertex or node to be explored
+   * @param visited already visited vertices
+   */
+  void explore(const std::vector> *adj, int u,
+      std::vector &visited) {
+    visited[u] = true;
+    for (auto v : (*adj)[u]) {
+      if (!visited[v]) {
+        explore(adj, v, visited);
+      }
     }
-};
+  }
 
-/**
- * \brief Function that add edge between two nodes or vertices of graph
- *
- * @param u any node or vertex of graph
- * @param v any node or vertex of graph
- */
-void graph::addEdge(int u, int v) {
-    adj[u - 1].push_back(v - 1);
-    adj[v - 1].push_back(u - 1);
-}
-
-/**
- * \brief Function that perfoms depth first search algorithm on graph
- */
-void graph::depth_first_search() {
-    int n = adj.size();
-    vector visited(n, false);
+  /**
+   * @brief Function that perfoms depth first search algorithm on graph
+   * and calculated the number of connected components.
+   */
+  int getConnectedComponents(const std::vector> *adj) {
+    int n = adj->size();
+    int connected_components = 0;
+    std::vector visited(n, false);
 
     for (int i = 0; i < n; i++) {
-        if (!visited[i]) {
-            explore(i, visited);
-            connected_components++;
-        }
-    }
-}
-/**
- * \brief Utility function for depth first seach algorithm
- * this function explores the vertex which is passed into.
- *
- * @param u vertex or node to be explored
- * @param visited already visited vertex
- */
-void graph::explore(int u, vector &visited) {
-    visited[u] = true;
-    for (auto v : adj[u]) {
-        if (!visited[v]) {
-            explore(v, visited);
-        }
+      if (!visited[i]) {
+        explore(adj, i, visited);
+        connected_components++;
+      }
     }
+    return connected_components;
+  }
+} // namespace graph
+
+/** Function to test the algorithm */
+void tests() {
+  std::cout << "Running predefined tests..." << std::endl;
+  std::cout << "Initiating Test 1..." << std::endl;
+  std::vector> adj1(9, std::vector());
+  graph::addEdge(&adj1, 1, 2); 
+  graph::addEdge(&adj1, 1, 3); 
+  graph::addEdge(&adj1, 3, 4); 
+  graph::addEdge(&adj1, 5, 7); 
+  graph::addEdge(&adj1, 5, 6); 
+  graph::addEdge(&adj1, 8, 9);
+  
+  assert(graph::getConnectedComponents(&adj1) == 3);
+  std::cout << "Test 1 Passed..." << std::endl;
+
+  std::cout << "Innitiating Test 2..." << std::endl;
+  std::vector> adj2(10, std::vector());
+  graph::addEdge(&adj2, 1, 2);
+  graph::addEdge(&adj2, 1, 3);
+  graph::addEdge(&adj2, 1, 4);
+  graph::addEdge(&adj2, 2, 3);
+  graph::addEdge(&adj2, 3, 4);
+  graph::addEdge(&adj2, 4, 8);
+  graph::addEdge(&adj2, 4, 10);
+  graph::addEdge(&adj2, 8, 10);
+  graph::addEdge(&adj2, 8, 9);
+  graph::addEdge(&adj2, 5, 7);
+  graph::addEdge(&adj2, 5, 6);
+  graph::addEdge(&adj2, 6, 7);
+
+  assert(graph::getConnectedComponents(&adj2) == 2);
+  std::cout << "Test 2 Passed..." << std::endl;
 }
 
 /** Main function */
 int main() {
-    /// creating a graph with 4 vertex
-    graph g(4);
+  /// running predefined tests
+  tests();
+  
+  int vertices, edges;
+  std::cout << "Enter the number of vertices : ";
+  std::cin >> vertices;
+  std::cout << "Enter the number of edges : ";
+  std::cin >> edges;
 
-    /// Adding edges between vertices
-    g.addEdge(1, 2);
-    g.addEdge(3, 2);
+  std::vector> adj(vertices, std::vector());
 
-    /// printing the connected components
-    std::cout << g.getConnectedComponents();
-    return 0;
+  while (edges--) {
+    int u, v;
+    std::cin >> u >> v;
+    graph::addEdge(&adj, u, v);
+  }
+
+  int cc = graph::getConnectedComponents(&adj);
+  std::cout << cc << std::endl;
+  return 0;
 }

From 117c8362bea8568ebd27bc52bc980461e06a560a Mon Sep 17 00:00:00 2001
From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Date: Thu, 16 Jul 2020 15:35:37 +0000
Subject: [PATCH 02/90] formatting source-code for
 7adb0d8d5e534324f9f713b3f0dd19823b6ed768

---
 graph/connected_components.cpp | 156 ++++++++++++++++-----------------
 1 file changed, 78 insertions(+), 78 deletions(-)

diff --git a/graph/connected_components.cpp b/graph/connected_components.cpp
index 8b2ce9944..bfb92c7c8 100644
--- a/graph/connected_components.cpp
+++ b/graph/connected_components.cpp
@@ -26,9 +26,9 @@
  */
 
 #include 
+#include 
 #include 
 #include 
-#include 
 
 /**
  * @namespace graph
@@ -36,107 +36,107 @@
  */
 
 namespace graph {
-  /**
-   * @brief Function that add edge between two nodes or vertices of graph
-   *
-   * @param u any node or vertex of graph
-   * @param v any node or vertex of graph
-   */
-  void addEdge(std::vector> *adj, int u, int v) {
+/**
+ * @brief Function that add edge between two nodes or vertices of graph
+ *
+ * @param u any node or vertex of graph
+ * @param v any node or vertex of graph
+ */
+void addEdge(std::vector> *adj, int u, int v) {
     (*adj)[u - 1].push_back(v - 1);
     (*adj)[v - 1].push_back(u - 1);
-  }
+}
 
-  /**
-   * @brief Utility function for depth first seach algorithm
-   * this function explores the vertex which is passed into.
-   *
-   * @param u vertex or node to be explored
-   * @param visited already visited vertices
-   */
-  void explore(const std::vector> *adj, int u,
-      std::vector &visited) {
+/**
+ * @brief Utility function for depth first seach algorithm
+ * this function explores the vertex which is passed into.
+ *
+ * @param u vertex or node to be explored
+ * @param visited already visited vertices
+ */
+void explore(const std::vector> *adj, int u,
+             std::vector &visited) {
     visited[u] = true;
     for (auto v : (*adj)[u]) {
-      if (!visited[v]) {
-        explore(adj, v, visited);
-      }
+        if (!visited[v]) {
+            explore(adj, v, visited);
+        }
     }
-  }
+}
 
-  /**
-   * @brief Function that perfoms depth first search algorithm on graph
-   * and calculated the number of connected components.
-   */
-  int getConnectedComponents(const std::vector> *adj) {
+/**
+ * @brief Function that perfoms depth first search algorithm on graph
+ * and calculated the number of connected components.
+ */
+int getConnectedComponents(const std::vector> *adj) {
     int n = adj->size();
     int connected_components = 0;
     std::vector visited(n, false);
 
     for (int i = 0; i < n; i++) {
-      if (!visited[i]) {
-        explore(adj, i, visited);
-        connected_components++;
-      }
+        if (!visited[i]) {
+            explore(adj, i, visited);
+            connected_components++;
+        }
     }
     return connected_components;
-  }
-} // namespace graph
+}
+}  // namespace graph
 
 /** Function to test the algorithm */
 void tests() {
-  std::cout << "Running predefined tests..." << std::endl;
-  std::cout << "Initiating Test 1..." << std::endl;
-  std::vector> adj1(9, std::vector());
-  graph::addEdge(&adj1, 1, 2); 
-  graph::addEdge(&adj1, 1, 3); 
-  graph::addEdge(&adj1, 3, 4); 
-  graph::addEdge(&adj1, 5, 7); 
-  graph::addEdge(&adj1, 5, 6); 
-  graph::addEdge(&adj1, 8, 9);
-  
-  assert(graph::getConnectedComponents(&adj1) == 3);
-  std::cout << "Test 1 Passed..." << std::endl;
+    std::cout << "Running predefined tests..." << std::endl;
+    std::cout << "Initiating Test 1..." << std::endl;
+    std::vector> adj1(9, std::vector());
+    graph::addEdge(&adj1, 1, 2);
+    graph::addEdge(&adj1, 1, 3);
+    graph::addEdge(&adj1, 3, 4);
+    graph::addEdge(&adj1, 5, 7);
+    graph::addEdge(&adj1, 5, 6);
+    graph::addEdge(&adj1, 8, 9);
 
-  std::cout << "Innitiating Test 2..." << std::endl;
-  std::vector> adj2(10, std::vector());
-  graph::addEdge(&adj2, 1, 2);
-  graph::addEdge(&adj2, 1, 3);
-  graph::addEdge(&adj2, 1, 4);
-  graph::addEdge(&adj2, 2, 3);
-  graph::addEdge(&adj2, 3, 4);
-  graph::addEdge(&adj2, 4, 8);
-  graph::addEdge(&adj2, 4, 10);
-  graph::addEdge(&adj2, 8, 10);
-  graph::addEdge(&adj2, 8, 9);
-  graph::addEdge(&adj2, 5, 7);
-  graph::addEdge(&adj2, 5, 6);
-  graph::addEdge(&adj2, 6, 7);
+    assert(graph::getConnectedComponents(&adj1) == 3);
+    std::cout << "Test 1 Passed..." << std::endl;
 
-  assert(graph::getConnectedComponents(&adj2) == 2);
-  std::cout << "Test 2 Passed..." << std::endl;
+    std::cout << "Innitiating Test 2..." << std::endl;
+    std::vector> adj2(10, std::vector());
+    graph::addEdge(&adj2, 1, 2);
+    graph::addEdge(&adj2, 1, 3);
+    graph::addEdge(&adj2, 1, 4);
+    graph::addEdge(&adj2, 2, 3);
+    graph::addEdge(&adj2, 3, 4);
+    graph::addEdge(&adj2, 4, 8);
+    graph::addEdge(&adj2, 4, 10);
+    graph::addEdge(&adj2, 8, 10);
+    graph::addEdge(&adj2, 8, 9);
+    graph::addEdge(&adj2, 5, 7);
+    graph::addEdge(&adj2, 5, 6);
+    graph::addEdge(&adj2, 6, 7);
+
+    assert(graph::getConnectedComponents(&adj2) == 2);
+    std::cout << "Test 2 Passed..." << std::endl;
 }
 
 /** Main function */
 int main() {
-  /// running predefined tests
-  tests();
-  
-  int vertices, edges;
-  std::cout << "Enter the number of vertices : ";
-  std::cin >> vertices;
-  std::cout << "Enter the number of edges : ";
-  std::cin >> edges;
+    /// running predefined tests
+    tests();
 
-  std::vector> adj(vertices, std::vector());
+    int vertices, edges;
+    std::cout << "Enter the number of vertices : ";
+    std::cin >> vertices;
+    std::cout << "Enter the number of edges : ";
+    std::cin >> edges;
 
-  while (edges--) {
-    int u, v;
-    std::cin >> u >> v;
-    graph::addEdge(&adj, u, v);
-  }
+    std::vector> adj(vertices, std::vector());
 
-  int cc = graph::getConnectedComponents(&adj);
-  std::cout << cc << std::endl;
-  return 0;
+    while (edges--) {
+        int u, v;
+        std::cin >> u >> v;
+        graph::addEdge(&adj, u, v);
+    }
+
+    int cc = graph::getConnectedComponents(&adj);
+    std::cout << cc << std::endl;
+    return 0;
 }

From f5a35eb8cd8d650a5c1c9a6e50fcdf818055d07b Mon Sep 17 00:00:00 2001
From: Ayaan Khan 
Date: Fri, 17 Jul 2020 19:52:16 +0530
Subject: [PATCH 03/90] fix: dijkstra.cpp

---
 graph/connected_components.cpp |  24 ++--
 graph/dijkstra.cpp             | 219 ++++++++++++++++++++++++++-------
 2 files changed, 184 insertions(+), 59 deletions(-)

diff --git a/graph/connected_components.cpp b/graph/connected_components.cpp
index 8b2ce9944..55bdccbc7 100644
--- a/graph/connected_components.cpp
+++ b/graph/connected_components.cpp
@@ -55,10 +55,10 @@ namespace graph {
    * @param visited already visited vertices
    */
   void explore(const std::vector> *adj, int u,
-      std::vector &visited) {
-    visited[u] = true;
+      std::vector *visited) {
+    (*visited)[u] = true;
     for (auto v : (*adj)[u]) {
-      if (!visited[v]) {
+      if (!(*visited)[v]) {
         explore(adj, v, visited);
       }
     }
@@ -75,26 +75,26 @@ namespace graph {
 
     for (int i = 0; i < n; i++) {
       if (!visited[i]) {
-        explore(adj, i, visited);
+        explore(adj, i, &visited);
         connected_components++;
       }
     }
     return connected_components;
   }
-} // namespace graph
+}  // namespace graph
 
 /** Function to test the algorithm */
 void tests() {
   std::cout << "Running predefined tests..." << std::endl;
   std::cout << "Initiating Test 1..." << std::endl;
   std::vector> adj1(9, std::vector());
-  graph::addEdge(&adj1, 1, 2); 
-  graph::addEdge(&adj1, 1, 3); 
-  graph::addEdge(&adj1, 3, 4); 
-  graph::addEdge(&adj1, 5, 7); 
-  graph::addEdge(&adj1, 5, 6); 
+  graph::addEdge(&adj1, 1, 2);
+  graph::addEdge(&adj1, 1, 3);
+  graph::addEdge(&adj1, 3, 4);
+  graph::addEdge(&adj1, 5, 7);
+  graph::addEdge(&adj1, 5, 6);
   graph::addEdge(&adj1, 8, 9);
-  
+
   assert(graph::getConnectedComponents(&adj1) == 3);
   std::cout << "Test 1 Passed..." << std::endl;
 
@@ -121,7 +121,7 @@ void tests() {
 int main() {
   /// running predefined tests
   tests();
-  
+
   int vertices, edges;
   std::cout << "Enter the number of vertices : ";
   std::cin >> vertices;
diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp
index 650f0cd51..357ecf843 100644
--- a/graph/dijkstra.cpp
+++ b/graph/dijkstra.cpp
@@ -1,52 +1,177 @@
-#include 
+/**
+ * @file
+ * @brief [Graph Dijkstras Shortest Path Algorithm
+ * (Dijkstra's Shortest Path)]
+ * (https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)
+ *
+ * @author [Ayaan Khan](http://github.com/ayaankhan98)
+ *
+ * @details
+ * Dijkstra's Algorithm is used to find the shortest path from a source
+ * vertex to all other reachable vertex in the graph.
+ * The algorithm initially assumes all the nodes are unreachable from the
+ * given source vertex so we mark the distances of all vertices as INF
+ * (infinity) from source vertex (INF / infinity denotes unable to reach).
+ *
+ * in similar fashion with BFS we assume the distance of source vertex as 0
+ * and pushes the vertex in a priority queue with it's distance.
+ * we maintain the priority queue as a min heap so that we can get the
+ * minimum element at the top of heap
+ *
+ * Basically what we do in this algorithm is that we try to minimize the
+ * distances of all the reachable vertices from the current vertex, look
+ * at the code below to understand in better way.
+ *
+ */
+#include 
 #include 
 #include 
+#include 
 #include 
-using namespace std;
-#define INF 10000010
-vector> graph[5 * 100001];
-int dis[5 * 100001];
-int dij(vector> *v, int s, int *dis) {
-    priority_queue, vector>,
-                   greater>>
-        pq;
-    // source distance to zero.
-    pq.push(make_pair(0, s));
-    dis[s] = 0;
-    int u;
-    while (!pq.empty()) {
-        u = (pq.top()).second;
-        pq.pop();
-        for (vector>::iterator it = v[u].begin();
-             it != v[u].end(); it++) {
-            if (dis[u] + it->first < dis[it->second]) {
-                dis[it->second] = dis[u] + it->first;
-                pq.push(make_pair(dis[it->second], it->second));
-            }
-        }
-    }
-}
-int main() {
-    int m, n, l, x, y, s;
-    // n--> number of nodes , m --> number of edges
-    cin >> n >> m;
-    for (int i = 0; i < m; i++) {
-        // input edges.
-        scanf("%d%d%d", &x, &y, &l);
-        graph[x].push_back(make_pair(l, y));
-        graph[y].push_back(
-            make_pair(l, x));  // comment this line for directed graph
-    }
-    // start node.
-    scanf("%d", &s);
-    // intialise all distances to infinity.
-    for (int i = 1; i <= n; i++) dis[i] = INF;
-    dij(graph, s, dis);
 
-    for (int i = 1; i <= n; i++)
-        if (dis[i] == INF)
-            cout << "-1 ";
-        else
-            cout << dis[i] << " ";
-    return 0;
+constexpr long long INF = 1000000000;
+
+/**
+ * @namespace graph
+ * @brief Graph Algorithms
+ */
+
+namespace graph {
+  /**
+   * @brief Function that add edge between two nodes or vertices of graph
+   *
+   * @param u any node or vertex of graph
+   * @param v any node or vertex of graph
+   */
+  void addEdge(std::vector>> *adj, int u, int v,
+      int w) {
+    (*adj)[u - 1].push_back(std::make_pair(v - 1, w));
+    // (*adj)[v - 1].push_back(std::make_pair(u - 1, w));
+  }
+  
+  /**
+   * @brief Function runs the dijkstra algorithm for some source vertex and
+   * target vertex in the graph and returns the shortest distance of target
+   * from the source.
+   *
+   * @param adj input graph
+   * @param s source vertex
+   * @param t target vertex
+   * 
+   * @return shortest distance if target is reachable from source else -1 in
+   * case if target is not reachable from source.
+   */
+  int dijkstra(std::vector>> *adj, int s, int t) {
+    /// n denotes the number of vertices in graph
+    int n = adj->size();
+
+    /// setting all the distances initially to INF
+    std::vector dist(n, INF);
+
+    /// creating a min heap using priority queue
+    /// first element of pair contains the distance
+    /// second element of pair contains the vertex
+    std::priority_queue, std::vector>,
+      std::greater>>
+        pq;
+    
+    /// pushing the source vertex 's' with 0 distance in min heap
+    pq.push(std::make_pair(0, s));
+
+    /// marking the distance of source as 0
+    dist[s] = 0;
+
+    while (!pq.empty()) {
+      /// second element of pair denotes the node / vertex
+      int currentNode = pq.top().second;
+
+      /// first element of pair denotes the distance
+      int currentDist = pq.top().first;
+
+      pq.pop();
+
+      /// for all the reachable vertex from the currently exploring vertex
+      /// we will try to minimize the distance
+      for (std::pair edge : (*adj)[currentNode]) {
+
+        /// minimizing distances
+        if (currentDist + edge.second < dist[edge.first]) {
+          dist[edge.first] = currentDist + edge.second;
+          pq.push(std::make_pair(dist[edge.first], edge.first));
+        }
+      }
+    }
+    if (dist[t] != INF) {
+      return dist[t];
+    }
+    return -1;
+  }
+} // namespace graph
+
+/** Function to test the Algorithm */
+void tests() {
+  std::cout << "Initiatinig Predefined Tests..." << std::endl;
+  std::cout << "Initiating Test 1..." << std::endl;
+  std::vector>> adj1(4, std::vector>());
+  graph::addEdge(&adj1, 1, 2, 1);
+  graph::addEdge(&adj1, 4, 1, 2);
+  graph::addEdge(&adj1, 2, 3, 2);
+  graph::addEdge(&adj1, 1, 3, 5);
+
+  int s = 1, t = 3;
+  assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3);
+  std::cout << "Test 1 Passed..." << std::endl;
+
+  s = 4, t = 3;
+  std::cout << "Initiating Test 2..." << std::endl;
+  assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5);
+  std::cout << "Test 2 Passed..." << std::endl;
+
+  std::vector>> adj2(5, std::vector>());
+  graph::addEdge(&adj2, 1, 2, 4);
+  graph::addEdge(&adj2, 1, 3, 2);
+  graph::addEdge(&adj2, 2, 3, 2);
+  graph::addEdge(&adj2, 3, 2, 1);
+  graph::addEdge(&adj2, 2, 4, 2);
+  graph::addEdge(&adj2, 3, 5, 4);
+  graph::addEdge(&adj2, 5, 4, 1);
+  graph::addEdge(&adj2, 2, 5, 3);
+  graph::addEdge(&adj2, 3, 4, 4);
+
+  s = 1, t = 5;
+  std::cout << "Initiating Test 3..." << std::endl;
+  assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6);
+  std::cout << "Test 3 Passed..." << std::endl;
+  std::cout << "All Test Passed..." << std::endl << std::endl;
+}
+
+/** Main function */
+int main(){
+  // running predefined tests
+  tests();
+
+  int vertices, edges;
+  std::cout << "Enter the number of vertices : ";
+  std::cin >> vertices;
+  std::cout << "Enter the number of edges : ";
+  std::cin >> edges;
+
+  std::vector>> adj(
+      vertices, std::vector>());
+
+  int u, v, w;
+  while (edges--) {
+    std::cin >> u >> v >> w;
+    graph::addEdge(&adj, u, v, w);
+  }
+
+  int s, t;
+  std::cin >> s >> t;
+  int dist = graph::dijkstra(&adj, s-1, t-1);
+  if (dist == -1) {
+    std::cout << "Target not reachable from source" << std::endl;
+  } else {
+    std::cout << "Shortest Path Distance : " << dist << std::endl;
+  }
+  return 0;
 }

From 541d3bfbcff70bbb81aad2b0080d8afd426e0da9 Mon Sep 17 00:00:00 2001
From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Date: Fri, 17 Jul 2020 14:27:01 +0000
Subject: [PATCH 04/90] formatting source-code for
 56556a81fbb1ff545f80cdf204e4bdfc3223d55c

---
 graph/dijkstra.cpp | 191 +++++++++++++++++++++++----------------------
 1 file changed, 96 insertions(+), 95 deletions(-)

diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp
index 357ecf843..3dca9e64a 100644
--- a/graph/dijkstra.cpp
+++ b/graph/dijkstra.cpp
@@ -37,31 +37,31 @@ constexpr long long INF = 1000000000;
  */
 
 namespace graph {
-  /**
-   * @brief Function that add edge between two nodes or vertices of graph
-   *
-   * @param u any node or vertex of graph
-   * @param v any node or vertex of graph
-   */
-  void addEdge(std::vector>> *adj, int u, int v,
-      int w) {
+/**
+ * @brief Function that add edge between two nodes or vertices of graph
+ *
+ * @param u any node or vertex of graph
+ * @param v any node or vertex of graph
+ */
+void addEdge(std::vector>> *adj, int u, int v,
+             int w) {
     (*adj)[u - 1].push_back(std::make_pair(v - 1, w));
     // (*adj)[v - 1].push_back(std::make_pair(u - 1, w));
-  }
-  
-  /**
-   * @brief Function runs the dijkstra algorithm for some source vertex and
-   * target vertex in the graph and returns the shortest distance of target
-   * from the source.
-   *
-   * @param adj input graph
-   * @param s source vertex
-   * @param t target vertex
-   * 
-   * @return shortest distance if target is reachable from source else -1 in
-   * case if target is not reachable from source.
-   */
-  int dijkstra(std::vector>> *adj, int s, int t) {
+}
+
+/**
+ * @brief Function runs the dijkstra algorithm for some source vertex and
+ * target vertex in the graph and returns the shortest distance of target
+ * from the source.
+ *
+ * @param adj input graph
+ * @param s source vertex
+ * @param t target vertex
+ *
+ * @return shortest distance if target is reachable from source else -1 in
+ * case if target is not reachable from source.
+ */
+int dijkstra(std::vector>> *adj, int s, int t) {
     /// n denotes the number of vertices in graph
     int n = adj->size();
 
@@ -72,9 +72,9 @@ namespace graph {
     /// first element of pair contains the distance
     /// second element of pair contains the vertex
     std::priority_queue, std::vector>,
-      std::greater>>
+                        std::greater>>
         pq;
-    
+
     /// pushing the source vertex 's' with 0 distance in min heap
     pq.push(std::make_pair(0, s));
 
@@ -82,96 +82,97 @@ namespace graph {
     dist[s] = 0;
 
     while (!pq.empty()) {
-      /// second element of pair denotes the node / vertex
-      int currentNode = pq.top().second;
+        /// second element of pair denotes the node / vertex
+        int currentNode = pq.top().second;
 
-      /// first element of pair denotes the distance
-      int currentDist = pq.top().first;
+        /// first element of pair denotes the distance
+        int currentDist = pq.top().first;
 
-      pq.pop();
+        pq.pop();
 
-      /// for all the reachable vertex from the currently exploring vertex
-      /// we will try to minimize the distance
-      for (std::pair edge : (*adj)[currentNode]) {
-
-        /// minimizing distances
-        if (currentDist + edge.second < dist[edge.first]) {
-          dist[edge.first] = currentDist + edge.second;
-          pq.push(std::make_pair(dist[edge.first], edge.first));
+        /// for all the reachable vertex from the currently exploring vertex
+        /// we will try to minimize the distance
+        for (std::pair edge : (*adj)[currentNode]) {
+            /// minimizing distances
+            if (currentDist + edge.second < dist[edge.first]) {
+                dist[edge.first] = currentDist + edge.second;
+                pq.push(std::make_pair(dist[edge.first], edge.first));
+            }
         }
-      }
     }
     if (dist[t] != INF) {
-      return dist[t];
+        return dist[t];
     }
     return -1;
-  }
-} // namespace graph
+}
+}  // namespace graph
 
 /** Function to test the Algorithm */
 void tests() {
-  std::cout << "Initiatinig Predefined Tests..." << std::endl;
-  std::cout << "Initiating Test 1..." << std::endl;
-  std::vector>> adj1(4, std::vector>());
-  graph::addEdge(&adj1, 1, 2, 1);
-  graph::addEdge(&adj1, 4, 1, 2);
-  graph::addEdge(&adj1, 2, 3, 2);
-  graph::addEdge(&adj1, 1, 3, 5);
+    std::cout << "Initiatinig Predefined Tests..." << std::endl;
+    std::cout << "Initiating Test 1..." << std::endl;
+    std::vector>> adj1(
+        4, std::vector>());
+    graph::addEdge(&adj1, 1, 2, 1);
+    graph::addEdge(&adj1, 4, 1, 2);
+    graph::addEdge(&adj1, 2, 3, 2);
+    graph::addEdge(&adj1, 1, 3, 5);
 
-  int s = 1, t = 3;
-  assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3);
-  std::cout << "Test 1 Passed..." << std::endl;
+    int s = 1, t = 3;
+    assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3);
+    std::cout << "Test 1 Passed..." << std::endl;
 
-  s = 4, t = 3;
-  std::cout << "Initiating Test 2..." << std::endl;
-  assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5);
-  std::cout << "Test 2 Passed..." << std::endl;
+    s = 4, t = 3;
+    std::cout << "Initiating Test 2..." << std::endl;
+    assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5);
+    std::cout << "Test 2 Passed..." << std::endl;
 
-  std::vector>> adj2(5, std::vector>());
-  graph::addEdge(&adj2, 1, 2, 4);
-  graph::addEdge(&adj2, 1, 3, 2);
-  graph::addEdge(&adj2, 2, 3, 2);
-  graph::addEdge(&adj2, 3, 2, 1);
-  graph::addEdge(&adj2, 2, 4, 2);
-  graph::addEdge(&adj2, 3, 5, 4);
-  graph::addEdge(&adj2, 5, 4, 1);
-  graph::addEdge(&adj2, 2, 5, 3);
-  graph::addEdge(&adj2, 3, 4, 4);
+    std::vector>> adj2(
+        5, std::vector>());
+    graph::addEdge(&adj2, 1, 2, 4);
+    graph::addEdge(&adj2, 1, 3, 2);
+    graph::addEdge(&adj2, 2, 3, 2);
+    graph::addEdge(&adj2, 3, 2, 1);
+    graph::addEdge(&adj2, 2, 4, 2);
+    graph::addEdge(&adj2, 3, 5, 4);
+    graph::addEdge(&adj2, 5, 4, 1);
+    graph::addEdge(&adj2, 2, 5, 3);
+    graph::addEdge(&adj2, 3, 4, 4);
 
-  s = 1, t = 5;
-  std::cout << "Initiating Test 3..." << std::endl;
-  assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6);
-  std::cout << "Test 3 Passed..." << std::endl;
-  std::cout << "All Test Passed..." << std::endl << std::endl;
+    s = 1, t = 5;
+    std::cout << "Initiating Test 3..." << std::endl;
+    assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6);
+    std::cout << "Test 3 Passed..." << std::endl;
+    std::cout << "All Test Passed..." << std::endl << std::endl;
 }
 
 /** Main function */
-int main(){
-  // running predefined tests
-  tests();
+int main() {
+    // running predefined tests
+    tests();
 
-  int vertices, edges;
-  std::cout << "Enter the number of vertices : ";
-  std::cin >> vertices;
-  std::cout << "Enter the number of edges : ";
-  std::cin >> edges;
+    int vertices, edges;
+    std::cout << "Enter the number of vertices : ";
+    std::cin >> vertices;
+    std::cout << "Enter the number of edges : ";
+    std::cin >> edges;
 
-  std::vector>> adj(
-      vertices, std::vector>());
+    std::vector>> adj(
+        vertices, std::vector>());
 
-  int u, v, w;
-  while (edges--) {
-    std::cin >> u >> v >> w;
-    graph::addEdge(&adj, u, v, w);
-  }
+    int u, v, w;
+    while (edges--) {
+        std::cin >> u >> v >> w;
+        graph::addEdge(&adj, u, v, w);
+    }
 
-  int s, t;
-  std::cin >> s >> t;
-  int dist = graph::dijkstra(&adj, s-1, t-1);
-  if (dist == -1) {
-    std::cout << "Target not reachable from source" << std::endl;
-  } else {
-    std::cout << "Shortest Path Distance : " << dist << std::endl;
-  }
-  return 0;
+    int s, t;
+    std::cin >> s >> t;
+    int dist = graph::dijkstra(&adj, s - 1, t - 1);
+    if (dist == -1) {
+        std::cout << "Target not reachable from source" << std::endl;
+    } else {
+        std::cout << "Shortest Path Distance : " << dist << std::endl;
+    }
+    return 0;
 }

From 54fd71569f935df6ad53a74a94745e3d01ec2611 Mon Sep 17 00:00:00 2001
From: Ayaan Khan 
Date: Fri, 17 Jul 2020 20:32:32 +0530
Subject: [PATCH 05/90] Added CMakeLists.txt

---
 CMakeLists.txt       |  2 +-
 graph/CMakeLists.txt | 20 ++++++++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)
 create mode 100644 graph/CMakeLists.txt

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 712c3db42..8f860eb11 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -76,7 +76,7 @@ add_subdirectory(probability)
 add_subdirectory(data_structures)
 add_subdirectory(machine_learning)
 add_subdirectory(numerical_methods)
-
+add_subdirectory(graph)
 set(CPACK_PROJECT_NAME ${PROJECT_NAME})
 set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
 include(CPack)
diff --git a/graph/CMakeLists.txt b/graph/CMakeLists.txt
new file mode 100644
index 000000000..e00516075
--- /dev/null
+++ b/graph/CMakeLists.txt
@@ -0,0 +1,20 @@
+# If necessary, use the RELATIVE flag, otherwise each source file may be listed
+# with full pathname. RELATIVE may makes it easier to extract an executable name
+# automatically.
+file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
+# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
+# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
+foreach( testsourcefile ${APP_SOURCES} )
+    # I used a simple string replace, to cut off .cpp.
+    string( REPLACE ".cpp" "" testname ${testsourcefile} )
+    add_executable( ${testname} ${testsourcefile} )
+
+    set_target_properties(${testname} PROPERTIES
+        LINKER_LANGUAGE CXX
+        CXX_STANDARD 14)
+    if(OpenMP_CXX_FOUND)
+        target_link_libraries(${testname} OpenMP::OpenMP_CXX)
+    endif()
+    install(TARGETS ${testname} DESTINATION "bin/graph")
+
+endforeach( testsourcefile ${APP_SOURCES} )

From 530da97aec01cfc1a1a0132a4875ea62a746858e Mon Sep 17 00:00:00 2001
From: Ayaan Khan 
Date: Fri, 17 Jul 2020 20:49:00 +0530
Subject: [PATCH 06/90] fix: infinity

---
 graph/dijkstra.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp
index 3dca9e64a..9b47e446f 100644
--- a/graph/dijkstra.cpp
+++ b/graph/dijkstra.cpp
@@ -28,8 +28,9 @@
 #include 
 #include 
 #include 
+#include 
 
-constexpr long long INF = 1000000000;
+constexpr long long INF = std::numeric_limits::max();
 
 /**
  * @namespace graph
@@ -66,7 +67,7 @@ int dijkstra(std::vector>> *adj, int s, int t) {
     int n = adj->size();
 
     /// setting all the distances initially to INF
-    std::vector dist(n, INF);
+    std::vector dist(n, INF);
 
     /// creating a min heap using priority queue
     /// first element of pair contains the distance

From eb10e84a15243a4ed5463b6d268dd5c013f71e68 Mon Sep 17 00:00:00 2001
From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Date: Fri, 17 Jul 2020 15:20:20 +0000
Subject: [PATCH 07/90] formatting source-code for
 530da97aec01cfc1a1a0132a4875ea62a746858e

---
 graph/dijkstra.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp
index 9b47e446f..620c0215c 100644
--- a/graph/dijkstra.cpp
+++ b/graph/dijkstra.cpp
@@ -25,10 +25,10 @@
  */
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
-#include 
 
 constexpr long long INF = std::numeric_limits::max();
 

From d141abaeb895377dc72dd5b19e486e1e2afe05af Mon Sep 17 00:00:00 2001
From: Ayaan Khan 
Date: Fri, 17 Jul 2020 21:42:28 +0530
Subject: [PATCH 08/90] Update graph/CMakeLists.txt

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>
---
 graph/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/graph/CMakeLists.txt b/graph/CMakeLists.txt
index e00516075..02b8f4840 100644
--- a/graph/CMakeLists.txt
+++ b/graph/CMakeLists.txt
@@ -11,7 +11,7 @@ foreach( testsourcefile ${APP_SOURCES} )
 
     set_target_properties(${testname} PROPERTIES
         LINKER_LANGUAGE CXX
-        CXX_STANDARD 14)
+    )
     if(OpenMP_CXX_FOUND)
         target_link_libraries(${testname} OpenMP::OpenMP_CXX)
     endif()

From 483e00c67e462f4d8b1ad9eb5a069da976a3dfa0 Mon Sep 17 00:00:00 2001
From: Ayaan Khan 
Date: Fri, 17 Jul 2020 21:46:17 +0530
Subject: [PATCH 09/90] Update CMakeLists.txt

Co-authored-by: David Leal 
---
 CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index da75802a5..29e8c2783 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -87,4 +87,4 @@ endif()
 
 set(CPACK_PROJECT_NAME ${PROJECT_NAME})
 set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
-include(CPack)
\ No newline at end of file
+include(CPack)

From 00233391820855bd63f4b9db98c57df4ce6a8463 Mon Sep 17 00:00:00 2001
From: Ayaan Khan 
Date: Fri, 17 Jul 2020 22:40:58 +0530
Subject: [PATCH 10/90] fix: clang-tidy errors

---
 graph/connected_components.cpp | 26 ++++++++++++++++----------
 graph/dijkstra.cpp             | 13 +++++++------
 2 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/graph/connected_components.cpp b/graph/connected_components.cpp
index bfb92c7c8..06bb1ee50 100644
--- a/graph/connected_components.cpp
+++ b/graph/connected_components.cpp
@@ -39,8 +39,9 @@ namespace graph {
 /**
  * @brief Function that add edge between two nodes or vertices of graph
  *
- * @param u any node or vertex of graph
- * @param v any node or vertex of graph
+ * @param adj adjacency list of graph.
+ * @param u any node or vertex of graph.
+ * @param v any node or vertex of graph.
  */
 void addEdge(std::vector> *adj, int u, int v) {
     (*adj)[u - 1].push_back(v - 1);
@@ -51,14 +52,15 @@ void addEdge(std::vector> *adj, int u, int v) {
  * @brief Utility function for depth first seach algorithm
  * this function explores the vertex which is passed into.
  *
- * @param u vertex or node to be explored
- * @param visited already visited vertices
+ * @param adj adjacency list of graph.
+ * @param u vertex or node to be explored.
+ * @param visited already visited vertices.
  */
 void explore(const std::vector> *adj, int u,
-             std::vector &visited) {
-    visited[u] = true;
+             std::vector *visited) {
+    (*visited)[u] = true;
     for (auto v : (*adj)[u]) {
-        if (!visited[v]) {
+        if (!(*visited)[v]) {
             explore(adj, v, visited);
         }
     }
@@ -67,6 +69,10 @@ void explore(const std::vector> *adj, int u,
 /**
  * @brief Function that perfoms depth first search algorithm on graph
  * and calculated the number of connected components.
+ *
+ * @param adj adjacency list of graph.
+ *
+ * @return connected_components number of connected components in graph.
  */
 int getConnectedComponents(const std::vector> *adj) {
     int n = adj->size();
@@ -75,7 +81,7 @@ int getConnectedComponents(const std::vector> *adj) {
 
     for (int i = 0; i < n; i++) {
         if (!visited[i]) {
-            explore(adj, i, visited);
+            explore(adj, i, &visited);
             connected_components++;
         }
     }
@@ -122,7 +128,7 @@ int main() {
     /// running predefined tests
     tests();
 
-    int vertices, edges;
+    int vertices = int(), edges = int();
     std::cout << "Enter the number of vertices : ";
     std::cin >> vertices;
     std::cout << "Enter the number of edges : ";
@@ -130,8 +136,8 @@ int main() {
 
     std::vector> adj(vertices, std::vector());
 
+    int u = int(), v = int();
     while (edges--) {
-        int u, v;
         std::cin >> u >> v;
         graph::addEdge(&adj, u, v);
     }
diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp
index 620c0215c..90fb0ec4f 100644
--- a/graph/dijkstra.cpp
+++ b/graph/dijkstra.cpp
@@ -29,8 +29,9 @@
 #include 
 #include 
 #include 
+#include 
 
-constexpr long long INF = std::numeric_limits::max();
+constexpr int64_t INF = std::numeric_limits::max();
 
 /**
  * @namespace graph
@@ -67,13 +68,13 @@ int dijkstra(std::vector>> *adj, int s, int t) {
     int n = adj->size();
 
     /// setting all the distances initially to INF
-    std::vector dist(n, INF);
+    std::vector dist(n, INF);
 
     /// creating a min heap using priority queue
     /// first element of pair contains the distance
     /// second element of pair contains the vertex
     std::priority_queue, std::vector>,
-                        std::greater>>
+                        std::greater<>>
         pq;
 
     /// pushing the source vertex 's' with 0 distance in min heap
@@ -152,7 +153,7 @@ int main() {
     // running predefined tests
     tests();
 
-    int vertices, edges;
+    int vertices = int(), edges = int();
     std::cout << "Enter the number of vertices : ";
     std::cin >> vertices;
     std::cout << "Enter the number of edges : ";
@@ -161,13 +162,13 @@ int main() {
     std::vector>> adj(
         vertices, std::vector>());
 
-    int u, v, w;
+    int u = int(), v = int(), w = int();
     while (edges--) {
         std::cin >> u >> v >> w;
         graph::addEdge(&adj, u, v, w);
     }
 
-    int s, t;
+    int s = int(), t = int();
     std::cin >> s >> t;
     int dist = graph::dijkstra(&adj, s - 1, t - 1);
     if (dist == -1) {

From cbb43c90aa2ebe69c32fcb095d946c350e85f60c Mon Sep 17 00:00:00 2001
From: Ayaan Khan 
Date: Sat, 25 Jul 2020 07:55:30 +0530
Subject: [PATCH 11/90] fix: build

---
 graph/bfs.cpp                                 | 240 +++++++++---
 .../bridge_finding_with_tarjan_algorithm.cpp  |   2 +
 graph/cycle_check_directed_graph.cpp          | 345 +++---------------
 graph/dfs.cpp                                 | 141 ++++++-
 graph/dijkstra.cpp                            | 184 +++++-----
 graph/kosaraju.cpp                            |  30 +-
 graph/kruskal.cpp                             |   2 +
 graph/lca.cpp                                 |  22 +-
 graph/topological_sort.cpp                    |  21 +-
 9 files changed, 494 insertions(+), 493 deletions(-)

diff --git a/graph/bfs.cpp b/graph/bfs.cpp
index 3acee8f80..31f9c0770 100644
--- a/graph/bfs.cpp
+++ b/graph/bfs.cpp
@@ -1,62 +1,196 @@
+/**
+ *
+ * \file
+ * \brief [Breadth First Search Algorithm
+ * (Breadth First Search)](https://en.wikipedia.org/wiki/Breadth-first_search)
+ *
+ * \author [Ayaan Khan](http://github.com/ayaankhan98)
+ *
+ * \details
+ * 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.
+ *
+ * Applications of Breadth First Search are
+ *
+ * 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)
+ * 2. Ford-Fulkerson Method for computing the maximum flow in a flow network.
+ * 3. Testing bipartiteness of a graph.
+ * 4. Cheney's Algorithm, Copying garbage collection.
+ *
+ * And there are many more...
+ *
+ * 

working

+ * In the implementation below we first created a graph using the adjacency + * list representation of graph. + * Breadth First Search Works as follows + * it requires a vertex as a start vertex, Start vertex is that vertex + * from where you want to start traversing the graph. + * we maintain a bool array or a vector to keep track of the vertices + * which we have visited so that we do not traverse the visited vertices + * again and again and eventually fall into an infinite loop. Along with this + * boolen array we use a Queue. + * + * 1. First we mark the start vertex as visited. + * 2. Push this visited vertex in the Queue. + * 3. while the queue is not empty we repeat the following steps + * + * 1. Take out an element from the front of queue + * 2. start exploring the adjacency list of this vertex + * if element in the adjacency list is not visited then we + * push that element into the queue and mark this as visited + * + */ +#include +#include #include -using namespace std; -class graph { - int v; - list *adj; +#include +#include - public: - graph(int v); - void addedge(int src, int dest); - void printgraph(); - void bfs(int s); -}; -graph::graph(int v) { - this->v = v; - this->adj = new list[v]; +/** + * \namespace graph + * \brief Graph algorithms + */ +namespace graph { +/** + * \brief + * Adds and edge between two vertices of graph say u and v in this + * case. + * + * @param adj Adjacency list representation of graph + * @param u first vertex + * @param v second vertex + * + */ +void addEdge(std::vector> *adj, int u, int v) { + /** + * Here we are considering directed graph that's the + * reason we are adding v to the adjacency list representation of u + * but not adding u to the adjacency list representation of v + * + * in case of a un-directed graph you can un comment the statement below. + */ + (*adj)[u - 1].push_back(v - 1); + // adj[v - 1].push_back(u -1); } -void graph::addedge(int src, int dest) { - src--; - dest--; - adj[src].push_back(dest); - // adj[dest].push_back(src); -} -void graph::printgraph() { - for (int i = 0; i < this->v; i++) { - cout << "Adjacency list of vertex " << i + 1 << " is \n"; - list::iterator it; - for (it = adj[i].begin(); it != adj[i].end(); ++it) { - cout << *it + 1 << " "; - } - cout << endl; - } -} -void graph::bfs(int s) { - bool *visited = new bool[this->v + 1]; - memset(visited, false, sizeof(bool) * (this->v + 1)); - visited[s] = true; - list q; - q.push_back(s); - list::iterator it; - while (!q.empty()) { - int u = q.front(); - cout << u << " "; - q.pop_front(); - for (it = adj[u].begin(); it != adj[u].end(); ++it) { - if (visited[*it] == false) { - visited[*it] = true; - q.push_back(*it); + +/** + * \brief + * Function performs the breadth first search algorithm over the graph + * + * @param adj Adjacency list representation of graph + * @param start vertex from where traversing starts + * + */ +std::vector beadth_first_search(const std::vector> &adj, + int start) { + size_t vertices = adj.size(); + + std::vector result; + + /// vector to keep track of visited vertices + std::vector visited(vertices, 0); + + std::queue tracker; + /// marking the start vertex as visited + visited[start] = true; + tracker.push(start); + while (!tracker.empty()) { + size_t vertex = tracker.front(); + tracker.pop(); + result.push_back(vertex + 1); + for (auto x : adj[vertex]) { + /// if the vertex is not visited then mark this as visited + /// and push it to the queue + if (!visited[x]) { + visited[x] = true; + tracker.push(x); } } } + return result; } +} // namespace graph + +void tests() { + std::cout << "Initiating Tests" << std::endl; + + /// Test 1 Begin + std::vector> graphData(4, std::vector()); + graph::addEdge(&graphData, 1, 2); + graph::addEdge(&graphData, 1, 3); + graph::addEdge(&graphData, 2, 3); + graph::addEdge(&graphData, 3, 1); + graph::addEdge(&graphData, 3, 4); + graph::addEdge(&graphData, 4, 4); + + std::vector returnedResult = graph::beadth_first_search(graphData, 2); + std::vector correctResult = {3, 1, 4, 2}; + + assert(std::equal(correctResult.begin(), correctResult.end(), + returnedResult.begin())); + std::cout << "Test 1 Passed..." << std::endl; + + /// Test 2 Begin + /// clear data from previous test + returnedResult.clear(); + correctResult.clear(); + + returnedResult = graph::beadth_first_search(graphData, 0); + correctResult = {1, 2, 3, 4}; + + assert(std::equal(correctResult.begin(), correctResult.end(), + returnedResult.begin())); + std::cout << "Test 2 Passed..." << std::endl; + + /// Test 3 Begins + /// clear data from previous test + graphData.clear(); + returnedResult.clear(); + correctResult.clear(); + + graphData.resize(6); + graph::addEdge(&graphData, 1, 2); + graph::addEdge(&graphData, 1, 3); + graph::addEdge(&graphData, 2, 4); + graph::addEdge(&graphData, 3, 4); + graph::addEdge(&graphData, 2, 5); + graph::addEdge(&graphData, 4, 6); + + returnedResult = graph::beadth_first_search(graphData, 0); + correctResult = {1, 2, 3, 4, 5, 6}; + + assert(std::equal(correctResult.begin(), correctResult.end(), + returnedResult.begin())); + std::cout << "Test 3 Passed..." << std::endl; +} + +/** Main function */ int main() { - graph g(4); - g.addedge(1, 2); - g.addedge(2, 3); - g.addedge(3, 4); - g.addedge(1, 4); - g.addedge(1, 3); - // g.printgraph(); - g.bfs(2); + /// running predefined test cases + tests(); + + size_t vertices, edges; + std::cout << "Enter the number of vertices : "; + std::cin >> vertices; + std::cout << "Enter the number of edges : "; + std::cin >> edges; + + /// creating a graph + std::vector> adj(vertices, std::vector()); + + /// taking input for edges + std::cout << "Enter vertices in pair which have edges between them : " + << std::endl; + while (edges--) { + int u, v; + std::cin >> u >> v; + graph::addEdge(&adj, u, v); + } + + /// running Breadth First Search Algorithm on the graph + graph::beadth_first_search(adj, 0); return 0; -} +} \ No newline at end of file diff --git a/graph/bridge_finding_with_tarjan_algorithm.cpp b/graph/bridge_finding_with_tarjan_algorithm.cpp index eec176af5..2b54e1b96 100644 --- a/graph/bridge_finding_with_tarjan_algorithm.cpp +++ b/graph/bridge_finding_with_tarjan_algorithm.cpp @@ -7,9 +7,11 @@ #include // for min & max #include // for cout #include // for std::vector + using std::cout; using std::min; using std::vector; + class Solution { vector> graph; vector in_time, out_time; diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index 0f7b84cd3..c991730c0 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -1,302 +1,57 @@ -/** - * Copyright 2020 - * @file cycle_check_directed graph.cpp - * - * @brief BFS and DFS algorithms to check for cycle in a directed graph. - * - * @author Anmol3299 - * contact: mittalanmol22@gmail.com - * - */ +#include +#include +#include +using std::vector; +using std::pair; -#include // for std::cout -#include // for std::queue -#include // for throwing errors -#include // for std::remove_reference_t -#include // for std::unordered_map -#include // for std::move -#include // for std::vector - -/** - * Implementation of non-weighted directed edge of a graph. - * - * The source vertex of the edge is labelled "src" and destination vertex is - * labelled "dest". - */ -struct Edge { - unsigned int src; - unsigned int dest; - - Edge() = delete; - ~Edge() = default; - Edge(Edge&&) = default; - Edge& operator=(Edge&&) = default; - Edge(Edge const&) = default; - Edge& operator=(Edge const&) = default; - - /** Set the source and destination of the vertex. - * - * @param source is the source vertex of the edge. - * @param destination is the destination vertex of the edge. - */ - Edge(unsigned int source, unsigned int destination) - : src(source), dest(destination) {} +void explore(int i, vector> &adj, int *state) +{ + state[i] = 1; + for(auto it2 : adj[i]) + { + if (state[it2] == 0) + { + explore(it2, adj,state); + } + if (state[it2] == 1) + { + std::cout<<"1"; + exit(0); + } + } + state[i] = 2; }; +int acyclic(vector > &adj,size_t n) { + //write your code here -using AdjList = std::unordered_map>; + int state[n]; // permitted states are 0 1 and 2 -/** - * Implementation of graph class. - * - * The graph will be represented using Adjacency List representation. - * This class contains 2 data members "m_vertices" & "m_adjList" used to - * represent the number of vertices and adjacency list of the graph - * respectively. The vertices are labelled 0 - (m_vertices - 1). - */ -class Graph { - public: - Graph() : m_vertices(0), m_adjList({}) {} - ~Graph() = default; - Graph(Graph&&) = default; - Graph& operator=(Graph&&) = default; - Graph(Graph const&) = default; - Graph& operator=(Graph const&) = default; + // mark the states of all vertices initially to 0 + for(int i=0;i const& edges) - : m_vertices(vertices) { - for (auto const& edge : edges) { - if (edge.src >= vertices || edge.dest >= vertices) { - throw std::range_error( - "Either src or dest of edge out of range"); - } - m_adjList[edge.src].emplace_back(edge.dest); - } + for(auto it1 = 0; it1 != adj.size(); it1++) + { + if (state[it1] == 0) + explore(it1,adj,state); + if (state[it1] == 1) + { + std::cout<<"1"; + exit(0); } - - /** Return a const reference of the adjacency list. - * - * @return const reference to the adjacency list - */ - std::remove_reference_t const& getAdjList() const { - return m_adjList; - } - - /** - * @return number of vertices in the graph. - */ - std::remove_reference_t const& getVertices() const { - return m_vertices; - } - - /** Add vertices in the graph. - * - * @param num is the number of vertices to be added. It adds 1 vertex by - * default. - * - */ - void addVertices(unsigned int num = 1) { m_vertices += num; } - - /** Add an edge in the graph. - * - * @param edge that needs to be added. - */ - void addEdge(Edge const& edge) { - if (edge.src >= m_vertices || edge.dest >= m_vertices) { - throw std::range_error("Either src or dest of edge out of range"); - } - m_adjList[edge.src].emplace_back(edge.dest); - } - - /** Add an Edge in the graph - * - * @param source is source vertex of the edge. - * @param destination is the destination vertex of the edge. - */ - void addEdge(unsigned int source, unsigned int destination) { - if (source >= m_vertices || destination >= m_vertices) { - throw std::range_error( - "Either source or destination of edge out of range"); - } - m_adjList[source].emplace_back(destination); - } - - private: - unsigned int m_vertices; - AdjList m_adjList; -}; - -class CycleCheck { - private: - enum nodeStates : uint8_t { not_visited = 0, in_stack, visited }; - - /** Helper function of "isCyclicDFS". - * - * @param adjList is the adjacency list representation of some graph. - * @param state is the state of the nodes of the graph. - * @param node is the node being evaluated. - * - * @return true if graph has a cycle, else false. - */ - static bool isCyclicDFSHelper(AdjList const& adjList, - std::vector* state, - unsigned int node) { - // Add node "in_stack" state. - (*state)[node] = in_stack; - - // If the node has children, then recursively visit all children of the - // node. - if (auto const& it = adjList.find(node); it != adjList.end()) { - for (auto child : it->second) { - // If state of child node is "not_visited", evaluate that child - // for presence of cycle. - if (auto state_of_child = (*state)[child]; - state_of_child == not_visited) { - if (isCyclicDFSHelper(adjList, state, child)) { - return true; - } - } else if (state_of_child == in_stack) { - // If child node was "in_stack", then that means that there - // is a cycle in the graph. Return true for presence of the - // cycle. - return true; - } - } - } - - // Current node has been evaluated for the presence of cycle and had no - // cycle. Mark current node as "visited". - (*state)[node] = visited; - // Return that current node didn't result in any cycles. - return false; - } - - public: - /** Driver function to check if a graph has a cycle. - * - * This function uses DFS to check for cycle in the graph. - * - * @param graph which needs to be evaluated for the presence of cycle. - * @return true if a cycle is detected, else false. - */ - static bool isCyclicDFS(Graph const& graph) { - /** State of the node. - * - * It is a vector of "nodeStates" which represents the state node is in. - * It can take only 3 values: "not_visited", "in_stack", and "visited". - * - * Initially, all nodes are in "not_visited" state. - */ - std::vector state(graph.getVertices(), not_visited); - - // Start visiting each node. - for (auto node = 0; node < graph.getVertices(); node++) { - // If a node is not visited, only then check for presence of cycle. - // There is no need to check for presence of cycle for a visited - // node as it has already been checked for presence of cycle. - if (state[node] == not_visited) { - // Check for cycle. - if (isCyclicDFSHelper(graph.getAdjList(), &state, node)) { - return true; - } - } - } - - // All nodes have been safely traversed, that means there is no cycle in - // the graph. Return false. - return false; - } - - /** Check if a graph has cycle or not. - * - * This function uses BFS to check if a graph is cyclic or not. - * - * @param graph which needs to be evaluated for the presence of cycle. - * @return true if a cycle is detected, else false. - */ - static bool isCyclicBFS(Graph const& graph) { - AdjList graphAjdList = graph.getAdjList(); - - std::vector indegree(graph.getVertices(), 0); - // Calculate the indegree i.e. the number of incident edges to the node. - for (auto const& [parent, children] : graphAjdList) { - for (auto const& child : children) { - indegree[child]++; - } - } - - std::queue can_be_solved; - for (auto node = 0; node < graph.getVertices(); node++) { - // If a node doesn't have any input edges, then that node will - // definately not result in a cycle and can be visited safely. - if (!indegree[node]) { - can_be_solved.emplace(node); - } - } - - // Vertices that need to be traversed. - auto remain = graph.getVertices(); - // While there are safe nodes that we can visit. - while (!can_be_solved.empty()) { - auto front = can_be_solved.front(); - // Visit the node. - can_be_solved.pop(); - // Decrease number of nodes that need to be traversed. - remain--; - - // Visit all the children of the visited node. - if (auto it = graphAjdList.find(front); it != graphAjdList.end()) { - for (auto child : it->second) { - // Check if we can visited the node safely. - if (--indegree[child] == 0) { - // if node can be visited safely, then add that node to - // the visit queue. - can_be_solved.emplace(child); - } - } - } - } - - // If there are still nodes that we can't visit, then it means that - // there is a cycle and return true, else return false. - return !(remain == 0); - } -}; - -/** - * Main function. - */ -int main() { - // Instantiate the graph. - Graph g(7, std::vector{{0, 1}, {1, 2}, {2, 0}, {2, 5}, {3, 5}}); - // Check for cycle using BFS method. - std::cout << CycleCheck::isCyclicBFS(g) << '\n'; - - // Check for cycle using DFS method. - std::cout << CycleCheck::isCyclicDFS(g) << '\n'; - return 0; + } + std::cout<<"0"; + return 0; +} + +int main() { + size_t n, m; + std::cin >> n >> m; + vector > adj(n, vector()); + for (size_t i = 0; i < m; i++) { + int x, y; + std::cin >> x >> y; + adj[x - 1].push_back(y - 1); + } + acyclic(adj,n); } diff --git a/graph/dfs.cpp b/graph/dfs.cpp index 2d38c8725..f79179045 100644 --- a/graph/dfs.cpp +++ b/graph/dfs.cpp @@ -1,26 +1,133 @@ +/** + * + * \file + * \brief [Depth First Search Algorithm + * (Depth First Search)](https://en.wikipedia.org/wiki/Depth-first_search) + * + * \author [Ayaan Khan](http://github.com/ayaankhan98) + * + * \details + * Depth First Search also quoted as DFS is a Graph Traversal Algorithm. + * Time Complexity O(|V| + |E|) where V is number of vertices and E + * is number of edges in graph. + * + * Application of Depth First Search are + * + * 1. Finding connected components + * 2. Finding 2-(edge or vertex)-connected components. + * 3. Finding 3-(edge or vertex)-connected components. + * 4. Finding the bridges of a graph. + * 5. Generating words in order to plot the limit set of a group. + * 6. Finding strongly connected components. + * + * And there are many more... + * + *

Working

+ * 1. Mark all vertices as unvisited first + * 2. start exploring from some starting vertex. + * + * While exploring vertex we mark the vertex as visited + * and start exploring the vertices connected to this + * vertex in recursive way. + * + */ + +#include #include -using namespace std; -int v = 4; -void DFSUtil_(int graph[4][4], bool visited[], int s) { - visited[s] = true; - cout << s << " "; - for (int i = 0; i < v; i++) { - if (graph[s][i] == 1 && visited[i] == false) { - DFSUtil_(graph, visited, i); +#include + +/** + * + * \namespace graph + * \brief Graph Algorithms + * + */ +namespace graph { +/** + * \brief + * Adds and edge between two vertices of graph say u and v in this + * case. + * + * @param adj Adjacency list representation of graph + * @param u first vertex + * @param v second vertex + * + */ +void addEdge(std::vector> *adj, size_t u, size_t v) { + /** + * + * Here we are considering undirected graph that's the + * reason we are adding v to the adjacency list representation of u + * and also adding u to the adjacency list representation of v + * + */ + (*adj)[u - 1].push_back(v - 1); + (*adj)[v - 1].push_back(u - 1); +} + +/** + * + * \brief + * Explores the given vertex, exploring a vertex means traversing + * over all the vertices which are connected to the vertex that is + * currently being explored. + * + * @param adj garph + * @param v vertex to be explored + * @param visited already visited vertices + * + */ +void explore(const std::vector> &adj, size_t v, + std::vector *visited) { + std::cout << v + 1 << " "; + (*visited)[v] = true; + for (auto x : adj[v]) { + if (!(*visited)[x]) { + explore(adj, x, visited); } } } -void DFS_(int graph[4][4], int s) { - bool visited[v]; - memset(visited, 0, sizeof(visited)); - DFSUtil_(graph, visited, s); -} +/** + * \brief + * initiates depth first search algorithm. + * + * @param adj adjacency list of graph + * @param start vertex from where DFS starts traversing. + * + */ +void depth_first_search(const std::vector> &adj, + size_t start) { + size_t vertices = adj.size(); + std::vector visited(vertices, false); + explore(adj, start, &visited); +} +} // namespace graph + +/** Main function */ int main() { - int graph[4][4] = {{0, 1, 1, 0}, {0, 0, 1, 0}, {1, 0, 0, 1}, {0, 0, 0, 1}}; - cout << "DFS: "; - DFS_(graph, 2); - cout << endl; + size_t vertices, edges; + std::cout << "Enter the Vertices : "; + std::cin >> vertices; + std::cout << "Enter the Edges : "; + std::cin >> edges; + + /// creating graph + std::vector> adj(vertices, std::vector()); + + /// taking input for edges + std::cout << "Enter the vertices which have edges between them : " + << std::endl; + while (edges--) { + size_t u, v; + std::cin >> u >> v; + graph::addEdge(&adj, u, v); + } + + /// running depth first search over graph + graph::depth_first_search(adj, 2); + + std::cout << std::endl; return 0; } \ No newline at end of file diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp index 90fb0ec4f..ac269de18 100644 --- a/graph/dijkstra.cpp +++ b/graph/dijkstra.cpp @@ -39,31 +39,31 @@ constexpr int64_t INF = std::numeric_limits::max(); */ namespace graph { -/** - * @brief Function that add edge between two nodes or vertices of graph - * - * @param u any node or vertex of graph - * @param v any node or vertex of graph - */ -void addEdge(std::vector>> *adj, int u, int v, - int w) { + /** + * @brief Function that add edge between two nodes or vertices of graph + * + * @param u any node or vertex of graph + * @param v any node or vertex of graph + */ + void addEdge(std::vector>> *adj, int u, int v, + int w) { (*adj)[u - 1].push_back(std::make_pair(v - 1, w)); // (*adj)[v - 1].push_back(std::make_pair(u - 1, w)); -} + } -/** - * @brief Function runs the dijkstra algorithm for some source vertex and - * target vertex in the graph and returns the shortest distance of target - * from the source. - * - * @param adj input graph - * @param s source vertex - * @param t target vertex - * - * @return shortest distance if target is reachable from source else -1 in - * case if target is not reachable from source. - */ -int dijkstra(std::vector>> *adj, int s, int t) { + /** + * @brief Function runs the dijkstra algorithm for some source vertex and + * target vertex in the graph and returns the shortest distance of target + * from the source. + * + * @param adj input graph + * @param s source vertex + * @param t target vertex + * + * @return shortest distance if target is reachable from source else -1 in + * case if target is not reachable from source. + */ + int dijkstra(std::vector>> *adj, int s, int t) { /// n denotes the number of vertices in graph int n = adj->size(); @@ -74,7 +74,7 @@ int dijkstra(std::vector>> *adj, int s, int t) { /// first element of pair contains the distance /// second element of pair contains the vertex std::priority_queue, std::vector>, - std::greater<>> + std::greater>> pq; /// pushing the source vertex 's' with 0 distance in min heap @@ -84,97 +84,97 @@ int dijkstra(std::vector>> *adj, int s, int t) { dist[s] = 0; while (!pq.empty()) { - /// second element of pair denotes the node / vertex - int currentNode = pq.top().second; + /// second element of pair denotes the node / vertex + int currentNode = pq.top().second; - /// first element of pair denotes the distance - int currentDist = pq.top().first; + /// first element of pair denotes the distance + int currentDist = pq.top().first; - pq.pop(); + pq.pop(); - /// for all the reachable vertex from the currently exploring vertex - /// we will try to minimize the distance - for (std::pair edge : (*adj)[currentNode]) { - /// minimizing distances - if (currentDist + edge.second < dist[edge.first]) { - dist[edge.first] = currentDist + edge.second; - pq.push(std::make_pair(dist[edge.first], edge.first)); - } + /// for all the reachable vertex from the currently exploring vertex + /// we will try to minimize the distance + for (std::pair edge : (*adj)[currentNode]) { + /// minimizing distances + if (currentDist + edge.second < dist[edge.first]) { + dist[edge.first] = currentDist + edge.second; + pq.push(std::make_pair(dist[edge.first], edge.first)); } + } } if (dist[t] != INF) { - return dist[t]; + return dist[t]; } return -1; -} + } } // namespace graph /** Function to test the Algorithm */ void tests() { - std::cout << "Initiatinig Predefined Tests..." << std::endl; - std::cout << "Initiating Test 1..." << std::endl; - std::vector>> adj1( - 4, std::vector>()); - graph::addEdge(&adj1, 1, 2, 1); - graph::addEdge(&adj1, 4, 1, 2); - graph::addEdge(&adj1, 2, 3, 2); - graph::addEdge(&adj1, 1, 3, 5); + std::cout << "Initiatinig Predefined Tests..." << std::endl; + std::cout << "Initiating Test 1..." << std::endl; + std::vector>> adj1( + 4, std::vector>()); + graph::addEdge(&adj1, 1, 2, 1); + graph::addEdge(&adj1, 4, 1, 2); + graph::addEdge(&adj1, 2, 3, 2); + graph::addEdge(&adj1, 1, 3, 5); - int s = 1, t = 3; - assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3); - std::cout << "Test 1 Passed..." << std::endl; + int s = 1, t = 3; + assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3); + std::cout << "Test 1 Passed..." << std::endl; - s = 4, t = 3; - std::cout << "Initiating Test 2..." << std::endl; - assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5); - std::cout << "Test 2 Passed..." << std::endl; + s = 4, t = 3; + std::cout << "Initiating Test 2..." << std::endl; + assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5); + std::cout << "Test 2 Passed..." << std::endl; - std::vector>> adj2( - 5, std::vector>()); - graph::addEdge(&adj2, 1, 2, 4); - graph::addEdge(&adj2, 1, 3, 2); - graph::addEdge(&adj2, 2, 3, 2); - graph::addEdge(&adj2, 3, 2, 1); - graph::addEdge(&adj2, 2, 4, 2); - graph::addEdge(&adj2, 3, 5, 4); - graph::addEdge(&adj2, 5, 4, 1); - graph::addEdge(&adj2, 2, 5, 3); - graph::addEdge(&adj2, 3, 4, 4); + std::vector>> adj2( + 5, std::vector>()); + graph::addEdge(&adj2, 1, 2, 4); + graph::addEdge(&adj2, 1, 3, 2); + graph::addEdge(&adj2, 2, 3, 2); + graph::addEdge(&adj2, 3, 2, 1); + graph::addEdge(&adj2, 2, 4, 2); + graph::addEdge(&adj2, 3, 5, 4); + graph::addEdge(&adj2, 5, 4, 1); + graph::addEdge(&adj2, 2, 5, 3); + graph::addEdge(&adj2, 3, 4, 4); - s = 1, t = 5; - std::cout << "Initiating Test 3..." << std::endl; - assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6); - std::cout << "Test 3 Passed..." << std::endl; - std::cout << "All Test Passed..." << std::endl << std::endl; + s = 1, t = 5; + std::cout << "Initiating Test 3..." << std::endl; + assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6); + std::cout << "Test 3 Passed..." << std::endl; + std::cout << "All Test Passed..." << std::endl << std::endl; } /** Main function */ int main() { - // running predefined tests - tests(); + // running predefined tests + tests(); - int vertices = int(), edges = int(); - std::cout << "Enter the number of vertices : "; - std::cin >> vertices; - std::cout << "Enter the number of edges : "; - std::cin >> edges; + int vertices = int(), edges = int(); + std::cout << "Enter the number of vertices : "; + std::cin >> vertices; + std::cout << "Enter the number of edges : "; + std::cin >> edges; - std::vector>> adj( - vertices, std::vector>()); + std::vector>> adj( + vertices, std::vector>()); - int u = int(), v = int(), w = int(); - while (edges--) { - std::cin >> u >> v >> w; - graph::addEdge(&adj, u, v, w); - } + int u = int(), v = int(), w = int(); + while (edges--) { + std::cin >> u >> v >> w; + graph::addEdge(&adj, u, v, w); + } - int s = int(), t = int(); - std::cin >> s >> t; - int dist = graph::dijkstra(&adj, s - 1, t - 1); - if (dist == -1) { - std::cout << "Target not reachable from source" << std::endl; - } else { - std::cout << "Shortest Path Distance : " << dist << std::endl; - } - return 0; + int s = int(), t = int(); + std::cin >> s >> t; + int dist = graph::dijkstra(&adj, s - 1, t - 1); + if (dist == -1) { + std::cout << "Target not reachable from source" << std::endl; + } else { + std::cout << "Shortest Path Distance : " << dist << std::endl; + } + return 0; } diff --git a/graph/kosaraju.cpp b/graph/kosaraju.cpp index 00c9d7ca0..a9ef121aa 100644 --- a/graph/kosaraju.cpp +++ b/graph/kosaraju.cpp @@ -4,8 +4,8 @@ #include #include +#include -using namespace std; /** * Iterative function/method to print graph: @@ -13,13 +13,13 @@ using namespace std; * @param V : vertices * @return void **/ -void print(vector a[], int V) { +void print(std::vector a[], int V) { for (int i = 0; i < V; i++) { if (!a[i].empty()) - cout << "i=" << i << "-->"; - for (int j = 0; j < a[i].size(); j++) cout << a[i][j] << " "; + std::cout << "i=" << i << "-->"; + for (int j = 0; j < a[i].size(); j++) std::cout << a[i][j] << " "; if (!a[i].empty()) - cout << endl; + std::cout << std::endl; } } @@ -31,7 +31,7 @@ void print(vector a[], int V) { * @param adj[] : array of vectors to represent graph * @return void **/ -void push_vertex(int v, stack &st, bool vis[], vector adj[]) { +void push_vertex(int v, std::stack &st, bool vis[], std::vector adj[]) { vis[v] = true; for (auto i = adj[v].begin(); i != adj[v].end(); i++) { if (vis[*i] == false) @@ -47,7 +47,7 @@ void push_vertex(int v, stack &st, bool vis[], vector adj[]) { * @param grev[] : graph with reversed edges * @return void **/ -void dfs(int v, bool vis[], vector grev[]) { +void dfs(int v, bool vis[], std::vector grev[]) { vis[v] = true; // cout<0)) i.e. it returns the count of (number of) strongly connected components (SCCs) in the graph. (variable 'count_scc' within function) **/ -int kosaraju(int V, vector adj[]) { +int kosaraju(int V, std::vector adj[]) { bool vis[V] = {}; - stack st; + std::stack st; for (int v = 0; v < V; v++) { if (vis[v] == false) push_vertex(v, st, vis, adj); } // making new graph (grev) with reverse edges as in adj[]: - vector grev[V]; + std::vector grev[V]; for (int i = 0; i < V + 1; i++) { for (auto j = adj[i].begin(); j != adj[i].end(); j++) { grev[*j].push_back(i); @@ -102,20 +102,20 @@ int kosaraju(int V, vector adj[]) { // Input your required values: (not hardcoded) int main() { int t; - cin >> t; + std::cin >> t; while (t--) { int a, b; // a->number of nodes, b->directed edges. - cin >> a >> b; + std::cin >> a >> b; int m, n; - vector adj[a + 1]; + std::vector adj[a + 1]; for (int i = 0; i < b; i++) // take total b inputs of 2 vertices each // required to form an edge. { - cin >> m >> n; // take input m,n denoting edge from m->n. + std::cin >> m >> n; // take input m,n denoting edge from m->n. adj[m].push_back(n); } // pass number of nodes and adjacency array as parameters to function: - cout << kosaraju(a, adj) << endl; + std::cout << kosaraju(a, adj) << std::endl; } return 0; } diff --git a/graph/kruskal.cpp b/graph/kruskal.cpp index b7b830668..861b81ae1 100644 --- a/graph/kruskal.cpp +++ b/graph/kruskal.cpp @@ -1,4 +1,6 @@ #include +#include +#include //#include // using namespace boost::multiprecision; const int mx = 1e6 + 5; diff --git a/graph/lca.cpp b/graph/lca.cpp index c05cf7b9b..e69be62fa 100644 --- a/graph/lca.cpp +++ b/graph/lca.cpp @@ -1,7 +1,9 @@ //#include #include - -using namespace std; +#include +#include +#include +#include // Find the lowest common ancestor using binary lifting in O(nlogn) // Zero based indexing // Resource : https://cp-algorithms.com/graph/lca_binary_lifting.html @@ -9,7 +11,7 @@ const int N = 1005; const int LG = log2(N) + 1; struct lca { int n; - vector adj[N]; // Graph + std::vector adj[N]; // Graph int up[LG][N]; // build this table int level[N]; // get the levels of all of them @@ -18,7 +20,7 @@ struct lca { memset(level, 0, sizeof(level)); for (int i = 0; i < n - 1; ++i) { int a, b; - cin >> a >> b; + std::cin >> a >> b; a--; b--; adj[a].push_back(b); @@ -30,15 +32,15 @@ struct lca { } void verify() { for (int i = 0; i < n; ++i) { - cout << i << " : level: " << level[i] << endl; + std::cout << i << " : level: " << level[i] << std::endl; } - cout << endl; + std::cout << std::endl; for (int i = 0; i < LG; ++i) { - cout << "Power:" << i << ": "; + std::cout << "Power:" << i << ": "; for (int j = 0; j < n; ++j) { - cout << up[i][j] << " "; + std::cout << up[i][j] << " "; } - cout << endl; + std::cout << std::endl; } } @@ -65,7 +67,7 @@ struct lca { u--; v--; if (level[v] > level[u]) { - swap(u, v); + std::swap(u, v); } // u is at the bottom. int dist = level[u] - level[v]; diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 9e6c8917b..e7dd7ab63 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -1,12 +1,11 @@ #include #include #include -using namespace std; int n, m; // For number of Vertices (V) and number of edges (E) -vector> G; -vector visited; -vector ans; +std::vector> G; +std::vector visited; +std::vector ans; void dfs(int v) { visited[v] = true; @@ -27,21 +26,21 @@ void topological_sort() { reverse(ans.begin(), ans.end()); } int main() { - cout << "Enter the number of vertices and the number of directed edges\n"; - cin >> n >> m; + std::cout << "Enter the number of vertices and the number of directed edges\n"; + std::cin >> n >> m; int x, y; - G.resize(n, vector()); + G.resize(n, std::vector()); for (int i = 0; i < n; ++i) { - cin >> x >> y; + std::cin >> x >> y; x--, y--; // to convert 1-indexed to 0-indexed G[x].push_back(y); } topological_sort(); - cout << "Topological Order : \n"; + std::cout << "Topological Order : \n"; for (int v : ans) { - cout << v + 1 + std::cout << v + 1 << ' '; // converting zero based indexing back to one based. } - cout << '\n'; + std::cout << '\n'; return 0; } From e2f344c498e27aba17194032d4da5e77b82acdc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Sat, 1 Aug 2020 22:55:50 -0700 Subject: [PATCH 12/90] Fix linter warnings in fixgraph branch. --- graph/bfs.cpp | 8 +- .../bridge_finding_with_tarjan_algorithm.cpp | 4 +- graph/cycle_check_directed_graph.cpp | 35 +++--- graph/dfs.cpp | 6 +- graph/kosaraju.cpp | 53 +++++---- graph/kruskal.cpp | 105 +++++------------- graph/lca.cpp | 4 +- graph/topological_sort.cpp | 8 +- 8 files changed, 92 insertions(+), 131 deletions(-) diff --git a/graph/bfs.cpp b/graph/bfs.cpp index 31f9c0770..9e66f3ff2 100644 --- a/graph/bfs.cpp +++ b/graph/bfs.cpp @@ -91,7 +91,7 @@ std::vector beadth_first_search(const std::vector> &adj, std::vector result; /// vector to keep track of visited vertices - std::vector visited(vertices, 0); + std::vector visited(vertices, false); std::queue tracker; /// marking the start vertex as visited @@ -172,7 +172,7 @@ int main() { /// running predefined test cases tests(); - size_t vertices, edges; + size_t vertices = 0, edges = 0; std::cout << "Enter the number of vertices : "; std::cin >> vertices; std::cout << "Enter the number of edges : "; @@ -185,7 +185,7 @@ int main() { std::cout << "Enter vertices in pair which have edges between them : " << std::endl; while (edges--) { - int u, v; + int u = 0, v = 0; std::cin >> u >> v; graph::addEdge(&adj, u, v); } @@ -193,4 +193,4 @@ int main() { /// running Breadth First Search Algorithm on the graph graph::beadth_first_search(adj, 0); return 0; -} \ No newline at end of file +} diff --git a/graph/bridge_finding_with_tarjan_algorithm.cpp b/graph/bridge_finding_with_tarjan_algorithm.cpp index 2b54e1b96..5f1b5796a 100644 --- a/graph/bridge_finding_with_tarjan_algorithm.cpp +++ b/graph/bridge_finding_with_tarjan_algorithm.cpp @@ -15,7 +15,7 @@ using std::vector; class Solution { vector> graph; vector in_time, out_time; - int timer; + int timer = 0; vector> bridge; vector visited; void dfs(int current_node, int parent) { @@ -51,7 +51,7 @@ class Solution { return bridge; } }; -int main(void) { +int main() { Solution s1; int number_of_node = 5; vector> node; diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index c991730c0..cb36a13c8 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -1,39 +1,44 @@ #include #include -#include +#include using std::vector; using std::pair; -void explore(int i, vector> &adj, int *state) +void explore(int i, const vector> &adj, std::vector *state) { - state[i] = 1; - for(auto it2 : adj[i]) + (*state)[i] = 1; + for (const auto it2 : adj[i]) { - if (state[it2] == 0) + if ((*state)[it2] == 0) { - explore(it2, adj,state); + explore(it2, adj, state); } - if (state[it2] == 1) + if ((*state)[it2] == 1) { std::cout<<"1"; exit(0); } } - state[i] = 2; + (*state)[i] = 2; }; -int acyclic(vector > &adj,size_t n) { +int acyclic(const vector > &adj, size_t n) { //write your code here - int state[n]; // permitted states are 0 1 and 2 + // permitted states are 0 1 and 2 + std::vector state(n, 0); // mark the states of all vertices initially to 0 - for(int i=0;i > &adj,size_t n) { } int main() { - size_t n, m; + size_t n = 0, m = 0; std::cin >> n >> m; vector > adj(n, vector()); for (size_t i = 0; i < m; i++) { - int x, y; + int x = 0, y = 0; std::cin >> x >> y; adj[x - 1].push_back(y - 1); } diff --git a/graph/dfs.cpp b/graph/dfs.cpp index f79179045..e3f4e7ece 100644 --- a/graph/dfs.cpp +++ b/graph/dfs.cpp @@ -107,7 +107,7 @@ void depth_first_search(const std::vector> &adj, /** Main function */ int main() { - size_t vertices, edges; + size_t vertices = 0, edges = 0; std::cout << "Enter the Vertices : "; std::cin >> vertices; std::cout << "Enter the Edges : "; @@ -120,7 +120,7 @@ int main() { std::cout << "Enter the vertices which have edges between them : " << std::endl; while (edges--) { - size_t u, v; + size_t u = 0, v = 0; std::cin >> u >> v; graph::addEdge(&adj, u, v); } @@ -130,4 +130,4 @@ int main() { std::cout << std::endl; return 0; -} \ No newline at end of file +} diff --git a/graph/kosaraju.cpp b/graph/kosaraju.cpp index a9ef121aa..751ace56f 100644 --- a/graph/kosaraju.cpp +++ b/graph/kosaraju.cpp @@ -13,13 +13,17 @@ * @param V : vertices * @return void **/ -void print(std::vector a[], int V) { +void print(const std::vector< std::vector > &a, int V) { for (int i = 0; i < V; i++) { - if (!a[i].empty()) + if (!a[i].empty()) { std::cout << "i=" << i << "-->"; - for (int j = 0; j < a[i].size(); j++) std::cout << a[i][j] << " "; - if (!a[i].empty()) + } + for (int j : a[i]) { + std::cout << j << " "; + } + if (!a[i].empty()) { std::cout << std::endl; + } } } @@ -27,32 +31,34 @@ void print(std::vector a[], int V) { * //Recursive function/method to push vertices into stack passed as parameter: * @param v : vertices * @param &st : stack passed by reference - * @param vis[] : array to keep track of visited nodes (boolean type) + * @param &vis : array to keep track of visited nodes (boolean type) * @param adj[] : array of vectors to represent graph * @return void **/ -void push_vertex(int v, std::stack &st, bool vis[], std::vector adj[]) { - vis[v] = true; +void push_vertex(int v, std::stack *st, std::vector *vis, const std::vector< std::vector > &adj) { + (*vis)[v] = true; for (auto i = adj[v].begin(); i != adj[v].end(); i++) { - if (vis[*i] == false) + if ((*vis)[*i] == false) { push_vertex(*i, st, vis, adj); + } } - st.push(v); + st->push(v); } /** * //Recursive function/method to implement depth first traversal(dfs): * @param v : vertices - * @param vis[] : array to keep track of visited nodes (boolean type) + * @param vis : array to keep track of visited nodes (boolean type) * @param grev[] : graph with reversed edges * @return void **/ -void dfs(int v, bool vis[], std::vector grev[]) { - vis[v] = true; +void dfs(int v, std::vector *vis, const std::vector< std::vector > &grev) { + (*vis)[v] = true; // cout<0)) i.e. it returns the count of (number of) strongly connected components (SCCs) in the graph. (variable 'count_scc' within function) **/ -int kosaraju(int V, std::vector adj[]) { - bool vis[V] = {}; +int kosaraju(int V, const std::vector< std::vector > &adj) { + std::vector vis(V, false); std::stack st; for (int v = 0; v < V; v++) { - if (vis[v] == false) - push_vertex(v, st, vis, adj); + if (vis[v] == false) { + push_vertex(v, &st, &vis, adj); + } } // making new graph (grev) with reverse edges as in adj[]: - std::vector grev[V]; + std::vector< std::vector > grev(V); for (int i = 0; i < V + 1; i++) { for (auto j = adj[i].begin(); j != adj[i].end(); j++) { grev[*j].push_back(i); @@ -89,7 +96,7 @@ int kosaraju(int V, std::vector adj[]) { int t = st.top(); st.pop(); if (vis[t] == false) { - dfs(t, vis, grev); + dfs(t, &vis, grev); count_scc++; } } @@ -101,13 +108,13 @@ int kosaraju(int V, std::vector adj[]) { // All critical/corner cases have been taken care of. // Input your required values: (not hardcoded) int main() { - int t; + int t = 0; std::cin >> t; while (t--) { - int a, b; // a->number of nodes, b->directed edges. + int a = 0, b = 0; // a->number of nodes, b->directed edges. std::cin >> a >> b; - int m, n; - std::vector adj[a + 1]; + int m = 0, n = 0; + std::vector< std::vector > adj(a + 1); for (int i = 0; i < b; i++) // take total b inputs of 2 vertices each // required to form an edge. { diff --git a/graph/kruskal.cpp b/graph/kruskal.cpp index 861b81ae1..deec717c0 100644 --- a/graph/kruskal.cpp +++ b/graph/kruskal.cpp @@ -1,75 +1,21 @@ #include #include #include +#include //#include // using namespace boost::multiprecision; const int mx = 1e6 + 5; -const long int inf = 2e9; -typedef long long ll; -#define rep(i, n) for (i = 0; i < n; i++) -#define repp(i, a, b) for (i = a; i <= b; i++) -#define pii pair -#define vpii vector -#define vi vector -#define vll vector -#define r(x) scanf("%d", &x) -#define rs(s) scanf("%s", s) -#define gc getchar_unlocked -#define pc putchar_unlocked -#define mp make_pair -#define pb push_back -#define lb lower_bound -#define ub upper_bound -#define endl "\n" -#define fast \ - ios_base::sync_with_stdio(false); \ - cin.tie(NULL); \ - cout.tie(NULL); -using namespace std; -void in(int &x) { - register int c = gc(); - x = 0; - int neg = 0; - for (; ((c < 48 || c > 57) && c != '-'); c = gc()) - ; - if (c == '-') { - neg = 1; - c = gc(); - } - for (; c > 47 && c < 58; c = gc()) { - x = (x << 1) + (x << 3) + c - 48; - } - if (neg) - x = -x; -} -void out(int n) { - int N = n, rev, count = 0; - rev = N; - if (N == 0) { - pc('0'); - return; - } - while ((rev % 10) == 0) { - count++; - rev /= 10; - } - rev = 0; - while (N != 0) { - rev = (rev << 3) + (rev << 1) + N % 10; - N /= 10; - } - while (rev != 0) { - pc(rev % 10 + '0'); - rev /= 10; - } - while (count--) pc('0'); -} -ll parent[mx], arr[mx], node, edge; -vector>> v; +typedef int64_t ll; + +std::array parent, arr; +ll node, edge; +std::vector>> v; void initial() { - int i; - rep(i, node + edge) parent[i] = i; + for (int i = 0; i < node + edge; ++i) { + parent[i] = i; + } } + int root(int i) { while (parent[i] != i) { parent[i] = parent[parent[i]]; @@ -77,16 +23,18 @@ int root(int i) { } return i; } + void join(int x, int y) { int root_x = root(x); // Disjoint set union by rank int root_y = root(y); parent[root_x] = root_y; } + ll kruskal() { - ll mincost = 0, i, x, y; - rep(i, edge) { - x = v[i].second.first; - y = v[i].second.second; + ll mincost = 0; + for (int i = 0; i < edge; ++i) { + ll x = v[i].second.first; + ll y = v[i].second.second; if (root(x) != root(y)) { mincost += v[i].first; join(x, y); @@ -94,23 +42,22 @@ ll kruskal() { } return mincost; } + int main() { - fast; - while (1) { - int i, j, from, to, cost, totalcost = 0; - cin >> node >> edge; // Enter the nodes and edges - if (node == 0 && edge == 0) + while (true) { + int from = 0, to = 0, cost = 0, totalcost = 0; + std::cin >> node >> edge; // Enter the nodes and edges + if (node == 0 && edge == 0) { break; // Enter 0 0 to break out + } initial(); // Initialise the parent array - rep(i, edge) { - cin >> from >> to >> cost; - v.pb(mp(cost, mp(from, to))); + for (int i = 0; i < edge; ++i) { + std::cin >> from >> to >> cost; + v.emplace_back(make_pair(cost, std::make_pair(from, to))); totalcost += cost; } sort(v.begin(), v.end()); - // rep(i,v.size()) - // cout< adj[N]; // Graph diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index e7dd7ab63..f77780cdc 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -10,8 +10,9 @@ std::vector ans; void dfs(int v) { visited[v] = true; for (int u : G[v]) { - if (!visited[u]) + if (!visited[u]) { dfs(u); + } } ans.push_back(v); } @@ -20,15 +21,16 @@ void topological_sort() { visited.assign(n, false); ans.clear(); for (int i = 0; i < n; ++i) { - if (!visited[i]) + if (!visited[i]) { dfs(i); + } } reverse(ans.begin(), ans.end()); } int main() { std::cout << "Enter the number of vertices and the number of directed edges\n"; std::cin >> n >> m; - int x, y; + int x = 0, y = 0; G.resize(n, std::vector()); for (int i = 0; i < n; ++i) { std::cin >> x >> y; From 1c5f229b3c2c11a5db7f77566227576f63f25163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Sun, 2 Aug 2020 23:45:56 -0700 Subject: [PATCH 13/90] Refactor lowest common ancestor --- graph/lca.cpp | 101 ------------- graph/lowest_common_ancestor.cpp | 252 +++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 101 deletions(-) delete mode 100644 graph/lca.cpp create mode 100644 graph/lowest_common_ancestor.cpp diff --git a/graph/lca.cpp b/graph/lca.cpp deleted file mode 100644 index 22c133016..000000000 --- a/graph/lca.cpp +++ /dev/null @@ -1,101 +0,0 @@ -//#include -#include -#include -#include -#include -#include -// Find the lowest common ancestor using binary lifting in O(nlogn) -// Zero based indexing -// Resource : https://cp-algorithms.com/graph/lca_binary_lifting.html -constexpr int N = 1005; -constexpr int LG = 20; -struct lca { - int n; - std::vector adj[N]; // Graph - int up[LG][N]; // build this table - int level[N]; // get the levels of all of them - - lca(int n_) : n(n_) { - memset(up, -1, sizeof(up)); - memset(level, 0, sizeof(level)); - for (int i = 0; i < n - 1; ++i) { - int a, b; - std::cin >> a >> b; - a--; - b--; - adj[a].push_back(b); - adj[b].push_back(a); - } - level[0] = 0; - dfs(0, -1); - build(); - } - void verify() { - for (int i = 0; i < n; ++i) { - std::cout << i << " : level: " << level[i] << std::endl; - } - std::cout << std::endl; - for (int i = 0; i < LG; ++i) { - std::cout << "Power:" << i << ": "; - for (int j = 0; j < n; ++j) { - std::cout << up[i][j] << " "; - } - std::cout << std::endl; - } - } - - void build() { - for (int i = 1; i < LG; ++i) { - for (int j = 0; j < n; ++j) { - if (up[i - 1][j] != -1) { - up[i][j] = up[i - 1][up[i - 1][j]]; - } - } - } - } - - void dfs(int node, int par) { - up[0][node] = par; - for (auto i : adj[node]) { - if (i != par) { - level[i] = level[node] + 1; - dfs(i, node); - } - } - } - int query(int u, int v) { - u--; - v--; - if (level[v] > level[u]) { - std::swap(u, v); - } - // u is at the bottom. - int dist = level[u] - level[v]; - // Go up this much distance - for (int i = LG - 1; i >= 0; --i) { - if (dist & (1 << i)) { - u = up[i][u]; - } - } - if (u == v) { - return u; - } - assert(level[u] == level[v]); - for (int i = LG - 1; i >= 0; --i) { - if (up[i][u] != up[i][v]) { - u = up[i][u]; - v = up[i][v]; - } - } - assert(up[0][u] == up[0][v]); - return up[0][u]; - } -}; - -int main() { - int n; // number of nodes in the tree. - lca l(n); // will take the input in the format given - // n-1 edges of the form - // a b - // Use verify function to see. -} diff --git a/graph/lowest_common_ancestor.cpp b/graph/lowest_common_ancestor.cpp new file mode 100644 index 000000000..994a574b4 --- /dev/null +++ b/graph/lowest_common_ancestor.cpp @@ -0,0 +1,252 @@ +/** + * + * \file + * + * \brief Data structure for finding the lowest common ancestor + * of two vertices in a rooted tree using binary lifting. + * + * \details + * Algorithm: https://cp-algorithms.com/graph/lca_binary_lifting.html + * + * Complexity: + * - Precomputation: \f$O(N \log N)\f$ where \f$N\f$ is the number of vertices in the tree + * - Query: \f$O(\log N)\f$ + * - Space: \f$O(N \log N)\f$ + * + * Example: + *
Tree: + *
+ *             _  3  _
+ *          /     |     \
+ *        1       6       4
+ *      / |     /   \       \
+ *    7   5   2       8       0
+ *            |
+ *            9
+ * 
+ * + *
lowest_common_ancestor(7, 4) = 3 + *
lowest_common_ancestor(9, 6) = 6 + *
lowest_common_ancestor(0, 0) = 0 + *
lowest_common_ancestor(8, 2) = 6 + * + * The query is symmetrical, therefore + * lowest_common_ancestor(x, y) = lowest_common_ancestor(y, x) + */ + +#include +#include +#include +#include +#include + +/** + * Class for representing a graph as an adjacency list. + * Its vertices are indexed 0, 1, ..., N - 1. + */ +class Graph { + public: + /** + * \brief Populate the adjacency list for each vertex in the graph. + * Assumes that evey edge is a pair of valid vertex indices. + * + * @param N number of vertices in the graph + * @param undirected_edges list of graph's undirected edges + */ + Graph(size_t N, const std::vector< std::pair > &undirected_edges) { + neighbors.resize(N); + for (auto &edge : undirected_edges) { + neighbors[edge.first].push_back(edge.second); + neighbors[edge.second].push_back(edge.first); + } + } + + /** + * Function to get the number of vertices in the graph + * @return the number of vertices in the graph. + */ + int number_of_vertices() const { + return neighbors.size(); + } + + /** \brief for each vertex it stores a list indicies of its neighbors */ + std::vector< std::vector > neighbors; +}; + +/** + * Representation of a rooted tree. For every vertex its parent is precalculated. + */ +class RootedTree : public Graph { + public: + /** + * \brief Constructs the tree by calculating parent for every vertex. + * Assumes a valid description of a tree is provided. + * + * @param undirected_edges list of graph's undirected edges + * @param root_ index of the root vertex + */ + RootedTree(const std::vector< std::pair > &undirected_edges, int root_) + : Graph(undirected_edges.size() + 1, undirected_edges), root(root_) { + populate_parents(); + } + + /** + * \brief Stores parent of every vertex and for root its own index. + * The root is technically not its own parent, but it's very practical + * for the lowest common ancestor algorithm. + */ + std::vector parent; + /** \brief Stores the distance from the root. */ + std::vector level; + /** \brief Index of the root vertex. */ + int root; + + protected: + /** + * \brief Calculate the parents for all the vertices in the tree. + * Implements the breadth first search algorithm starting from the root + * vertex searching the entire tree and labeling parents for all vertices. + * @returns none + */ + void populate_parents() { + // Initialize the vector with -1 which indicates the vertex + // wasn't yet visited. + parent = std::vector(number_of_vertices(), -1); + level = std::vector(number_of_vertices()); + parent[root] = root; + level[root] = 0; + std::queue queue_of_vertices; + queue_of_vertices.push(root); + while (!queue_of_vertices.empty()) { + int vertex = queue_of_vertices.front(); + queue_of_vertices.pop(); + for (int neighbor : neighbors[vertex]) { + // As long as the vertex was not yet visited. + if (parent[neighbor] == -1) { + parent[neighbor] = vertex; + level[neighbor] = level[vertex] + 1; + queue_of_vertices.push(neighbor); + } + } + } + } + +}; + +/** + * A structure that holds a rooted tree and allow for effecient + * queries of the lowest common ancestor of two given vertices in the tree. + */ +class LowestCommonAncestor { + public: + /** + * \brief Stores the tree and precomputs "up lifts". + * @param tree_ rooted tree. + */ + explicit LowestCommonAncestor(const RootedTree& tree_) : tree(tree_) { + populate_up(); + } + + /** + * \brief Query the structure to find the lowest common ancestor. + * Assumes that the provided numbers are valid indices of vertices. + * Iterativelly modifies ("lifts") u an v until it finnds their lowest + * common ancestor. + * @param u index of one of the queried vertex + * @param v index of the other queried vertex + * @return index of the vertex which is the lowet common ancestor of u and v + */ + int lowest_common_ancestor(int u, int v) const { + // Ensure u is the deeper (higher level) of the two vertices + if (tree.level[v] > tree.level[u]) { + std::swap(u, v); + } + + // "Lift" u to the same level as v. + int level_diff = tree.level[u] - tree.level[v]; + for (int i = 0; (1 << i) <= level_diff; ++i) { + if (level_diff & (1 << i)) { + u = up[u][i]; + } + } + assert(tree.level[u] == tree.level[v]); + + if (u == v) { + return u; + } + + // "Lift" u and v to their 2^i th ancestor if they are different + for (int i = static_cast(up[u].size()) - 1; i >= 0; --i) { + if (up[u][i] != up[v][i]) { + u = up[u][i]; + v = up[v][i]; + } + } + + // As we regressed u an v such that they cannot further be lifted so + // that their ancestor would be different, the only logical + // consequence is that their parent is the sought answer. + assert(up[u][0] == up[v][0]); + return up[u][0]; + } + + /* \brief reference to the rooted tree this structure allows to query */ + const RootedTree& tree; + /** + * \brief for every vertex stores a list of its ancestors by powers of two + * For each vertex, the first element of the corresponding list contains + * the index of its parent. The i-th element of the list is an index of + * the (2^i)-th ancestor of the vertex. + */ + std::vector< std::vector > up; + + protected: + /** + * Populate the "up" structure. See above. + */ + void populate_up() { + up.resize(tree.number_of_vertices()); + for (int vertex = 0; vertex < tree.number_of_vertices(); ++vertex) { + up[vertex].push_back(tree.parent[vertex]); + } + for (int level = 0; (1 << level) < tree.number_of_vertices(); ++level) { + for (int vertex = 0; vertex < tree.number_of_vertices(); ++vertex) { + // up[vertex][level + 1] = 2^(level + 1) th ancestor of vertex = + // = 2^level th ancestor of 2^level th ancestor of vertex = + // = 2^level th ancestor of up[vertex][level] + up[vertex].push_back(up[up[vertex][level]][level]); + } + } + } +}; + +/** + * Unit tests + * @rerturns none + */ +void tests() { + /** + * _ 3 _ + * / | \ + * 1 6 4 + * / | / \ \ + * 7 5 2 8 0 + * | + * 9 + */ + std::vector< std::pair > edges = { + {7, 1}, {1, 5}, {1, 3}, {3, 6}, {6, 2}, {2, 9}, {6, 8}, {4, 3}, {0, 4} + }; + RootedTree t(edges, 3); + LowestCommonAncestor lca(t); + assert(lca.lowest_common_ancestor(7, 4) == 3); + assert(lca.lowest_common_ancestor(9, 6) == 6); + assert(lca.lowest_common_ancestor(0, 0) == 0); + assert(lca.lowest_common_ancestor(8, 2) == 6); +} + +/** Main function */ +int main() { + tests(); + return 0; +} From 0c10e6fee5eb1dc3b9a6adc1172980d4f3f352c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 01:32:48 -0700 Subject: [PATCH 14/90] Fix linter for topological_sort_by_kahns_algo --- graph/topological_sort_by_kahns_algo.cpp | 41 ++++++++++++------------ 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/graph/topological_sort_by_kahns_algo.cpp b/graph/topological_sort_by_kahns_algo.cpp index 57ee01b23..c29c0fc2d 100644 --- a/graph/topological_sort_by_kahns_algo.cpp +++ b/graph/topological_sort_by_kahns_algo.cpp @@ -1,19 +1,20 @@ -#include -#include +#include +#include #include #include #include -int *topoSortKahn(int N, std::vector adj[]); +std::vector topoSortKahn(int N, const std::vector< std::vector > &adj); int main() { - int nodes, edges; + int nodes = 0, edges = 0; std::cin >> edges >> nodes; - if (edges == 0 || nodes == 0) + if (edges == 0 || nodes == 0) { return 0; - int u, v; + } + int u = 0, v = 0; - std::vector graph[nodes]; + std::vector< std::vector > graph(nodes); // create graph // example // 6 6 @@ -24,19 +25,19 @@ int main() { graph[u].push_back(v); } - int *topo = topoSortKahn(nodes, graph); + std::vector topo = topoSortKahn(nodes, graph); // topologically sorted nodes for (int i = 0; i < nodes; i++) { std::cout << topo[i] << " "; } } -int *topoSortKahn(int V, std::vector adj[]) { +std::vector topoSortKahn(int V, const std::vector< std::vector > &adj) { std::vector vis(V + 1, false); std::vector deg(V + 1, 0); for (int i = 0; i < V; i++) { - for (int j = 0; j < adj[i].size(); j++) { - deg[adj[i][j]]++; + for (int j : adj[i]) { + deg[j]++; } } std::queue q; @@ -46,20 +47,18 @@ int *topoSortKahn(int V, std::vector adj[]) { vis[i] = true; } } - int *arr = new int[V + 1]; - memset(arr, 0, V + 1); + std::vector arr(V + 1, 0); int count = 0; while (!q.empty()) { int cur = q.front(); q.pop(); - arr[count] = cur; - count++; - for (int i = 0; i < adj[cur].size(); i++) { - if (!vis[adj[cur][i]]) { - deg[adj[cur][i]]--; - if (deg[adj[cur][i]] == 0) { - q.push(adj[cur][i]); - vis[adj[cur][i]] = true; + arr[count++] = cur; + for (int i : adj[cur]) { + if (!vis[i]) { + deg[i]--; + if (deg[i] == 0) { + q.push(i); + vis[i] = true; } } } From 6be333680f65b387766b65a7694324acdbc4e849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 01:50:47 -0700 Subject: [PATCH 15/90] Remove unused global variable from dfs_with_stack --- graph/dfs_with_stack.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/graph/dfs_with_stack.cpp b/graph/dfs_with_stack.cpp index 193f3f291..5ca1c53b7 100644 --- a/graph/dfs_with_stack.cpp +++ b/graph/dfs_with_stack.cpp @@ -9,8 +9,6 @@ using namespace std; -int checked[999] = {WHITE}; - void dfs(const list lista[], int start) { stack stack; From 891e0dd5ca4e98a7798d3e71d258113d1dd7d23e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 02:06:20 -0700 Subject: [PATCH 16/90] Fix linter for dfs_with_stack. --- graph/dfs_with_stack.cpp | 47 +++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/graph/dfs_with_stack.cpp b/graph/dfs_with_stack.cpp index 5ca1c53b7..2d5752322 100644 --- a/graph/dfs_with_stack.cpp +++ b/graph/dfs_with_stack.cpp @@ -1,32 +1,29 @@ #include #include +#include #include -#define WHITE 0 -#define GREY 1 -#define BLACK 2 -#define INF 99999 - -using namespace std; - -void dfs(const list lista[], int start) { - stack stack; - - int checked[999] = {WHITE}; - - stack.push(start); +constexpr int WHITE = 0; +constexpr int GREY = 1; +constexpr int BLACK = 2; +constexpr int INF = 99999; +void dfs(const std::vector< std::list > &graph, int start) { + std::vector checked(graph.size(), WHITE); checked[start] = GREY; + std::stack stack; + stack.push(start); while (!stack.empty()) { int act = stack.top(); stack.pop(); if (checked[act] == GREY) { - cout << act << ' '; - for (auto it = lista[act].begin(); it != lista[act].end(); ++it) { - stack.push(*it); - if (checked[*it] != BLACK) - checked[*it] = GREY; + std::cout << act << ' '; + for (auto it : graph[act]) { + stack.push(it); + if (checked[it] != BLACK) { + checked[it] = GREY; + } } checked[act] = BLACK; // nodo controllato } @@ -34,16 +31,16 @@ void dfs(const list lista[], int start) { } int main() { - int u, w; - int n; - cin >> n; - list lista[INF]; + int n = 0; + std::cin >> n; + std::vector< std::list > graph(INF); for (int i = 0; i < n; ++i) { - cin >> u >> w; - lista[u].push_back(w); + int u = 0, w = 0; + std::cin >> u >> w; + graph[u].push_back(w); } - dfs(lista, 0); + dfs(graph, 0); return 0; } From d2b70159019827e809ead19c48a30a741140c593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 02:07:59 -0700 Subject: [PATCH 17/90] Remove unused global variable from graph/kruskal. --- graph/kruskal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph/kruskal.cpp b/graph/kruskal.cpp index deec717c0..9ee26766f 100644 --- a/graph/kruskal.cpp +++ b/graph/kruskal.cpp @@ -7,7 +7,7 @@ const int mx = 1e6 + 5; typedef int64_t ll; -std::array parent, arr; +std::array parent; ll node, edge; std::vector>> v; void initial() { From ef031dc8f294da2d6c165d7b7898390b23f8fd1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 02:10:00 -0700 Subject: [PATCH 18/90] Rename global variable from v to edges in kruskal. --- graph/kruskal.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/graph/kruskal.cpp b/graph/kruskal.cpp index 9ee26766f..b81a6a104 100644 --- a/graph/kruskal.cpp +++ b/graph/kruskal.cpp @@ -9,7 +9,7 @@ typedef int64_t ll; std::array parent; ll node, edge; -std::vector>> v; +std::vector>> edges; void initial() { for (int i = 0; i < node + edge; ++i) { parent[i] = i; @@ -33,10 +33,10 @@ void join(int x, int y) { ll kruskal() { ll mincost = 0; for (int i = 0; i < edge; ++i) { - ll x = v[i].second.first; - ll y = v[i].second.second; + ll x = edges[i].second.first; + ll y = edges[i].second.second; if (root(x) != root(y)) { - mincost += v[i].first; + mincost += edges[i].first; join(x, y); } } @@ -53,12 +53,12 @@ int main() { initial(); // Initialise the parent array for (int i = 0; i < edge; ++i) { std::cin >> from >> to >> cost; - v.emplace_back(make_pair(cost, std::make_pair(from, to))); + edges.emplace_back(make_pair(cost, std::make_pair(from, to))); totalcost += cost; } - sort(v.begin(), v.end()); + sort(edges.begin(), edges.end()); std::cout << kruskal() << std::endl; - v.clear(); + edges.clear(); } return 0; } From ac26555ec9b5c47848b08003dedc2c2886cff2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 23:40:09 -0700 Subject: [PATCH 19/90] Refactor prim --- graph/prim.cpp | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/graph/prim.cpp b/graph/prim.cpp index 5cc70bd39..c156bf3b8 100644 --- a/graph/prim.cpp +++ b/graph/prim.cpp @@ -3,56 +3,55 @@ #include #include -const int MAX = 1e4 + 5; typedef std::pair PII; -bool marked[MAX]; -std::vector adj[MAX]; - -int prim(int x) { +int prim(int x, const std::vector< std::vector > &graph) { // priority queue to maintain edges with respect to weights std::priority_queue, std::greater > Q; - int y; - int minimumCost = 0; - PII p; + std::vector marked(graph.size(), false); + int minimum_cost = 0; Q.push(std::make_pair(0, x)); while (!Q.empty()) { // Select the edge with minimum weight - p = Q.top(); + PII p = Q.top(); Q.pop(); x = p.second; // Checking for cycle - if (marked[x] == true) + if (marked[x] == true) { continue; - minimumCost += p.first; + } + minimum_cost += p.first; marked[x] = true; - for (int i = 0; i < adj[x].size(); ++i) { - y = adj[x][i].second; - if (marked[y] == false) - Q.push(adj[x][i]); + for (const PII &neighbor : graph[x]) { + int y = neighbor.second; + if (marked[y] == false) { + Q.push(neighbor); + } } } - return minimumCost; + return minimum_cost; } int main() { - int nodes, edges, x, y; - int weight, minimumCost; - + int nodes = 0, edges = 0; std::cin >> nodes >> edges; // number of nodes & edges in graph - if (nodes == 0 || edges == 0) + if (nodes == 0 || edges == 0) { return 0; + } + + std::vector< std::vector > graph(nodes); // Edges with their nodes & weight for (int i = 0; i < edges; ++i) { + int x = 0, y = 0, weight = 0; std::cin >> x >> y >> weight; - adj[x].push_back(std::make_pair(weight, y)); - adj[y].push_back(std::make_pair(weight, x)); + graph[x].push_back(std::make_pair(weight, y)); + graph[y].push_back(std::make_pair(weight, x)); } // Selecting 1 as the starting node - minimumCost = prim(1); - std::cout << minimumCost << std::endl; + int minimum_cost = prim(1, graph); + std::cout << minimum_cost << std::endl; return 0; } From 95227af3bc02ada95a0e068061c2ccc15408faa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 23:41:48 -0700 Subject: [PATCH 20/90] Fix linter for connected_components_with_dsu --- graph/connected_components_with_dsu.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/graph/connected_components_with_dsu.cpp b/graph/connected_components_with_dsu.cpp index aa03bef8f..71731e9da 100644 --- a/graph/connected_components_with_dsu.cpp +++ b/graph/connected_components_with_dsu.cpp @@ -13,8 +13,9 @@ void make_set() { // function the initialize every node as it's own parent } // To find the component where following node belongs to int find_set(int v) { - if (v == parent[v]) + if (v == parent[v]) { return v; + } return parent[v] = find_set(parent[v]); } @@ -22,8 +23,9 @@ void union_sets(int a, int b) { // To join 2 components to belong to one a = find_set(a); b = find_set(b); if (a != b) { - if (siz[a] < siz[b]) + if (siz[a] < siz[b]) { std::swap(a, b); + } parent[b] = a; siz[a] += siz[b]; } @@ -42,10 +44,10 @@ int main() { parent.resize(N + 1); siz.resize(N + 1); make_set(); - int edges; + int edges = 0; std::cin >> edges; // no of edges in the graph while (edges--) { - int node_a, node_b; + int node_a = 0, node_b = 0; std::cin >> node_a >> node_b; union_sets(node_a, node_b); } From 146f1c08b5d7ca237e68a5e5b741315c0a21dcc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 23:55:04 -0700 Subject: [PATCH 21/90] Fix linter warnings in ford_fulkerson --- ...th_ford_fulkerson_and_edmond_karp_algo.cpp | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp b/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp index cbd6bc15c..87b2fa101 100644 --- a/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp +++ b/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp @@ -16,16 +16,14 @@ // std::max capacity of node in graph const int MAXN = 505; class Graph { - int residual_capacity[MAXN][MAXN]; - int capacity[MAXN][MAXN]; // used while checking the flow of edge - int total_nodes; - int total_edges, source, sink; - int parent[MAXN]; + std::vector< std::vector > residual_capacity, capacity; + int total_nodes = 0; + int total_edges = 0, source = 0, sink = 0; + std::vector parent; std::vector > edge_participated; std::bitset visited; int max_flow = 0; bool bfs(int source, int sink) { // to find the augmented - path - memset(parent, -1, sizeof(parent)); visited.reset(); std::queue q; q.push(source); @@ -49,16 +47,17 @@ class Graph { } public: - Graph() { memset(residual_capacity, 0, sizeof(residual_capacity)); } - void set_graph(void) { + void set_graph() { std::cin >> total_nodes >> total_edges >> source >> sink; - for (int start, destination, capacity_, i = 0; i < total_edges; ++i) { + parent = std::vector(total_nodes, -1); + capacity = residual_capacity = std::vector< std::vector >(total_nodes, std::vector(total_nodes)); + for (int start = 0, destination = 0, capacity_ = 0, i = 0; i < total_edges; ++i) { std::cin >> start >> destination >> capacity_; residual_capacity[start][destination] = capacity_; capacity[start][destination] = capacity_; } } - void ford_fulkerson(void) { + void ford_fulkerson() { while (bfs(source, sink)) { int current_node = sink; int flow = std::numeric_limits::max(); @@ -77,12 +76,12 @@ class Graph { } } } - void print_flow_info(void) { + void print_flow_info() { for (int i = 0; i < total_nodes; ++i) { for (int j = 0; j < total_nodes; ++j) { if (capacity[i][j] && residual_capacity[i][j] < capacity[i][j]) { - edge_participated.push_back(std::make_tuple( + edge_participated.emplace_back(std::make_tuple( i, j, capacity[i][j] - residual_capacity[i][j])); } } @@ -92,14 +91,14 @@ class Graph { << '\n'; std::cout << "\nSource\tDestination\tCapacity\total_nodes"; for (auto& edge_data : edge_participated) { - int source, destination, capacity_; + int source = 0, destination = 0, capacity_ = 0; std::tie(source, destination, capacity_) = edge_data; std::cout << source << "\t" << destination << "\t\t" << capacity_ << '\t'; } } }; -int main(void) { +int main() { /* Input Graph: (for testing ) 4 5 0 3 From 1f9da87c129e3763f94af46f8038b7123ee88840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 3 Aug 2020 23:57:57 -0700 Subject: [PATCH 22/90] Fix global variable name for connected_component_with_dsu. --- graph/connected_components_with_dsu.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/graph/connected_components_with_dsu.cpp b/graph/connected_components_with_dsu.cpp index 71731e9da..50d0c4242 100644 --- a/graph/connected_components_with_dsu.cpp +++ b/graph/connected_components_with_dsu.cpp @@ -2,13 +2,13 @@ #include #include -int N; // denotes number of nodes; +int number_of_nodes; // denotes number of nodes; std::vector parent; -std::vector siz; +std::vector connected_set_size; void make_set() { // function the initialize every node as it's own parent - for (int i = 1; i <= N; i++) { + for (int i = 1; i <= number_of_nodes; i++) { parent[i] = i; - siz[i] = 1; + connected_set_size[i] = 1; } } // To find the component where following node belongs to @@ -23,26 +23,26 @@ void union_sets(int a, int b) { // To join 2 components to belong to one a = find_set(a); b = find_set(b); if (a != b) { - if (siz[a] < siz[b]) { + if (connected_set_size[a] < connected_set_size[b]) { std::swap(a, b); } parent[b] = a; - siz[a] += siz[b]; + connected_set_size[a] += connected_set_size[b]; } } int no_of_connected_components() { // To find total no of connected components std::set temp; // temp set to count number of connected components - for (int i = 1; i <= N; i++) temp.insert(find_set(i)); + for (int i = 1; i <= number_of_nodes; i++) temp.insert(find_set(i)); return temp.size(); } // All critical/corner cases have been taken care of. // Input your required values: (not hardcoded) int main() { - std::cin >> N; - parent.resize(N + 1); - siz.resize(N + 1); + std::cin >> number_of_nodes; + parent.resize(number_of_nodes + 1); + connected_set_size.resize(number_of_nodes + 1); make_set(); int edges = 0; std::cin >> edges; // no of edges in the graph From 5dbf857fb41bfbbd70b012a4c6014bcfb7e451ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Tue, 4 Aug 2020 00:00:52 -0700 Subject: [PATCH 23/90] Fix global variable name in topological_sort. --- graph/topological_sort.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index f77780cdc..3827b9bcf 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -2,44 +2,44 @@ #include #include -int n, m; // For number of Vertices (V) and number of edges (E) -std::vector> G; +int number_of_vertices, number_of_edges; // For number of Vertices (V) and number of edges (E) +std::vector> graph; std::vector visited; -std::vector ans; +std::vector topological_order; void dfs(int v) { visited[v] = true; - for (int u : G[v]) { + for (int u : graph[v]) { if (!visited[u]) { dfs(u); } } - ans.push_back(v); + topological_order.push_back(v); } void topological_sort() { - visited.assign(n, false); - ans.clear(); - for (int i = 0; i < n; ++i) { + visited.assign(number_of_vertices, false); + topological_order.clear(); + for (int i = 0; i < number_of_vertices; ++i) { if (!visited[i]) { dfs(i); } } - reverse(ans.begin(), ans.end()); + reverse(topological_order.begin(), topological_order.end()); } int main() { std::cout << "Enter the number of vertices and the number of directed edges\n"; - std::cin >> n >> m; + std::cin >> number_of_vertices >> number_of_edges; int x = 0, y = 0; - G.resize(n, std::vector()); - for (int i = 0; i < n; ++i) { + graph.resize(number_of_vertices, std::vector()); + for (int i = 0; i < number_of_edges; ++i) { std::cin >> x >> y; x--, y--; // to convert 1-indexed to 0-indexed - G[x].push_back(y); + graph[x].push_back(y); } topological_sort(); std::cout << "Topological Order : \n"; - for (int v : ans) { + for (int v : topological_order) { std::cout << v + 1 << ' '; // converting zero based indexing back to one based. } From a2178f33cd7632cfda9a7f46d4ef6a76e26d0e39 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sat, 8 Aug 2020 00:39:39 +0000 Subject: [PATCH 24/90] updating DIRECTORY.md --- DIRECTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 21953a553..b6070388a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -84,7 +84,7 @@ * [Hamiltons Cycle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hamiltons_cycle.cpp) * [Kosaraju](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kosaraju.cpp) * [Kruskal](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kruskal.cpp) - * [Lca](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/lca.cpp) + * [Lowest Common Ancestor](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/lowest_common_ancestor.cpp) * [Max Flow With Ford Fulkerson And Edmond Karp Algo](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp) * [Prim](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/prim.cpp) * [Topological Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/topological_sort.cpp) From b7383ed5523599d5c9a4f1b2c38dbbf31f21f56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Sat, 8 Aug 2020 13:02:14 -0700 Subject: [PATCH 25/90] Undo changs in cycle check. --- graph/cycle_check_directed_graph.cpp | 357 +++++++++++++++++++++++---- 1 file changed, 304 insertions(+), 53 deletions(-) diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index cb36a13c8..8a651cfc5 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -1,62 +1,313 @@ -#include -#include -#include -using std::vector; -using std::pair; +/** + * @file cycle_check_directed graph.cpp + * + * @brief BFS and DFS algorithms to check for cycle in a directed graph. + * + * @author [Anmol3299](mailto:mittalanmol22@gmail.com) + * + */ -void explore(int i, const vector> &adj, std::vector *state) -{ - (*state)[i] = 1; - for (const auto it2 : adj[i]) - { - if ((*state)[it2] == 0) - { - explore(it2, adj, state); - } - if ((*state)[it2] == 1) - { - std::cout<<"1"; - exit(0); - } - } - (*state)[i] = 2; +#include // for std::cout +#include // for std::map +#include // for std::queue +#include // for throwing errors +#include // for std::remove_reference +#include // for std::move +#include // for std::vector + +/** + * Implementation of non-weighted directed edge of a graph. + * + * The source vertex of the edge is labelled "src" and destination vertex is + * labelled "dest". + */ +struct Edge { + unsigned int src; + unsigned int dest; + + Edge() = delete; + ~Edge() = default; + Edge(Edge&&) = default; + Edge& operator=(Edge&&) = default; + Edge(Edge const&) = default; + Edge& operator=(Edge const&) = default; + + /** Set the source and destination of the vertex. + * + * @param source is the source vertex of the edge. + * @param destination is the destination vertex of the edge. + */ + Edge(unsigned int source, unsigned int destination) + : src(source), dest(destination) {} }; -int acyclic(const vector > &adj, size_t n) { - //write your code here - // permitted states are 0 1 and 2 - std::vector state(n, 0); +using AdjList = std::map>; - // mark the states of all vertices initially to 0 - for (size_t i = 0; i < n; i++) - { - state[i] = 0; - } +/** + * Implementation of graph class. + * + * The graph will be represented using Adjacency List representation. + * This class contains 2 data members "m_vertices" & "m_adjList" used to + * represent the number of vertices and adjacency list of the graph + * respectively. The vertices are labelled 0 - (m_vertices - 1). + */ +class Graph { + public: + Graph() : m_adjList({}) {} + ~Graph() = default; + Graph(Graph&&) = default; + Graph& operator=(Graph&&) = default; + Graph(Graph const&) = default; + Graph& operator=(Graph const&) = default; - for (size_t it1 = 0; it1 != adj.size(); it1++) - { - if (state[it1] == 0) - { - explore(it1, adj, &state); + /** Create a graph from vertices and adjacency list. + * + * @param vertices specify the number of vertices the graph would contain. + * @param adjList is the adjacency list representation of graph. + */ + Graph(unsigned int vertices, AdjList adjList) + : m_vertices(vertices), m_adjList(std::move(adjList)) {} + + /** Create a graph from vertices and adjacency list. + * + * @param vertices specify the number of vertices the graph would contain. + * @param adjList is the adjacency list representation of graph. + */ + Graph(unsigned int vertices, AdjList&& adjList) + : m_vertices(vertices), m_adjList(std::move(adjList)) {} + + /** Create a graph from vertices and a set of edges. + * + * Adjacency list of the graph would be created from the set of edges. If + * the source or destination of any edge has a value greater or equal to + * number of vertices, then it would throw a range_error. + * + * @param vertices specify the number of vertices the graph would contain. + * @param edges is a vector of edges. + */ + Graph(unsigned int vertices, std::vector const& edges) + : m_vertices(vertices) { + for (auto const& edge : edges) { + if (edge.src >= vertices || edge.dest >= vertices) { + throw std::range_error( + "Either src or dest of edge out of range"); + } + m_adjList[edge.src].emplace_back(edge.dest); + } } - if (state[it1] == 1) - { - std::cout<<"1"; - exit(0); - } - } - std::cout<<"0"; - return 0; -} + /** Return a const reference of the adjacency list. + * + * @return const reference to the adjacency list + */ + std::remove_reference::type const& getAdjList() const { + return m_adjList; + } + + /** + * @return number of vertices in the graph. + */ + unsigned int getVertices() const { return m_vertices; } + + /** Add vertices in the graph. + * + * @param num is the number of vertices to be added. It adds 1 vertex by + * default. + * + */ + void addVertices(unsigned int num = 1) { m_vertices += num; } + + /** Add an edge in the graph. + * + * @param edge that needs to be added. + */ + void addEdge(Edge const& edge) { + if (edge.src >= m_vertices || edge.dest >= m_vertices) { + throw std::range_error("Either src or dest of edge out of range"); + } + m_adjList[edge.src].emplace_back(edge.dest); + } + + /** Add an Edge in the graph + * + * @param source is source vertex of the edge. + * @param destination is the destination vertex of the edge. + */ + void addEdge(unsigned int source, unsigned int destination) { + if (source >= m_vertices || destination >= m_vertices) { + throw std::range_error( + "Either source or destination of edge out of range"); + } + m_adjList[source].emplace_back(destination); + } + + private: + unsigned int m_vertices = 0; + AdjList m_adjList; +}; + +/** + * Check if a directed graph has a cycle or not. + * + * This class provides 2 methods to check for cycle in a directed graph: + * isCyclicDFS & isCyclicBFS. + * + * - isCyclicDFS uses DFS traversal method to check for cycle in a graph. + * - isCyclidBFS used BFS traversal method to check for cycle in a graph. + */ +class CycleCheck { + private: + enum nodeStates : uint8_t { not_visited = 0, in_stack, visited }; + + /** Helper function of "isCyclicDFS". + * + * @param adjList is the adjacency list representation of some graph. + * @param state is the state of the nodes of the graph. + * @param node is the node being evaluated. + * + * @return true if graph has a cycle, else false. + */ + static bool isCyclicDFSHelper(AdjList const& adjList, + std::vector* state, + unsigned int node) { + // Add node "in_stack" state. + (*state)[node] = in_stack; + + // If the node has children, then recursively visit all children of the + // node. + auto const it = adjList.find(node); + if (it != adjList.end()) { + for (auto child : it->second) { + // If state of child node is "not_visited", evaluate that child + // for presence of cycle. + auto state_of_child = (*state)[child]; + if (state_of_child == not_visited) { + if (isCyclicDFSHelper(adjList, state, child)) { + return true; + } + } else if (state_of_child == in_stack) { + // If child node was "in_stack", then that means that there + // is a cycle in the graph. Return true for presence of the + // cycle. + return true; + } + } + } + + // Current node has been evaluated for the presence of cycle and had no + // cycle. Mark current node as "visited". + (*state)[node] = visited; + // Return that current node didn't result in any cycles. + return false; + } + + public: + /** Driver function to check if a graph has a cycle. + * + * This function uses DFS to check for cycle in the graph. + * + * @param graph which needs to be evaluated for the presence of cycle. + * @return true if a cycle is detected, else false. + */ + static bool isCyclicDFS(Graph const& graph) { + auto vertices = graph.getVertices(); + + /** State of the node. + * + * It is a vector of "nodeStates" which represents the state node is in. + * It can take only 3 values: "not_visited", "in_stack", and "visited". + * + * Initially, all nodes are in "not_visited" state. + */ + std::vector state(vertices, not_visited); + + // Start visiting each node. + for (unsigned int node = 0; node < vertices; node++) { + // If a node is not visited, only then check for presence of cycle. + // There is no need to check for presence of cycle for a visited + // node as it has already been checked for presence of cycle. + if (state[node] == not_visited) { + // Check for cycle. + if (isCyclicDFSHelper(graph.getAdjList(), &state, node)) { + return true; + } + } + } + + // All nodes have been safely traversed, that means there is no cycle in + // the graph. Return false. + return false; + } + + /** Check if a graph has cycle or not. + * + * This function uses BFS to check if a graph is cyclic or not. + * + * @param graph which needs to be evaluated for the presence of cycle. + * @return true if a cycle is detected, else false. + */ + static bool isCyclicBFS(Graph const& graph) { + auto graphAjdList = graph.getAdjList(); + auto vertices = graph.getVertices(); + + std::vector indegree(vertices, 0); + // Calculate the indegree i.e. the number of incident edges to the node. + for (auto const& list : graphAjdList) { + auto children = list.second; + for (auto const& child : children) { + indegree[child]++; + } + } + + std::queue can_be_solved; + for (unsigned int node = 0; node < vertices; node++) { + // If a node doesn't have any input edges, then that node will + // definately not result in a cycle and can be visited safely. + if (!indegree[node]) { + can_be_solved.emplace(node); + } + } + + // Vertices that need to be traversed. + auto remain = vertices; + // While there are safe nodes that we can visit. + while (!can_be_solved.empty()) { + auto solved = can_be_solved.front(); + // Visit the node. + can_be_solved.pop(); + // Decrease number of nodes that need to be traversed. + remain--; + + // Visit all the children of the visited node. + auto it = graphAjdList.find(solved); + if (it != graphAjdList.end()) { + for (auto child : it->second) { + // Check if we can visited the node safely. + if (--indegree[child] == 0) { + // if node can be visited safely, then add that node to + // the visit queue. + can_be_solved.emplace(child); + } + } + } + } + + // If there are still nodes that we can't visit, then it means that + // there is a cycle and return true, else return false. + return !(remain == 0); + } +}; + +/** + * Main function. + */ int main() { - size_t n = 0, m = 0; - std::cin >> n >> m; - vector > adj(n, vector()); - for (size_t i = 0; i < m; i++) { - int x = 0, y = 0; - std::cin >> x >> y; - adj[x - 1].push_back(y - 1); - } - acyclic(adj,n); + // Instantiate the graph. + Graph g(7, std::vector{{0, 1}, {1, 2}, {2, 0}, {2, 5}, {3, 5}}); + // Check for cycle using BFS method. + std::cout << CycleCheck::isCyclicBFS(g) << '\n'; + + // Check for cycle using DFS method. + std::cout << CycleCheck::isCyclicDFS(g) << '\n'; + return 0; } From 2e43bbdfb30d1ed115f0fe1597bd22e59d57d2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Sat, 8 Aug 2020 19:12:03 -0700 Subject: [PATCH 26/90] Rename dfs to depth_first_search.: --- DIRECTORY.md | 4 ++-- graph/{dfs.cpp => depth_first_search.cpp} | 0 .../{dfs_with_stack.cpp => depth_first_search_with_stack.cpp} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename graph/{dfs.cpp => depth_first_search.cpp} (100%) rename graph/{dfs_with_stack.cpp => depth_first_search_with_stack.cpp} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 76fa1154d..3ad77d592 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -78,8 +78,8 @@ * [Connected Components](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/connected_components.cpp) * [Connected Components With Dsu](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/connected_components_with_dsu.cpp) * [Cycle Check Directed Graph](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/cycle_check_directed_graph.cpp) - * [Dfs](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dfs.cpp) - * [Dfs With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dfs_with_stack.cpp) + * [Depth-First Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search.cpp) + * [Depth-First Search With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search_with_stack.cpp) * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dijkstra.cpp) * [Hamiltons Cycle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hamiltons_cycle.cpp) * [Kosaraju](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kosaraju.cpp) diff --git a/graph/dfs.cpp b/graph/depth_first_search.cpp similarity index 100% rename from graph/dfs.cpp rename to graph/depth_first_search.cpp diff --git a/graph/dfs_with_stack.cpp b/graph/depth_first_search_with_stack.cpp similarity index 100% rename from graph/dfs_with_stack.cpp rename to graph/depth_first_search_with_stack.cpp From f7ff88e7d73e379c62c328b9cc2b3e1093fce8de Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 9 Aug 2020 02:14:27 +0000 Subject: [PATCH 27/90] updating DIRECTORY.md --- DIRECTORY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 3ad77d592..119200746 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -78,8 +78,8 @@ * [Connected Components](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/connected_components.cpp) * [Connected Components With Dsu](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/connected_components_with_dsu.cpp) * [Cycle Check Directed Graph](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/cycle_check_directed_graph.cpp) - * [Depth-First Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search.cpp) - * [Depth-First Search With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search_with_stack.cpp) + * [Depth First Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search.cpp) + * [Depth First Search With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search_with_stack.cpp) * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dijkstra.cpp) * [Hamiltons Cycle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hamiltons_cycle.cpp) * [Kosaraju](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kosaraju.cpp) From 78ecea32743f539d8a14d0f6e1678e41368960d5 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 16 Aug 2020 19:32:56 +0000 Subject: [PATCH 28/90] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 76fa1154d..60f4717a4 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -82,6 +82,7 @@ * [Dfs With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dfs_with_stack.cpp) * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dijkstra.cpp) * [Hamiltons Cycle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hamiltons_cycle.cpp) + * [Is Graph Bipartite](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/is_graph_bipartite.cpp) * [Kosaraju](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kosaraju.cpp) * [Kruskal](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kruskal.cpp) * [Lowest Common Ancestor](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/lowest_common_ancestor.cpp) From d50f1c67c851a6f0c8d547551efef52ba618e092 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 16 Aug 2020 19:33:27 +0000 Subject: [PATCH 29/90] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 119200746..dff4ab078 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -82,6 +82,7 @@ * [Depth First Search With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search_with_stack.cpp) * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dijkstra.cpp) * [Hamiltons Cycle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hamiltons_cycle.cpp) + * [Is Graph Bipartite](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/is_graph_bipartite.cpp) * [Kosaraju](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kosaraju.cpp) * [Kruskal](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kruskal.cpp) * [Lowest Common Ancestor](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/lowest_common_ancestor.cpp) From 9927b462059075c74a5a85952de42cade8c73f08 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 17 Aug 2020 06:36:56 +0000 Subject: [PATCH 30/90] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index dff4ab078..0479e61dd 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -70,6 +70,7 @@ * [Tree Height](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/tree_height.cpp) ## Geometry + * [Jarvis Algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/jarvis_algorithm.cpp) * [Line Segment Intersection](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/line_segment_intersection.cpp) ## Graph From c1a2e33f2f0396fc21c93f799cfbd47101bd3dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Hl=C3=A1sek?= Date: Mon, 17 Aug 2020 20:10:03 -0700 Subject: [PATCH 31/90] Adjust comment. --- graph/depth_first_search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph/depth_first_search.cpp b/graph/depth_first_search.cpp index e3f4e7ece..e99d44fc9 100644 --- a/graph/depth_first_search.cpp +++ b/graph/depth_first_search.cpp @@ -54,7 +54,7 @@ namespace graph { * */ void addEdge(std::vector> *adj, size_t u, size_t v) { - /** + /* * * Here we are considering undirected graph that's the * reason we are adding v to the adjacency list representation of u From 4a34bec125762442dd7b74a465765dc777f87cd8 Mon Sep 17 00:00:00 2001 From: Deep Raval Date: Thu, 20 Aug 2020 00:55:32 +0530 Subject: [PATCH 32/90] feat: Add Neural Network (Multilayer Perceptron) (#1025) * Completed NN * Made changes * Added return in identity function * Added and fixed namespace naming * clang-tidy changes * Update machine_learning/neural_network.cpp Co-authored-by: David Leal * Update machine_learning/neural_network.cpp Co-authored-by: David Leal * Update machine_learning/neural_network.cpp Co-authored-by: David Leal * Update machine_learning/vector_ops.hpp Co-authored-by: David Leal * Update machine_learning/vector_ops.hpp Co-authored-by: David Leal * Update machine_learning/neural_network.cpp Co-authored-by: David Leal * Update machine_learning/neural_network.cpp Co-authored-by: David Leal * added std::cerr and changed argmax's namespace * Done suggested changes * Fixed a comment * clang-tidy fixes Co-authored-by: David Leal --- machine_learning/iris.csv | 152 ++++++ machine_learning/neural_network.cpp | 790 ++++++++++++++++++++++++++++ machine_learning/vector_ops.hpp | 484 +++++++++++++++++ 3 files changed, 1426 insertions(+) create mode 100644 machine_learning/iris.csv create mode 100644 machine_learning/neural_network.cpp create mode 100644 machine_learning/vector_ops.hpp diff --git a/machine_learning/iris.csv b/machine_learning/iris.csv new file mode 100644 index 000000000..e73d4a332 --- /dev/null +++ b/machine_learning/iris.csv @@ -0,0 +1,152 @@ +https://archive.ics.uci.edu/ml/datasets/iris +sepal length in cm,sepal width in cm,petal length in cm,petal width in cm +5.1,3.5,1.4,.2,0 +4.9,3,1.4,.2,0 +4.7,3.2,1.3,.2,0 +4.6,3.1,1.5,.2,0 +5,3.6,1.4,.2,0 +5.4,3.9,1.7,.4,0 +4.6,3.4,1.4,.3,0 +5,3.4,1.5,.2,0 +4.4,2.9,1.4,.2,0 +4.9,3.1,1.5,.1,0 +5.4,3.7,1.5,.2,0 +4.8,3.4,1.6,.2,0 +4.8,3,1.4,.1,0 +4.3,3,1.1,.1,0 +5.8,4,1.2,.2,0 +5.7,4.4,1.5,.4,0 +5.4,3.9,1.3,.4,0 +5.1,3.5,1.4,.3,0 +5.7,3.8,1.7,.3,0 +5.1,3.8,1.5,.3,0 +5.4,3.4,1.7,.2,0 +5.1,3.7,1.5,.4,0 +4.6,3.6,1,.2,0 +5.1,3.3,1.7,.5,0 +4.8,3.4,1.9,.2,0 +5,3,1.6,.2,0 +5,3.4,1.6,.4,0 +5.2,3.5,1.5,.2,0 +5.2,3.4,1.4,.2,0 +4.7,3.2,1.6,.2,0 +4.8,3.1,1.6,.2,0 +5.4,3.4,1.5,.4,0 +5.2,4.1,1.5,.1,0 +5.5,4.2,1.4,.2,0 +4.9,3.1,1.5,.2,0 +5,3.2,1.2,.2,0 +5.5,3.5,1.3,.2,0 +4.9,3.6,1.4,.1,0 +4.4,3,1.3,.2,0 +5.1,3.4,1.5,.2,0 +5,3.5,1.3,.3,0 +4.5,2.3,1.3,.3,0 +4.4,3.2,1.3,.2,0 +5,3.5,1.6,.6,0 +5.1,3.8,1.9,.4,0 +4.8,3,1.4,.3,0 +5.1,3.8,1.6,.2,0 +4.6,3.2,1.4,.2,0 +5.3,3.7,1.5,.2,0 +5,3.3,1.4,.2,0 +7,3.2,4.7,1.4,1 +6.4,3.2,4.5,1.5,1 +6.9,3.1,4.9,1.5,1 +5.5,2.3,4,1.3,1 +6.5,2.8,4.6,1.5,1 +5.7,2.8,4.5,1.3,1 +6.3,3.3,4.7,1.6,1 +4.9,2.4,3.3,1,1 +6.6,2.9,4.6,1.3,1 +5.2,2.7,3.9,1.4,1 +5,2,3.5,1,1 +5.9,3,4.2,1.5,1 +6,2.2,4,1,1 +6.1,2.9,4.7,1.4,1 +5.6,2.9,3.6,1.3,1 +6.7,3.1,4.4,1.4,1 +5.6,3,4.5,1.5,1 +5.8,2.7,4.1,1,1 +6.2,2.2,4.5,1.5,1 +5.6,2.5,3.9,1.1,1 +5.9,3.2,4.8,1.8,1 +6.1,2.8,4,1.3,1 +6.3,2.5,4.9,1.5,1 +6.1,2.8,4.7,1.2,1 +6.4,2.9,4.3,1.3,1 +6.6,3,4.4,1.4,1 +6.8,2.8,4.8,1.4,1 +6.7,3,5,1.7,1 +6,2.9,4.5,1.5,1 +5.7,2.6,3.5,1,1 +5.5,2.4,3.8,1.1,1 +5.5,2.4,3.7,1,1 +5.8,2.7,3.9,1.2,1 +6,2.7,5.1,1.6,1 +5.4,3,4.5,1.5,1 +6,3.4,4.5,1.6,1 +6.7,3.1,4.7,1.5,1 +6.3,2.3,4.4,1.3,1 +5.6,3,4.1,1.3,1 +5.5,2.5,4,1.3,1 +5.5,2.6,4.4,1.2,1 +6.1,3,4.6,1.4,1 +5.8,2.6,4,1.2,1 +5,2.3,3.3,1,1 +5.6,2.7,4.2,1.3,1 +5.7,3,4.2,1.2,1 +5.7,2.9,4.2,1.3,1 +6.2,2.9,4.3,1.3,1 +5.1,2.5,3,1.1,1 +5.7,2.8,4.1,1.3,1 +6.3,3.3,6,2.5,2 +5.8,2.7,5.1,1.9,2 +7.1,3,5.9,2.1,2 +6.3,2.9,5.6,1.8,2 +6.5,3,5.8,2.2,2 +7.6,3,6.6,2.1,2 +4.9,2.5,4.5,1.7,2 +7.3,2.9,6.3,1.8,2 +6.7,2.5,5.8,1.8,2 +7.2,3.6,6.1,2.5,2 +6.5,3.2,5.1,2,2 +6.4,2.7,5.3,1.9,2 +6.8,3,5.5,2.1,2 +5.7,2.5,5,2,2 +5.8,2.8,5.1,2.4,2 +6.4,3.2,5.3,2.3,2 +6.5,3,5.5,1.8,2 +7.7,3.8,6.7,2.2,2 +7.7,2.6,6.9,2.3,2 +6,2.2,5,1.5,2 +6.9,3.2,5.7,2.3,2 +5.6,2.8,4.9,2,2 +7.7,2.8,6.7,2,2 +6.3,2.7,4.9,1.8,2 +6.7,3.3,5.7,2.1,2 +7.2,3.2,6,1.8,2 +6.2,2.8,4.8,1.8,2 +6.1,3,4.9,1.8,2 +6.4,2.8,5.6,2.1,2 +7.2,3,5.8,1.6,2 +7.4,2.8,6.1,1.9,2 +7.9,3.8,6.4,2,2 +6.4,2.8,5.6,2.2,2 +6.3,2.8,5.1,1.5,2 +6.1,2.6,5.6,1.4,2 +7.7,3,6.1,2.3,2 +6.3,3.4,5.6,2.4,2 +6.4,3.1,5.5,1.8,2 +6,3,4.8,1.8,2 +6.9,3.1,5.4,2.1,2 +6.7,3.1,5.6,2.4,2 +6.9,3.1,5.1,2.3,2 +5.8,2.7,5.1,1.9,2 +6.8,3.2,5.9,2.3,2 +6.7,3.3,5.7,2.5,2 +6.7,3,5.2,2.3,2 +6.3,2.5,5,1.9,2 +6.5,3,5.2,2,2 +6.2,3.4,5.4,2.3,2 +5.9,3,5.1,1.8,2 diff --git a/machine_learning/neural_network.cpp b/machine_learning/neural_network.cpp new file mode 100644 index 000000000..aad469b14 --- /dev/null +++ b/machine_learning/neural_network.cpp @@ -0,0 +1,790 @@ +/** + * @file + * @author [Deep Raval](https://github.com/imdeep2905) + * + * @brief Implementation of [Multilayer Perceptron] (https://en.wikipedia.org/wiki/Multilayer_perceptron). + * + * @details + * A multilayer perceptron (MLP) is a class of feedforward artificial neural network (ANN). The term MLP is used ambiguously, + * sometimes loosely to any feedforward ANN, sometimes strictly to refer to networks composed of multiple layers of perceptrons + * (with threshold activation). Multilayer perceptrons are sometimes colloquially referred to as "vanilla" neural networks, + * especially when they have a single hidden layer. + * + * An MLP consists of at least three layers of nodes: an input layer, a hidden layer and an output layer. Except for the + * input nodes, each node is a neuron that uses a nonlinear activation function. MLP utilizes a supervised learning technique + * called backpropagation for training. Its multiple layers and non-linear activation distinguish MLP from a linear + * perceptron. It can distinguish data that is not linearly separable. + * + * See [Backpropagation](https://en.wikipedia.org/wiki/Backpropagation) for training algorithm. + * + * \note This implementation uses mini-batch gradient descent as optimizer and MSE as loss function. Bias is also not included. + */ + +#include "vector_ops.hpp" // Custom header file for vector operations + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** \namespace machine_learning + * \brief Machine learning algorithms + */ +namespace machine_learning { + /** \namespace neural_network + * \brief Neural Network or Multilayer Perceptron + */ + namespace neural_network { + /** \namespace activations + * \brief Various activation functions used in Neural network + */ + namespace activations { + /** + * Sigmoid function + * @param X Value + * @return Returns sigmoid(x) + */ + double sigmoid (const double &x) { + return 1.0 / (1.0 + std::exp(-x)); + } + + /** + * Derivative of sigmoid function + * @param X Value + * @return Returns derivative of sigmoid(x) + */ + double dsigmoid (const double &x) { + return x * (1 - x); + } + + /** + * Relu function + * @param X Value + * @returns relu(x) + */ + double relu (const double &x) { + return std::max(0.0, x); + } + + /** + * Derivative of relu function + * @param X Value + * @returns derivative of relu(x) + */ + double drelu (const double &x) { + return x >= 0.0 ? 1.0 : 0.0; + } + + /** + * Tanh function + * @param X Value + * @return Returns tanh(x) + */ + double tanh (const double &x) { + return 2 / (1 + std::exp(-2 * x)) - 1; + } + + /** + * Derivative of Sigmoid function + * @param X Value + * @return Returns derivative of tanh(x) + */ + double dtanh (const double &x) { + return 1 - x * x; + } + } // namespace activations + /** \namespace util_functions + * \brief Various utility functions used in Neural network + */ + namespace util_functions { + /** + * Square function + * @param X Value + * @return Returns x * x + */ + double square(const double &x) { + return x * x; + } + /** + * Identity function + * @param X Value + * @return Returns x + */ + double identity_function(const double &x) { + return x; + } + } // namespace util_functions + /** \namespace layers + * \brief This namespace contains layers used + * in MLP. + */ + namespace layers { + /** + * neural_network::layers::DenseLayer class is used to store all necessary information about + * the layers (i.e. neurons, activation and kernal). This class + * is used by NeuralNetwork class to store layers. + * + */ + class DenseLayer { + public: + // To store activation function and it's derivative + double (*activation_function)(const double &); + double (*dactivation_function)(const double &); + int neurons; // To store number of neurons (used in summary) + std::string activation; // To store activation name (used in summary) + std::vector > kernal; // To store kernal (aka weights) + + /** + * Constructor for neural_network::layers::DenseLayer class + * @param neurons number of neurons + * @param activation activation function for layer + * @param kernal_shape shape of kernal + * @param random_kernal flag for whether to intialize kernal randomly + */ + DenseLayer(const int &neurons, + const std::string &activation, + const std::pair &kernal_shape, + const bool &random_kernal) { + // Choosing activation (and it's derivative) + if (activation == "sigmoid") { + activation_function = neural_network::activations::sigmoid; + dactivation_function = neural_network::activations::sigmoid; + } + else if (activation == "relu") { + activation_function = neural_network::activations::relu; + dactivation_function = neural_network::activations::drelu; + } + else if (activation == "tanh") { + activation_function = neural_network::activations::tanh; + dactivation_function = neural_network::activations::dtanh; + } + else if (activation == "none") { + // Set identity function in casse of none is supplied + activation_function = neural_network::util_functions::identity_function; + dactivation_function = neural_network::util_functions::identity_function; + } + else { + // If supplied activation is invalid + std::cerr << "ERROR: Invalid argument for layer -> constructor -> activation, "; + std::cerr << "Expected from {none, sigmoid, relu, tanh} got "; + std::cerr << activation << std::endl; + std::exit(EXIT_FAILURE); + } + this -> activation = activation; // Setting activation name + this -> neurons = neurons; // Setting number of neurons + // Initialize kernal according to flag + if(random_kernal) { + uniform_random_initialization(kernal, kernal_shape, -1.0, 1.0); + } + else { + unit_matrix_initialization(kernal, kernal_shape); + } + } + /** + * Constructor for neural_network::layers::DenseLayer class + * @param neurons number of neurons + * @param activation activation function for layer + * @param kernal values of kernal (useful in loading model) + */ + DenseLayer (const int &neurons, + const std::string &activation, + const std::vector > &kernal) { + // Choosing activation (and it's derivative) + if (activation == "sigmoid") { + activation_function = neural_network::activations::sigmoid; + dactivation_function = neural_network::activations::sigmoid; + } + else if (activation == "relu") { + activation_function = neural_network::activations::relu; + dactivation_function = neural_network::activations::drelu; + } + else if (activation == "tanh") { + activation_function = neural_network::activations::tanh; + dactivation_function = neural_network::activations::dtanh; + } + else if (activation == "none") { + // Set identity function in casse of none is supplied + activation_function = neural_network::util_functions::identity_function; + dactivation_function = neural_network::util_functions::identity_function; + } + else { + // If supplied activation is invalid + std::cerr << "ERROR: Invalid argument for layer -> constructor -> activation, "; + std::cerr << "Expected from {none, sigmoid, relu, tanh} got "; + std::cerr << activation << std::endl; + std::exit(EXIT_FAILURE); + } + this -> activation = activation; // Setting activation name + this -> neurons = neurons; // Setting number of neurons + this -> kernal = kernal; // Setting supplied kernal values + } + + /** + * Copy Constructor for class DenseLayer. + * + * @param model instance of class to be copied. + */ + DenseLayer(const DenseLayer &layer) = default; + + /** + * Destructor for class DenseLayer. + */ + ~DenseLayer() = default; + + /** + * Copy assignment operator for class DenseLayer + */ + DenseLayer& operator = (const DenseLayer &layer) = default; + + /** + * Move constructor for class DenseLayer + */ + DenseLayer(DenseLayer &&) = default; + + /** + * Move assignment operator for class DenseLayer + */ + DenseLayer& operator = (DenseLayer &&) = default; + }; + } // namespace layers + /** + * NeuralNetwork class is implements MLP. This class is + * used by actual user to create and train networks. + * + */ + class NeuralNetwork { + private: + std::vector layers; // To store layers + /** + * Private Constructor for class NeuralNetwork. This constructor + * is used internally to load model. + * @param config vector containing pair (neurons, activation) + * @param kernals vector containing all pretrained kernals + */ + NeuralNetwork(const std::vector > &config, + const std::vector >> &kernals) { + // First layer should not have activation + if(config.begin() -> second != "none") { + std::cerr << "ERROR: First layer can't have activation other than none"; + std::cerr << std::endl; + std::exit(EXIT_FAILURE); + } + // Network should have atleast two layers + if(config.size() <= 1) { + std::cerr << "ERROR: Invalid size of network, "; + std::cerr << "Atleast two layers are required"; + std::exit(EXIT_FAILURE); + } + // Reconstructing all pretrained layers + for(size_t i = 0; i < config.size(); i++) { + layers.emplace_back(neural_network::layers::DenseLayer(config[i].first, + config[i].second, + kernals[i])); + } + std::cout << "INFO: Network constructed successfully" << std::endl; + } + /** + * Private function to get detailed predictions (i.e. + * activated neuron values). This function is used in + * backpropagation, single predict and batch predict. + * @param X input vector + */ + std::vector>> + __detailed_single_prediction (const std::vector> &X) { + std::vector >> details; + std::vector < std::valarray > current_pass = X; + details.emplace_back(X); + for(const auto &l : layers) { + current_pass = multiply(current_pass, l.kernal); + current_pass = apply_function(current_pass, l.activation_function); + details.emplace_back(current_pass); + } + return details; + } + public: + /** + * Default Constructor for class NeuralNetwork. This constructor + * is used to create empty variable of type NeuralNetwork class. + */ + NeuralNetwork() = default; + + /** + * Constructor for class NeuralNetwork. This constructor + * is used by user. + * @param config vector containing pair (neurons, activation) + */ + explicit NeuralNetwork(const std::vector > &config) { + // First layer should not have activation + if(config.begin() -> second != "none") { + std::cerr << "ERROR: First layer can't have activation other than none"; + std::cerr << std::endl; + std::exit(EXIT_FAILURE); + } + // Network should have atleast two layers + if(config.size() <= 1) { + std::cerr << "ERROR: Invalid size of network, "; + std::cerr << "Atleast two layers are required"; + std::exit(EXIT_FAILURE); + } + // Separately creating first layer so it can have unit matrix + // as kernal. + layers.push_back(neural_network::layers::DenseLayer(config[0].first, + config[0].second, + {config[0].first, config[0].first}, + false)); + // Creating remaining layers + for(size_t i = 1; i < config.size(); i++) { + layers.push_back(neural_network::layers::DenseLayer(config[i].first, + config[i].second, + {config[i - 1].first, config[i].first}, + true)); + } + std::cout << "INFO: Network constructed successfully" << std::endl; + } + + /** + * Copy Constructor for class NeuralNetwork. + * + * @param model instance of class to be copied. + */ + NeuralNetwork(const NeuralNetwork &model) = default; + + /** + * Destructor for class NeuralNetwork. + */ + ~NeuralNetwork() = default; + + /** + * Copy assignment operator for class NeuralNetwork + */ + NeuralNetwork& operator = (const NeuralNetwork &model) = default; + + /** + * Move constructor for class NeuralNetwork + */ + NeuralNetwork(NeuralNetwork &&) = default; + + /** + * Move assignment operator for class NeuralNetwork + */ + NeuralNetwork& operator = (NeuralNetwork &&) = default; + + /** + * Function to get X and Y from csv file (where X = data, Y = label) + * @param file_name csv file name + * @param last_label flag for whether label is in first or last column + * @param normalize flag for whether to normalize data + * @param slip_lines number of lines to skip + * @return returns pair of X and Y + */ + std::pair>>, std::vector>>> + get_XY_from_csv(const std::string &file_name, + const bool &last_label, + const bool &normalize, + const int &slip_lines = 1) { + std::ifstream in_file; // Ifstream to read file + in_file.open(file_name.c_str(), std::ios::in); // Open file + std::vector >> X, Y; // To store X and Y + std::string line; // To store each line + // Skip lines + for(int i = 0; i < slip_lines; i ++) { + std::getline(in_file, line, '\n'); // Ignore line + } + // While file has information + while(!in_file.eof() && std::getline(in_file, line, '\n')) + { + std::valarray x_data, y_data; // To store single sample and label + std::stringstream ss(line); // Constructing stringstream from line + std::string token; // To store each token in line (seprated by ',') + while(std::getline(ss, token, ',')) { // For each token + // Insert numerical value of token in x_data + x_data = insert_element(x_data, std::stod(token)); + } + // If label is in last column + if(last_label) { + y_data.resize(this -> layers.back().neurons); + // If task is classification + if(y_data.size() > 1) { + y_data[x_data[x_data.size() - 1]] = 1; + } + // If task is regrssion (of single value) + else { + y_data[0] = x_data[x_data.size() - 1]; + } + x_data = pop_back(x_data); // Remove label from x_data + } + else { + y_data.resize(this -> layers.back().neurons); + // If task is classification + if(y_data.size() > 1) { + y_data[x_data[x_data.size() - 1]] = 1; + } + // If task is regrssion (of single value) + else { + y_data[0] = x_data[x_data.size() - 1]; + } + x_data = pop_front(x_data); // Remove label from x_data + } + // Push collected X_data and y_data in X and Y + X.push_back({x_data}); + Y.push_back({y_data}); + } + in_file.close(); + // Normalize training data if flag is set + if(normalize) { + // Scale data between 0 and 1 using min-max scaler + X = minmax_scaler(X, 0.01, 1.0); + } + return make_pair(X, Y); // Return pair of X and Y + } + + /** + * Function to get prediction of model on single sample. + * @param X array of feature vectors + * @return returns predictions as vector + */ + std::vector> + single_predict (const std::vector> &X) { + // Get activations of all layers + auto activations = this -> __detailed_single_prediction(X); + // Return activations of last layer (actual predicted values) + return activations.back(); + } + + /** + * Function to get prediction of model on batch + * @param X array of feature vectors + * @return returns predicted values as vector + */ + std::vector < std::vector >> + batch_predict (const std::vector >> &X) { + // Store predicted values + std::vector < std::vector >> predicted_batch(X.size()); + for(size_t i = 0; i < X.size(); i++) { // For every sample + // Push predicted values + predicted_batch[i] = this -> single_predict(X[i]); + } + return predicted_batch; // Return predicted values + } + + /** + * Function to fit model on supplied data + * @param X array of feature vectors + * @param Y array of target values + * @param epochs number of epochs (default = 100) + * @param learning_rate learning rate (default = 0.01) + * @param batch_size batch size for gradient descent (default = 32) + * @param shuffle flag for whether to shuffle data (default = true) + */ + void fit(const std::vector < std::vector >> &X_, + const std::vector < std::vector >> &Y_, + const int &epochs = 100, + const double &learning_rate = 0.01, + const size_t &batch_size = 32, + const bool &shuffle = true) { + std::vector < std::vector >> X = X_, Y = Y_; + // Both label and input data should have same size + if (X.size() != Y.size()) { + std::cerr << "ERROR : X and Y in fit have different sizes" << std::endl; + std::exit(EXIT_FAILURE); + } + std::cout << "INFO: Training Started" << std::endl; + for (int epoch = 1; epoch <= epochs; epoch++) { // For every epoch + // Shuffle X and Y if flag is set + if(shuffle) { + equal_shuffle(X, Y); + } + auto start = std::chrono::high_resolution_clock::now(); // Start clock + double loss = 0, acc = 0; // Intialize performance metrics with zero + // For each starting index of batch + for(size_t batch_start = 0; batch_start < X.size(); batch_start += batch_size) { + for(size_t i = batch_start; i < std::min(X.size(), batch_start + batch_size); i++) { + std::vector > grad, cur_error, predicted; + auto activations = this -> __detailed_single_prediction(X[i]); + // Gradients vector to store gradients for all layers + // They will be averaged and applied to kernal + std::vector>> gradients; + gradients.resize(this -> layers.size()); + // First intialize gradients to zero + for(size_t i = 0; i < gradients.size(); i++) { + zeroes_initialization(gradients[i], get_shape(this -> layers[i].kernal)); + } + predicted = activations.back(); // Predicted vector + cur_error = predicted - Y[i]; // Absoulute error + // Calculating loss with MSE + loss += sum(apply_function(cur_error, neural_network::util_functions::square)); + // If prediction is correct + if(argmax(predicted) == argmax(Y[i])) { + acc += 1; + } + // For every layer (except first) starting from last one + for(size_t j = this -> layers.size() - 1; j >= 1; j--) { + // Backpropogating errors + cur_error = hadamard_product(cur_error, + apply_function(activations[j + 1], + this -> layers[j].dactivation_function)); + // Calculating gradient for current layer + grad = multiply(transpose(activations[j]), cur_error); + // Change error according to current kernal values + cur_error = multiply(cur_error, transpose(this -> layers[j].kernal)); + // Adding gradient values to collection of gradients + gradients[j] = gradients[j] + grad / double(batch_size); + } + // Applying gradients + for(size_t j = this -> layers.size() - 1; j >= 1; j--) { + // Updating kernal (aka weights) + this -> layers[j].kernal = this -> layers[j].kernal - + gradients[j] * learning_rate; + } + } + } + auto stop = std::chrono::high_resolution_clock::now(); // Stoping the clock + // Calculate time taken by epoch + auto duration = std::chrono::duration_cast(stop - start); + loss /= X.size(); // Averaging loss + acc /= X.size(); // Averaging accuracy + std::cout.precision(4); // set output precision to 4 + // Printing training stats + std::cout << "Training: Epoch " << epoch << '/' << epochs; + std::cout << ", Loss: " << loss; + std::cout << ", Accuracy: " << acc; + std::cout << ", Taken time: " << duration.count() / 1e6 << " seconds"; + std::cout << std::endl; + } + return; + } + + /** + * Function to fit model on data stored in csv file + * @param file_name csv file name + * @param last_label flag for whether label is in first or last column + * @param epochs number of epochs + * @param learning_rate learning rate + * @param normalize flag for whether to normalize data + * @param slip_lines number of lines to skip + * @param batch_size batch size for gradient descent (default = 32) + * @param shuffle flag for whether to shuffle data (default = true) + */ + void fit_from_csv (const std::string &file_name, + const bool &last_label, + const int &epochs, + const double &learning_rate, + const bool &normalize, + const int &slip_lines = 1, + const size_t &batch_size = 32, + const bool &shuffle = true) { + // Getting training data from csv file + auto data = this -> get_XY_from_csv(file_name, last_label, normalize, slip_lines); + // Fit the model on training data + this -> fit(data.first, data.second, epochs, learning_rate, batch_size, shuffle); + return; + } + + /** + * Function to evaluate model on supplied data + * @param X array of feature vectors (input data) + * @param Y array of target values (label) + */ + void evaluate(const std::vector< std::vector >> &X, + const std::vector< std::vector >> &Y) { + std::cout << "INFO: Evaluation Started" << std::endl; + double acc = 0, loss = 0; // intialize performance metrics with zero + for(size_t i = 0; i < X.size(); i++) { // For every sample in input + // Get predictions + std::vector> pred = this -> single_predict(X[i]); + // If predicted class is correct + if(argmax(pred) == argmax(Y[i])) { + acc += 1; // Increment accuracy + } + // Calculating loss - Mean Squared Error + loss += sum(apply_function((Y[i] - pred), + neural_network::util_functions::square) * 0.5); + } + acc /= X.size(); // Averaging accuracy + loss /= X.size(); // Averaging loss + // Prinitng performance of the model + std::cout << "Evaluation: Loss: " << loss; + std::cout << ", Accuracy: " << acc << std::endl; + return; + } + + /** + * Function to evaluate model on data stored in csv file + * @param file_name csv file name + * @param last_label flag for whether label is in first or last column + * @param normalize flag for whether to normalize data + * @param slip_lines number of lines to skip + */ + void evaluate_from_csv (const std::string &file_name, + const bool &last_label, + const bool &normalize, + const int &slip_lines = 1) { + // Getting training data from csv file + auto data = this -> get_XY_from_csv(file_name, last_label, normalize, slip_lines); + // Evaluating model + this -> evaluate(data.first, data.second); + return; + } + + /** + * Function to save current model. + * @param file_name file name to save model (*.model) + */ + void save_model (const std::string &_file_name) { + std::string file_name = _file_name; + // Adding ".model" extension if it is not already there in name + if(file_name.find(".model") == file_name.npos) { + file_name += ".model"; + } + std::ofstream out_file; // Ofstream to write in file + // Open file in out|trunc mode + out_file.open(file_name.c_str(), std::ofstream::out | std::ofstream::trunc); + /** + Format in which model is saved: + + total_layers + neurons(1st neural_network::layers::DenseLayer) activation_name(1st neural_network::layers::DenseLayer) + kernal_shape(1st neural_network::layers::DenseLayer) + kernal_values + . + . + . + neurons(Nth neural_network::layers::DenseLayer) activation_name(Nth neural_network::layers::DenseLayer) + kernal_shape(Nth neural_network::layers::DenseLayer) + kernal_value + + For Example, pretrained model with 3 layers: +
+                        3
+                        4 none
+                        4 4
+                        1 0 0 0 
+                        0 1 0 0 
+                        0 0 1 0 
+                        0 0 0 1 
+                        6 relu
+                        4 6
+                        -1.88963 -3.61165 1.30757 -0.443906 -2.41039 -2.69653 
+                        -0.684753 0.0891452 0.795294 -2.39619 2.73377 0.318202 
+                        -2.91451 -4.43249 -0.804187 2.51995 -6.97524 -1.07049 
+                        -0.571531 -1.81689 -1.24485 1.92264 -2.81322 1.01741 
+                        3 sigmoid
+                        6 3
+                        0.390267 -0.391703 -0.0989607 
+                        0.499234 -0.564539 -0.28097 
+                        0.553386 -0.153974 -1.92493 
+                        -2.01336 -0.0219682 1.44145 
+                        1.72853 -0.465264 -0.705373 
+                        -0.908409 -0.740547 0.376416 
+                        
+ */ + // Saving model in the same format + out_file << layers.size(); + out_file << std::endl; + for(const auto &layer : this -> layers) { + out_file << layer.neurons << ' ' << layer.activation << std::endl; + const auto shape = get_shape(layer.kernal); + out_file << shape.first << ' ' << shape.second << std::endl; + for(const auto &row : layer.kernal) { + for(const auto &val : row) { + out_file << val << ' '; + } + out_file << std::endl; + } + } + std::cout << "INFO: Model saved successfully with name : "; + std::cout << file_name << std::endl; + return; + } + + /** + * Function to load earlier saved model. + * @param file_name file from which model will be loaded (*.model) + * @return instance of NeuralNetwork class with pretrained weights + */ + NeuralNetwork load_model (const std::string &file_name) { + std::ifstream in_file; // Ifstream to read file + in_file.open(file_name.c_str()); // Openinig file + std::vector > config; // To store config + std::vector >> kernals; // To store pretrained kernals + // Loading model from saved file format + size_t total_layers = 0; + in_file >> total_layers; + for(size_t i = 0; i < total_layers; i++) { + int neurons = 0; + std::string activation; + size_t shape_a = 0, shape_b = 0; + std::vector> kernal; + in_file >> neurons >> activation >> shape_a >> shape_b; + for(size_t r = 0; r < shape_a; r++) { + std::valarray row(shape_b); + for(size_t c = 0; c < shape_b; c++) { + in_file >> row[c]; + } + kernal.push_back(row); + } + config.emplace_back(make_pair(neurons, activation));; + kernals.emplace_back(kernal); + } + std::cout << "INFO: Model loaded successfully" << std::endl; + return NeuralNetwork(config, kernals); // Return instance of NeuralNetwork class + } + + /** + * Function to print summary of the network. + */ + void summary () { + // Printing Summary + std::cout << "===============================================================" << std::endl; + std::cout << "\t\t+ MODEL SUMMARY +\t\t\n"; + std::cout << "===============================================================" << std::endl; + for(size_t i = 1; i <= layers.size(); i++) { // For every layer + std::cout << i << ")"; + std::cout << " Neurons : " << layers[i - 1].neurons; // number of neurons + std::cout << ", Activation : " << layers[i - 1].activation; // activation + std::cout << ", Kernal Shape : " << get_shape(layers[i - 1].kernal); // kernal shape + std::cout << std::endl; + } + std::cout << "===============================================================" << std::endl; + return; + } + + }; + } // namespace neural_network +} // namespace machine_learning + +/** + * Function to test neural network + * @returns none + */ +static void test() { + // Creating network with 3 layers for "iris.csv" + machine_learning::neural_network::NeuralNetwork myNN = + machine_learning::neural_network::NeuralNetwork({ + {4, "none"}, // First layer with 3 neurons and "none" as activation + {6, "relu"}, // Second layer with 6 neurons and "relu" as activation + {3, "sigmoid"} // Third layer with 3 neurons and "sigmoid" as activation + }); + // Printing summary of model + myNN.summary(); + // Training Model + myNN.fit_from_csv("iris.csv", true, 100, 0.3, false, 2, 32, true); + // Testing predictions of model + assert(machine_learning::argmax(myNN.single_predict({{5,3.4,1.6,0.4}})) == 0); + assert(machine_learning::argmax(myNN.single_predict({{6.4,2.9,4.3,1.3}})) == 1); + assert(machine_learning::argmax(myNN.single_predict({{6.2,3.4,5.4,2.3}})) == 2); + return; +} + +/** Driver Code */ +int main() { + // Testing + test(); + return 0; +} diff --git a/machine_learning/vector_ops.hpp b/machine_learning/vector_ops.hpp new file mode 100644 index 000000000..bb70b5c4f --- /dev/null +++ b/machine_learning/vector_ops.hpp @@ -0,0 +1,484 @@ +/** + * @file vector_ops.hpp + * @author [Deep Raval](https://github.com/imdeep2905) + * + * @brief Various functions for vectors associated with [NeuralNetwork (aka Multilayer Perceptron)] + * (https://en.wikipedia.org/wiki/Multilayer_perceptron). + * + */ +#ifndef VECTOR_OPS_FOR_NN +#define VECTOR_OPS_FOR_NN + +#include +#include +#include +#include +#include +#include + +/** + * @namespace machine_learning + * @brief Machine Learning algorithms + */ +namespace machine_learning { +/** + * Overloaded operator "<<" to print 2D vector + * @tparam T typename of the vector + * @param out std::ostream to output + * @param A 2D vector to be printed + */ +template +std::ostream &operator<<(std::ostream &out, + std::vector> const &A) { + // Setting output precision to 4 in case of floating point numbers + out.precision(4); + for(const auto &a : A) { // For each row in A + for(const auto &x : a) { // For each element in row + std::cerr << x << ' '; // print element + } + std::cerr << std::endl; + } + return out; +} + +/** + * Overloaded operator "<<" to print a pair + * @tparam T typename of the pair + * @param out std::ostream to output + * @param A Pair to be printed + */ +template +std::ostream &operator<<(std::ostream &out, const std::pair &A) { + // Setting output precision to 4 in case of floating point numbers + out.precision(4); + // printing pair in the form (p, q) + std::cerr << "(" << A.first << ", " << A.second << ")"; + return out; +} + +/** + * Overloaded operator "<<" to print a 1D vector + * @tparam T typename of the vector + * @param out std::ostream to output + * @param A 1D vector to be printed + */ +template +std::ostream &operator<<(std::ostream &out, const std::valarray &A) { + // Setting output precision to 4 in case of floating point numbers + out.precision(4); + for(const auto &a : A) { // For every element in the vector. + std::cerr << a << ' '; // Print element + } + std::cerr << std::endl; + return out; +} + +/** + * Function to insert element into 1D vector + * @tparam T typename of the 1D vector and the element + * @param A 1D vector in which element will to be inserted + * @param ele element to be inserted + * @return new resultant vector + */ +template +std::valarray insert_element(const std::valarray &A, const T &ele) { + std::valarray B; // New 1D vector to store resultant vector + B.resize(A.size() + 1); // Resizing it accordingly + for(size_t i = 0; i < A.size(); i++) { // For every element in A + B[i] = A[i]; // Copy element in B + } + B[B.size() - 1] = ele; // Inserting new element in last position + return B; // Return resultant vector +} + +/** + * Function to remove first element from 1D vector + * @tparam T typename of the vector + * @param A 1D vector from which first element will be removed + * @return new resultant vector + */ +template +std::valarray pop_front(const std::valarray &A) { + std::valarray B; // New 1D vector to store resultant vector + B.resize(A.size() - 1); // Resizing it accordingly + for(size_t i = 1; i < A.size(); i ++) { // // For every (except first) element in A + B[i - 1] = A[i]; // Copy element in B with left shifted position + } + return B; // Return resultant vector +} + +/** + * Function to remove last element from 1D vector + * @tparam T typename of the vector + * @param A 1D vector from which last element will be removed + * @return new resultant vector + */ +template +std::valarray pop_back(const std::valarray &A) { + std::valarray B; // New 1D vector to store resultant vector + B.resize(A.size() - 1); // Resizing it accordingly + for(size_t i = 0; i < A.size() - 1; i ++) { // For every (except last) element in A + B[i] = A[i]; // Copy element in B + } + return B; // Return resultant vector +} + +/** + * Function to equally shuffle two 3D vectors (used for shuffling training data) + * @tparam T typename of the vector + * @param A First 3D vector + * @param B Second 3D vector + */ +template +void equal_shuffle(std::vector < std::vector > > &A, + std::vector < std::vector > > &B) { + // If two vectors have different sizes + if(A.size() != B.size()) + { + std::cerr << "ERROR : Can not equally shuffle two vectors with different sizes: "; + std::cerr << A.size() << " and " << B.size() << std::endl; + std::exit(EXIT_FAILURE); + } + for(size_t i = 0; i < A.size(); i++) { // For every element in A and B + // Genrating random index < size of A and B + std::srand(std::chrono::system_clock::now().time_since_epoch().count()); + size_t random_index = std::rand() % A.size(); + // Swap elements in both A and B with same random index + std::swap(A[i], A[random_index]); + std::swap(B[i], B[random_index]); + } + return; +} + +/** + * Function to initialize given 2D vector using uniform random initialization + * @tparam T typename of the vector + * @param A 2D vector to be initialized + * @param shape required shape + * @param low lower limit on value + * @param high upper limit on value + */ +template +void uniform_random_initialization(std::vector> &A, + const std::pair &shape, + const T &low, + const T &high) { + A.clear(); // Making A empty + // Uniform distribution in range [low, high] + std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count()); + std::uniform_real_distribution distribution(low, high); + for(size_t i = 0; i < shape.first; i++) { // For every row + std::valarray row; // Making empty row which will be inserted in vector + row.resize(shape.second); + for(auto &r : row) { // For every element in row + r = distribution(generator); // copy random number + } + A.push_back(row); // Insert new row in vector + } + return; +} + + +/** + * Function to Intialize 2D vector as unit matrix + * @tparam T typename of the vector + * @param A 2D vector to be initialized + * @param shape required shape + */ +template +void unit_matrix_initialization(std::vector> &A, + const std::pair &shape + ) { + A.clear(); // Making A empty + for(size_t i = 0; i < shape.first; i++) { + std::valarray row; // Making empty row which will be inserted in vector + row.resize(shape.second); + row[i] = T(1); // Insert 1 at ith position + A.push_back(row); // Insert new row in vector + } + return; +} + +/** + * Function to Intialize 2D vector as zeroes + * @tparam T typename of the vector + * @param A 2D vector to be initialized + * @param shape required shape + */ +template +void zeroes_initialization(std::vector> &A, + const std::pair &shape + ) { + A.clear(); // Making A empty + for(size_t i = 0; i < shape.first; i++) { + std::valarray row; // Making empty row which will be inserted in vector + row.resize(shape.second); // By default all elements are zero + A.push_back(row); // Insert new row in vector + } + return; +} + +/** + * Function to get sum of all elements in 2D vector + * @tparam T typename of the vector + * @param A 2D vector for which sum is required + * @return returns sum of all elements of 2D vector + */ +template +T sum(const std::vector> &A) { + T cur_sum = 0; // Initially sum is zero + for(const auto &a : A) { // For every row in A + cur_sum += a.sum(); // Add sum of that row to current sum + } + return cur_sum; // Return sum +} + +/** + * Function to get shape of given 2D vector + * @tparam T typename of the vector + * @param A 2D vector for which shape is required + * @return shape as pair + */ +template +std::pair get_shape(const std::vector> &A) { + const size_t sub_size = (*A.begin()).size(); + for(const auto &a : A) { + // If supplied vector don't have same shape in all rows + if(a.size() != sub_size) { + std::cerr << "ERROR: (get_shape) Supplied vector is not 2D Matrix" << std::endl; + std::exit(EXIT_FAILURE); + } + } + return std::make_pair(A.size(), sub_size); // Return shape as pair +} + +/** + * Function to scale given 3D vector using min-max scaler + * @tparam T typename of the vector + * @param A 3D vector which will be scaled + * @param low new minimum value + * @param high new maximum value + * @return new scaled 3D vector + */ +template +std::vector>> +minmax_scaler(const std::vector>> &A, const T &low, const T &high) { + std::vector>> B = A; // Copying into new vector B + const auto shape = get_shape(B[0]); // Storing shape of B's every element + // As this function is used for scaling training data vector should be of shape (1, X) + if(shape.first != 1) { + std::cerr << "ERROR: (MinMax Scaling) Supplied vector is not supported for minmax scaling, shape: "; + std::cerr << shape << std::endl; + std::exit(EXIT_FAILURE); + } + for(size_t i = 0; i < shape.second; i++) { + T min = B[0][0][i], max = B[0][0][i]; + for(size_t j = 0; j < B.size(); j++) { + // Updating minimum and maximum values + min = std::min(min, B[j][0][i]); + max = std::max(max, B[j][0][i]); + } + for(size_t j = 0; j < B.size(); j++) { + // Applying min-max scaler formula + B[j][0][i] = ((B[j][0][i] - min) / (max - min)) * (high - low) + low; + } + } + return B; // Return new resultant 3D vector +} + +/** + * Function to get index of maximum element in 2D vector + * @tparam T typename of the vector + * @param A 2D vector for which maximum index is required + * @return index of maximum element + */ +template +size_t argmax(const std::vector> &A) { + const auto shape = get_shape(A); + // As this function is used on predicted (or target) vector, shape should be (1, X) + if(shape.first != 1) { + std::cerr << "ERROR: (argmax) Supplied vector is ineligible for argmax" << std::endl; + std::exit(EXIT_FAILURE); + } + // Return distance of max element from first element (i.e. index) + return std::distance(std::begin(A[0]), std::max_element(std::begin(A[0]), std::end(A[0]))); +} + +/** + * Function which applys supplied function to every element of 2D vector + * @tparam T typename of the vector + * @param A 2D vector on which function will be applied + * @param func Function to be applied + * @return new resultant vector + */ +template +std::vector > apply_function(const std::vector > &A, + T (*func) (const T &)) { + std::vector> B = A; // New vector to store resultant vector + for(auto &b : B) { // For every row in vector + b = b.apply(func); // Apply function to that row + } + return B; // Return new resultant 2D vector +} + +/** + * Overloaded operator "*" to multiply given 2D vector with scaler + * @tparam T typename of both vector and the scaler + * @param A 2D vector to which scaler will be multiplied + * @param val Scaler value which will be multiplied + * @return new resultant vector + */ +template +std::vector > operator * (const std::vector> &A, const T& val) { + std::vector> B = A; // New vector to store resultant vector + for(auto &b : B) { // For every row in vector + b = b * val; // Multiply row with scaler + } + return B; // Return new resultant 2D vector +} + +/** + * Overloaded operator "/" to divide given 2D vector with scaler + * @tparam T typename of the vector and the scaler + * @param A 2D vector to which scaler will be divided + * @param val Scaler value which will be divided + * @return new resultant vector + */ +template +std::vector > operator / (const std::vector> &A, const T& val) { + std::vector> B = A; // New vector to store resultant vector + for(auto &b : B) { // For every row in vector + b = b / val; // Divide row with scaler + } + return B; // Return new resultant 2D vector +} + +/** + * Function to get transpose of 2D vector + * @tparam T typename of the vector + * @param A 2D vector which will be transposed + * @return new resultant vector + */ +template +std::vector > transpose(const std::vector> &A) { + const auto shape = get_shape(A); // Current shape of vector + std::vector > B; // New vector to store result + // Storing transpose values of A in B + for(size_t j = 0; j < shape.second; j++) { + std::valarray row; + row.resize(shape.first); + for(size_t i = 0; i < shape.first; i++) { + row[i] = A[i][j]; + } + B.push_back(row); + } + return B; // Return new resultant 2D vector +} + +/** + * Overloaded operator "+" to add two 2D vectors + * @tparam T typename of the vector + * @param A First 2D vector + * @param B Second 2D vector + * @return new resultant vector + */ +template +std::vector > operator + (const std::vector> &A, const std::vector> &B) { + const auto shape_a = get_shape(A); + const auto shape_b = get_shape(B); + // If vectors don't have equal shape + if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) { + std::cerr << "ERROR: (vector addition) Supplied vectors have different shapes "; + std::cerr << shape_a << " and " << shape_b << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector> C; + for(size_t i = 0; i < A.size(); i++) { // For every row + C.push_back(A[i] + B[i]); // Elementwise addition + } + return C; // Return new resultant 2D vector +} + +/** + * Overloaded operator "-" to add subtract 2D vectors + * @tparam T typename of the vector + * @param A First 2D vector + * @param B Second 2D vector + * @return new resultant vector + */ +template +std::vector > operator - (const std::vector> &A, const std::vector> &B) { + const auto shape_a = get_shape(A); + const auto shape_b = get_shape(B); + // If vectors don't have equal shape + if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) { + std::cerr << "ERROR: (vector subtraction) Supplied vectors have different shapes "; + std::cerr << shape_a << " and " << shape_b << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector> C; // Vector to store result + for(size_t i = 0; i < A.size(); i++) { // For every row + C.push_back(A[i] - B[i]); // Elementwise substraction + } + return C; // Return new resultant 2D vector +} + +/** + * Function to multiply two 2D vectors + * @tparam T typename of the vector + * @param A First 2D vector + * @param B Second 2D vector + * @return new resultant vector + */ +template +std::vector > multiply(const std::vector> &A, const std::vector> &B) { + const auto shape_a = get_shape(A); + const auto shape_b = get_shape(B); + // If vectors are not eligible for multiplication + if(shape_a.second != shape_b.first ) { + std::cerr << "ERROR: (multiply) Supplied vectors are not eligible for multiplication "; + std::cerr << shape_a << " and " << shape_b << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector> C; // Vector to store result + // Normal matrix multiplication + for (size_t i = 0; i < shape_a.first; i++) { + std::valarray row; + row.resize(shape_b.second); + for(size_t j = 0; j < shape_b.second; j++) { + for(size_t k = 0; k < shape_a.second; k++) { + row[j] += A[i][k] * B[k][j]; + } + } + C.push_back(row); + } + return C; // Return new resultant 2D vector +} + +/** + * Function to get hadamard product of two 2D vectors + * @tparam T typename of the vector + * @param A First 2D vector + * @param B Second 2D vector + * @return new resultant vector + */ +template +std::vector > hadamard_product(const std::vector> &A, const std::vector> &B) { + const auto shape_a = get_shape(A); + const auto shape_b = get_shape(B); + // If vectors are not eligible for hadamard product + if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) { + std::cerr << "ERROR: (hadamard_product) Supplied vectors have different shapes "; + std::cerr << shape_a << " and " << shape_b << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector> C; // Vector to store result + for(size_t i = 0; i < A.size(); i++) { + C.push_back(A[i] * B[i]); // Elementwise multiplication + } + return C; // Return new resultant 2D vector +} +} // namespace machine_learning + + +#endif From 416a3bc169b123225cdd1ed150d6ab7279438a87 Mon Sep 17 00:00:00 2001 From: Aniruthan R <67466816+aneee004@users.noreply.github.com> Date: Thu, 20 Aug 2020 01:10:46 +0530 Subject: [PATCH 33/90] Added heavy_light_decomposition.cpp (#1016) * Added heavy_light_decomposition.cpp * Update range_queries/heavy_light_decomposition.cpp Co-authored-by: David Leal * Update range_queries/heavy_light_decomposition.cpp Co-authored-by: David Leal * Update range_queries/heavy_light_decomposition.cpp Co-authored-by: David Leal * Added function documentation TODO: Data member documentation. * Update heavy_light_decomposition.cpp * Update heavy_light_decomposition.cpp * Corrected typos, and remove comma * Corrected typos, and removed comma * Edited comments * Update range_queries/heavy_light_decomposition.cpp Co-authored-by: David Leal * Added wiki * Create c-cpp.yml * updating DIRECTORY.md * Delete c-cpp.yml * Added namespace, Removed quoted @params * updating DIRECTORY.md * clang-tidy fixes for 25f16d74838e15cde7171febc768c9132819de7a * Update heavy_light_decomposition.cpp * Update heavy_light_decomposition.cpp * Apply suggestions from code review Co-authored-by: David Leal * Edited Comments * clang-tidy fixes for 07b46bbd9dda96bfb9346dccad13ace187b72533 * Apply suggestions from code review Co-authored-by: David Leal * Fixed markdown * Apply suggestions from code review Co-authored-by: David Leal * Edited comments * Update heavy_light_decomposition.cpp * Update heavy_light_decomposition.cpp * Apply suggestions from code review Co-authored-by: David Leal * Apply suggestions from code review Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Added data member documentation * Formatted Code * Edited data member comments * Update range_queries/heavy_light_decomposition.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update heavy_light_decomposition.cpp * Added testcase verification * Update heavy_light_decomposition.cpp * Added a new test case Co-authored-by: David Leal Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> --- DIRECTORY.md | 1 + range_queries/heavy_light_decomposition.cpp | 639 ++++++++++++++++++++ 2 files changed, 640 insertions(+) create mode 100644 range_queries/heavy_light_decomposition.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 681b0c3ba..605c4a1fe 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -204,6 +204,7 @@ ## Range Queries * [Fenwick Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/fenwick_tree.cpp) + * [Heavy Light Decomposition](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/heavy_light_decomposition.cpp) * [Mo](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/mo.cpp) * [Segtree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/segtree.cpp) diff --git a/range_queries/heavy_light_decomposition.cpp b/range_queries/heavy_light_decomposition.cpp new file mode 100644 index 000000000..b535a9808 --- /dev/null +++ b/range_queries/heavy_light_decomposition.cpp @@ -0,0 +1,639 @@ +/** + * @file + * @brief [Heavy Light + * Decomposition](https://en.wikipedia.org/wiki/Heavy_path_decomposition) + * implementation + * @author [Aniruthan R](https://github.com/aneee004) + * + * @details + * Heavy-Light Decomposition is a technique on trees, that supports the + * following: + * 1. Update node s, with a value v + * 2. Return the (sum) of all node values on the simple path from a to b + * (sum) can also be replced with XOR, OR, AND, min, or max + * + * The update is done in O(log n) time, and + * the query is done in O(log^2 n) time with HLD + * where, n is the number of nodes + * + * The template type is the data type of the value stored in the nodes. + * If a non-primitive data-type is used as a template, + * the coressponding operators must be overloaded. + * + * An HLD object can only be created with a constant number of nodes, and + * it cannot be changed later. Creaty an empty instance is not supported. + * + * To start answering updates and queries, + * 1. Create an instance of HLD object (obj), with the required data type. + * 2. Read in the edge/parent information and update it with obj.add_edge(). + * Note: The edges addes must be 0 indexed. + * 3. Create a vector with initial node values, and call set_node_val() with it. + * 4. Call obj.init() to populate the required information for supporting + * operations. + * 5. Call obj.update(node, new_val), to update the value at index 'node' to the + * new value. Note: node must be 0 indexed + * 6. Call obj.query(a, b) to get the (sum) of node values in the simple path + * from a to b. Note: a and b, must be 0 indexed. + * + * Sample I/O at the bottom. + * @todo Support edge weight queries, by storing the edge weight value in it's + * child algorithm verified by testing in CSES path queries: + * https://cses.fi/problemset/task/1138 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @namespace range_queries + * @brief Algorithms and Data Structures that support range queries and updates. + */ +namespace range_queries { +/** + * @namespace heavy_light_decomposition + * @brief Heavy light decomposition algorithm + */ +namespace heavy_light_decomposition { +/** + * @brief A Basic Tree, which supports binary lifting + * @tparam the data type of the values stored in the tree nodes + * @details Deleting the default constructor + * An instance can only be created with the number of nodes + * Defaults: + * t_node indexing are zero based + * t_root is 0 + * depth of root_node is 0 + * Supports: + * lift :- lift a node k units up the tree + * kth_ancestor :- returns the kth ancestor + * lca :- returns the least common ancestor + */ +template class Tree { + // + +private: + std::vector> + t_adj; ///< an adjacency list to stores the tree edges + const int t_nodes, ///< number of nodes + t_maxlift; ///< maximum possible height of the tree + std::vector> + t_par; ///< a matrix to store every node's 2^kth parent + std::vector t_depth, ///< a vector to store the depth of a node, + t_size; ///< a vector to store the subtree size rooted at node + + int t_root; ///< the root of the tree + std::vector t_val; ///< values of nodes + template friend class HLD; + + /** + * @brief Utility function to compute sub-tree sizes + * @param u current dfs node + * @param p the parent of node @param u + * @returns void + */ + void dfs_size(int u, int p = -1) { + for (const int &v : t_adj[u]) { + if (v ^ p) { + dfs_size(v, u); + t_size[u] += t_size[v]; + } + } + } + + /** + * @brief Utility function to populate the t_par vector + * @param u current dfs node + * @param p the parent of node u + * @returns void + */ + void dfs_lca(int u, int p = -1) { + t_par[u][0] = p; + if (p != -1) { + t_depth[u] = 1 + t_depth[p]; + } + for (int k = 1; k < t_maxlift; k++) { + if (t_par[u][k - 1] != -1) { + t_par[u][k] = t_par[t_par[u][k - 1]][k - 1]; + } + } + + for (const int &v : t_adj[u]) { + if (v ^ p) { + dfs_lca(v, u); + } + } + } + +public: + /** + * @brief Class parameterized constructor, resizes the and initializes the + * data members + * @param nodes the total number of nodes in the tree + */ + explicit Tree(int nodes) + : t_nodes(nodes), t_maxlift(static_cast(floor(log2(nodes))) + 1) { + /* Initialize and resize all the vectors */ + t_root = 0; /* Default */ + t_adj.resize(t_nodes); + t_par.assign(t_nodes, std::vector(t_maxlift, -1)); + t_depth.assign(t_nodes, 0); + t_size.assign(t_nodes, 1); + t_val.resize(t_nodes); + } + + /** + * @brief Adds an undirected edge from node u to node v in the tree + * @param u the node where the edge is from + * @param v the node where the edge is to + * @returns void + */ + void add_edge(const int u, const int v) { + t_adj[u].push_back(v); + t_adj[v].push_back(u); + } + + /** + * @brief Set the root for the tree + * @param new_root the new root + * @returns void + */ + void change_root(int new_root) { t_root = new_root; } + + /** + * @brief Set the values for all the nodes + * @param node_val a vector of size n, with all the node values where, n is + * the number of nodes + * @returns void + */ + void set_node_val(const std::vector &node_val) { + assert(static_cast(node_val.size()) == t_nodes); + t_val = node_val; + } + + /** + * @brief This function must be called after the tree adjacency list and node + * values are populated The function initializes the required parameters, and + * populates the segment tree + * @returns void + */ + void init() { + assert(t_nodes > 0); + dfs_size(t_root); + dfs_lca(t_root); + } + + /** + * @brief The function lifts a node, k units up the tree. + * The lifting is done in place, and the result is stored in the address + * pointed by p. + * @param p a pointer to the variable that stores the node id + * @param dist the distance to move up the tree + * @returns void + */ + void lift(int *const p, int dist) { + for (int k = 0; k < t_maxlift; k++) { + if (*p == -1) { + return; + } + if (dist & 1) { + *p = t_par[*p][k]; + } + dist >>= 1; + } + } + + /** + * @brief The function returns the kth ancestor of a node + * @param p the node id whose kth ancestor is to be found + * @param dist the distance to move up the tree + * @returns the kth ancestor of node + */ + int kth_ancestor(int p, const int &dist) { + lift(&p, dist); + return p; + } + + /** + * @brief The function returns the least common ancestor of two nodes + * @param a node id_1 + * @param b node id_2 + * @returns the least common ancestor of node a, and node b + */ + int lca(int a, int b) { + assert(a >= 0 and b >= 0 and a < t_nodes and b < t_nodes); + if (t_depth[a] > t_depth[b]) { + lift(&a, t_depth[a] - t_depth[b]); + } + if (t_depth[b] > t_depth[a]) { + lift(&b, t_depth[b] - t_depth[a]); + } + if (a == b) { + return a; + } + for (int k = t_maxlift - 1; k >= 0; k--) { + if (t_par[a][k] != t_par[b][k]) { + a = t_par[a][k]; + b = t_par[b][k]; + } + } + return t_par[a][0]; + } +}; + +/** + * @brief Segment Tree, to store heavy chains + * @tparam the data type of the values stored in the tree nodes + */ +template class SG { +private: + /** + * @brief Everything here is private, + * and can only be accessed through the methods, + * in the derived class (HLD) + */ + + std::vector s_tree; ///< the segment tree, stored as a vector + int s_size; ///< number of leaves in the segment tree + X sret_init = 0; ///< inital query return value + template friend class HLD; + + /** + * @brief Function that specifies the type of operation involved when segments + * are combined + * @param lhs the left segment + * @param rhs the right segment + * @returns the combined result + */ + X combine(X lhs, X rhs) { return lhs + rhs; } + + /** + * @brief Class parameterized constructor. Resizes the and initilizes the data + * members. + * @param nodes the total number of nodes in the tree + * @returns void + */ + explicit SG(int size) { + s_size = size; + s_tree.assign(2 * s_size, 0ll); + } + + /** + * @brief Update the value at a node + * @param p the node to be udpated + * @param v the update value + * @returns void + */ + void update(int p, X v) { + for (p += s_size; p > 0; p >>= 1) { + s_tree[p] += v; + } + } + + /** + * @brief Make a range query from node label l to node label r + * @param l node label where the path starts + * @param r node label where the path ends + * @returns void + */ + X query(int l, int r) { + X lhs = sret_init, rhs = sret_init; + for (l += s_size, r += s_size + 1; l < r; l >>= 1, r >>= 1) { + if (l & 1) { + lhs = combine(lhs, s_tree[l++]); + } + if (r & 1) { + rhs = combine(s_tree[--r], rhs); + } + } + return combine(lhs, rhs); + } + + /** + * @brief Set the initialization for the query data type, based on requirement + * + * @details + * Change the sret_init, based on requirement: + * * Sum Query: 0 (Default) + * * XOR Query: 0 (Default) + * * Min Query: Infinity + * * Max Query: -Infinity + * @param new_sret_init the new init + */ + void set_sret_init(X new_sret_init) { sret_init = new_sret_init; } +}; + +/** + * @brief The Heavy-Light Decomposition class + * @tparam the data type of the values stored in the tree nodes + */ +template class HLD : public Tree, public SG { +private: + int label; ///< utility member to assign labels in dfs_labels() + std::vector h_label, ///< stores the label of a node + h_heavychlid, ///< stores the heavy child of a node + h_parent; ///< stores the top of the heavy chain from a node + + /** + * @brief Utility function to assign heavy child to each node (-1 for a leaf + * node) + * @param u current dfs node + * @param p the parent of node u + * @returns void + */ + void dfs_hc(int u, int p = -1) { + int hc_size = -1, hc_id = -1; + for (const int &v : Tree::t_adj[u]) { + if (v ^ p) { + dfs_hc(v, u); + if (Tree::t_size[v] > hc_size) { + hc_size = Tree::t_size[v]; + hc_id = v; + } + } + } + h_heavychlid[u] = hc_id; + } + + /** + * @brief Utility function to assign highest parent that can be reached though + * heavy chains + * @param u current dfs node + * @param p the parent of node u + * @returns void + */ + void dfs_par(int u, int p = -1) { + if (h_heavychlid[u] != -1) { + h_parent[h_heavychlid[u]] = h_parent[u]; + dfs_par(h_heavychlid[u], u); + } + for (const int &v : Tree::t_adj[u]) { + if (v ^ p and v ^ h_heavychlid[u]) { + dfs_par(v, u); + } + } + } + + /** + * @brief Utility function to lable the nodes so that heavy chains have a + * contigous lable + * @param u current dfs node + * @param p the parent of node u + * @returns void + */ + void dfs_labels(int u, int p = -1) { + h_label[u] = label++; + if (h_heavychlid[u] != -1) { + dfs_labels(h_heavychlid[u], u); + } + for (const int &v : Tree::t_adj[u]) { + if (v ^ p and v ^ h_heavychlid[u]) { + dfs_labels(v, u); + } + } + } + + /** + * @brief Utility function to break down a path query into two chain queries + * @param a node where the path starts + * @param b node where the path ends + * a and b must belong to a single root to leaf chain + * @returns the sum of ndoe values in the simple path from a to b + */ + X chain_query(int a, int b) { + X ret = SG::sret_init; + if (Tree::t_depth[a] < Tree::t_depth[b]) { + std::swap(a, b); + } + while (Tree::t_depth[a] >= Tree::t_depth[b]) { + int l = h_label[h_parent[a]]; + int r = h_label[a]; + if (Tree::t_depth[h_parent[a]] < Tree::t_depth[b]) { + l += Tree::t_depth[b] - Tree::t_depth[h_parent[a]]; + } + ret = SG::combine(ret, SG::query(l, r)); + a = Tree::t_par[h_parent[a]][0]; + if (a == -1) { + break; + } + } + return ret; + } + +public: + /** + * @brief Class parameterized constructor. Resizes the and initilizes the data + * members. + * @param nodes the total number of nodes in the tree + */ + explicit HLD(int nodes) : Tree(nodes), SG(nodes) { + /* Initialization and resize vectors */ + label = 0; + h_label.assign(Tree::t_nodes, -1); + h_heavychlid.assign(Tree::t_nodes, -1); + h_parent.resize(Tree::t_nodes); + iota(h_parent.begin(), h_parent.end(), 0); + } + + /** + * @brief This function must be called after the tree adjacency list and node + * values are populated The function initializes the required parametes, and + * populates the segment tree + * @returns void + */ + void init() { + Tree::init(); + + // Fill the heavy child, greatest parent, and labels + label = 0; + dfs_hc(Tree::t_root); + dfs_par(Tree::t_root); + dfs_labels(Tree::t_root); + + // Segment Tree Initialization + for (int i = 0; i < Tree::t_nodes; i++) { + SG::s_tree[h_label[i] + Tree::t_nodes] = Tree::t_val[i]; + } + for (int i = Tree::t_nodes - 1; i > 0; i--) { + SG::s_tree[i] = + SG::combine(SG::s_tree[i << 1], SG::s_tree[i << 1 | 1]); + } + } + + /** + * @brief This function updates the value at node with val + * @param node the node where the update is done + * @param val the value that is being updated + * @returns void + */ + void update(int node, X val) { + X diff = val - Tree::t_val[node]; + SG::update(h_label[node], diff); + Tree::t_val[node] = val; + } + + /** + * @brief This function returns the sum of node values in the simple path from + * from node_1 to node_2 + * @param a the node where the simple path starts + * @param b the node where the simple path ends + * (parameters are interchangeable, i.e., the function is commutative) + * @returns the sum of node values in the simple path from a to b + */ + X query(int a, int b) { + int lc = Tree::lca(a, b); + X ret = SG::sret_init; + assert(lc != -1); + ret += chain_query(a, lc); + ret += chain_query(b, lc); + return ret - Tree::t_val[lc]; + } +}; +} // namespace heavy_light_decomposition +} // namespace range_queries + +/** + * Test implementations + * @returns none + */ +static void test_1() { + std::cout << "Test 1:\n"; + + // Test details + int n = 5; + std::vector node_values = {4, 2, 5, 2, 1}; + std::vector> edges = {{1, 2}, {1, 3}, {3, 4}, {3, 5}}; + std::vector> queries = { + {2, 1, 4}, + {1, 3, 2}, + {2, 1, 4}, + }; + std::vector expected_result = {11, 8}; + std::vector code_result; + + range_queries::heavy_light_decomposition::HLD hld(n); + hld.set_node_val(node_values); + for (int i = 0; i < n - 1; i++) { + int u = edges[i][0], v = edges[i][1]; + hld.add_edge(u - 1, v - 1); + } + hld.init(); + for (const auto &q : queries) { + int type = q[0]; + if (type == 1) { + int p = q[1], x = q[2]; + hld.update(p - 1, x); + } else if (type == 2) { + int a = q[1], b = q[2]; + code_result.push_back(hld.query(a - 1, b - 1)); + } else { + continue; + } + } + for (int i = 0; i < static_cast(expected_result.size()); i++) { + assert(expected_result[i] == code_result[i]); + } + std::cout << "\nTest 1 passed!\n"; +} + +/** + * Second test implementations + * @returns void + */ +static void test_2() { + std::cout << "Test 2:\n"; + + // Test details (Bamboo) + int n = 10; + std::vector node_values = {1, 8, 6, 8, 6, 2, 9, 2, 3, 2}; + std::vector> edges = { + {10, 5}, {6, 2}, {10, 7}, {5, 2}, {3, 9}, {8, 3}, {1, 4}, {6, 4}, {8, 7}}; + std::vector> queries = { + {2, 1, 10}, {2, 1, 6}, {1, 3, 4}, {2, 1, 9}, {1, 5, 3}, + {1, 7, 8}, {2, 1, 4}, {2, 1, 8}, {1, 1, 4}, {1, 2, 7}}; + std::vector expected_result = {27, 11, 45, 9, 34}; + std::vector code_result; + + range_queries::heavy_light_decomposition::HLD hld(n); + hld.set_node_val(node_values); + for (int i = 0; i < n - 1; i++) { + int u = edges[i][0], v = edges[i][1]; + hld.add_edge(u - 1, v - 1); + } + hld.init(); + for (const auto &q : queries) { + int type = q[0]; + if (type == 1) { + int p = q[1], x = q[2]; + hld.update(p - 1, x); + } else if (type == 2) { + int a = q[1], b = q[2]; + code_result.push_back(hld.query(a - 1, b - 1)); + } else { + continue; + } + } + for (int i = 0; i < static_cast(expected_result.size()); i++) { + assert(expected_result[i] == code_result[i]); + } + std::cout << "\nTest2 passed!\n"; +} + +/** + * Third test implementations + * @returns void + */ +static void test_3() { + std::cout << "Test 3:\n"; + + // Test details + int n = 8; + std::vector node_values = {1, 8, 6, 8, 6, 2, 9, 2}; + std::vector> edges = {{1, 2}, {2, 3}, {3, 4}, {1, 5}, + {6, 3}, {7, 5}, {8, 7}}; + std::vector> queries = { + {2, 6, 8}, {2, 3, 6}, {1, 3, 4}, {2, 7, 1}, {1, 5, 3}, + {1, 7, 8}, {2, 6, 4}, {2, 7, 8}, {1, 1, 4}, {1, 2, 7}}; + std::vector expected_result = {34, 8, 16, 14, 10}; + std::vector code_result; + + range_queries::heavy_light_decomposition::HLD hld(n); + hld.set_node_val(node_values); + for (int i = 0; i < n - 1; i++) { + int u = edges[i][0], v = edges[i][1]; + hld.add_edge(u - 1, v - 1); + } + hld.init(); + for (const auto &q : queries) { + int type = q[0]; + if (type == 1) { + int p = q[1], x = q[2]; + hld.update(p - 1, x); + } else if (type == 2) { + int a = q[1], b = q[2]; + code_result.push_back(hld.query(a - 1, b - 1)); + } else { + continue; + } + } + for (int i = 0; i < static_cast(expected_result.size()); i++) { + assert(expected_result[i] == code_result[i]); + } + std::cout << "\nTest3 passed!\n"; +} + +/** + * Main function + */ +int main() { + test_1(); + test_2(); + test_3(); + return 0; +} From 1433ef22106c140121ecaba64cc2116fd0c39aa9 Mon Sep 17 00:00:00 2001 From: Rp-sushil Date: Thu, 20 Aug 2020 17:21:30 +0530 Subject: [PATCH 34/90] [BUG] #804 search/median_search.cpp FIXED --- search/median_search.cpp | 158 +++++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 63 deletions(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index 7379cad26..3190afa2d 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -1,78 +1,110 @@ /** * \file * \brief [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm - * \warning This core is erroneous and gives invorrect answers. Tested using * cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) * \ingroup median search - * \{ */ -#include + #include +#include #include +using namespace std; -/** - * @todo add documentation - */ -template -void comp(X x, std::vector *s1, std::vector *s2, - std::vector *s3) { - if (s1->size() >= x && s1->size() + s2->size() < x) { - std::cout << (*s2)[0] << " is the " << x + 1 << "th element from front"; - } else if (s1->size() > x) { - std::sort(s1->begin(), s1->end()); - std::cout << (*s1)[x] << " is the " << x + 1 << "th element from front"; - } else if (s1->size() + s2->size() <= x && s3->size() > x) { - std::sort(s3->begin(), s3->end()); - std::cout << (*s3)[x - s1->size() - s2->size()] << " is the " << x + 1 - << "th element from front"; - } else { - std::cout << x + 1 << " is invalid location"; - } +/* Assume that all the elements of A are distinct + def median_of_medians(A, i): + + #divide A into sublists of len 5 + sublists = [A[j:j+5] for j in range(0, len(A), 5)] + medians = [sorted(sublist)[len(sublist)/2] for sublist in sublists] + if len(medians) <= 5: + pivot = sorted(medians)[len(medians)/2] + else: + #the pivot is the median of the medians + pivot = median_of_medians(medians, len(medians)/2) + + #partitioning step + low = [j for j in A if j < pivot] + high = [j for j in A if j > pivot] + + k = len(low) + if i < k: + return median_of_medians(low,i) + elif i > k: + return median_of_medians(high,i-k-1) + else: #pivot = k + return pivot +*/ + +/* + * Here are some example lists you can use to see how the algorithm works + * A = [1,2,3,4,5,1000,8,9,99] (Contain Unique Elements) + * B = [1,2,3,4,5,6] (Contains Unique Elements) + * print median_of_medians(A, 0) #should be 1 + * print median_of_medians(A,7) #should be 99 + * print median_of_medians(B,4) #should be 5 +*/ + +int median_of_medians(vector a, int idx){ + int pivot; + vector m; + int r = a.size(); + for(int i = 0; i < r; i += 5){ + sort(a.begin() + i, a.begin() + min(r, i + 5)); + int mid = (i + min(r, i + 5)) / 2; + m.push_back(a[mid]); + } + int sz = int(m.size()); + if(sz <= 5){ + sort(m.begin(), m.end()); + pivot = m[(sz- 1) / 2]; + } + else{ + pivot = median_of_medians(m, idx); + } + vector low; + vector high; + for(int i = 0; i < r; i++){ + if(a[i] < pivot) + low.push_back(a[i]); + else if(a[i] > pivot) + high.push_back(a[i]); + } + int k = int(low.size()); + if(idx < k) + return median_of_medians(low, idx); + else if(idx > k) + return median_of_medians(high, idx-k-1); + else + return pivot; } -#define MAX_NUM 20 ///< maximum number of values to sort from +/* Main function*/ -/** - * Main function - */ -int main() { - std::vector v{25, 21, 98, 100, 76, 22, 43, 60, 89, 87}; - std::vector s1; - std::vector s2; - std::vector s3; +int main() +{ + int n; + cout << "Enter Size of Array: "; + cin >> n; + vector a(n); + cout << "Enter Array: "; + for(int i = 0; i < n; i++) + cin >> a[i]; - // creates an array of random numbers - // for (int i = 0; i < MAX_NUM; i++) { - // int r = std::rand() % 1000; - // v.push_back(r); - // std::cout << r << " "; - // } - for (int r : v) std::cout << r << " "; + cout << "Median: "; + int x = median_of_medians(a, (n - 1) / 2); + if(n % 2 == 0){ + int y = median_of_medians(a, n / 2); + cout << (float(x) + float(y))/2.0; + } + else + cout << x; - int median = std::rand() % 1000; // initialize to a random numnber - - std::cout << "\nmedian=" << median << std::endl; - int avg1, avg2, avg3, sum1 = 0, sum2 = 0, sum3 = 0; - - for (int i = 0; i < v.size(); i++) { // iterate through all numbers - if (v.back() == v[median]) { - avg1 = sum1 + v.back(); - s2.push_back(v.back()); - } else if (v.back() < v[median]) { - avg2 = sum2 + v.back(); - s1.push_back(v.back()); - } else { - avg3 = sum3 + v.back(); - s3.push_back(v.back()); - } - v.pop_back(); - } - - int x; - std::cout << "enter the no. to be searched form begining:- "; - std::cin >> x; - comp(x - 1, &s1, &s2, &s3); - - return 0; + cout << "\nTo find i-th smallest element "; + cout << "\nEnter i: "; + int idx; + cin >> idx; + idx--; + cout << "i-th smallest element: " << median_of_medians(a, idx) << endl; + return 0; } -/// } + From 0d61b0494f01149fe78a3e3132e095392ed06cfd Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Fri, 21 Aug 2020 10:45:00 +0530 Subject: [PATCH 35/90] fixed clang-tidy warnings --- search/median_search.cpp | 68 +++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index 3190afa2d..c67ad170b 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -8,7 +8,6 @@ #include #include #include -using namespace std; /* Assume that all the elements of A are distinct def median_of_medians(A, i): @@ -44,67 +43,72 @@ using namespace std; * print median_of_medians(B,4) #should be 5 */ -int median_of_medians(vector a, int idx){ - int pivot; - vector m; +int median_of_medians(std::vector a, int idx){ + int pivot = 0; // initialized with zero + std::vector m; int r = a.size(); for(int i = 0; i < r; i += 5){ - sort(a.begin() + i, a.begin() + min(r, i + 5)); - int mid = (i + min(r, i + 5)) / 2; + std::sort(a.begin() + i, a.begin() + std::min(r, i + 5)); + int mid = (i + std::min(r, i + 5)) / 2; m.push_back(a[mid]); } int sz = int(m.size()); if(sz <= 5){ - sort(m.begin(), m.end()); + std::sort(m.begin(), m.end()); pivot = m[(sz- 1) / 2]; } else{ pivot = median_of_medians(m, idx); } - vector low; - vector high; + std::vector low; + std::vector high; for(int i = 0; i < r; i++){ - if(a[i] < pivot) + if(a[i] < pivot){ low.push_back(a[i]); - else if(a[i] > pivot) + } + else if(a[i] > pivot){ high.push_back(a[i]); + } } int k = int(low.size()); - if(idx < k) + if(idx < k){ return median_of_medians(low, idx); - else if(idx > k) + } + else if(idx > k){ return median_of_medians(high, idx-k-1); - else + } + else{ return pivot; + } } /* Main function*/ int main() { - int n; - cout << "Enter Size of Array: "; - cin >> n; - vector a(n); - cout << "Enter Array: "; - for(int i = 0; i < n; i++) - cin >> a[i]; - - cout << "Median: "; + int n = 0; + std::cout << "Enter Size of Array: "; + std::cin >> n; + std::vector a(n); + std::cout << "Enter Array: "; + for(int i = 0; i < n; i++){ + std::cin >> a[i]; + } + std::cout << "Median: "; // Median defination: https://en.wikipedia.org/wiki/Median int x = median_of_medians(a, (n - 1) / 2); if(n % 2 == 0){ int y = median_of_medians(a, n / 2); - cout << (float(x) + float(y))/2.0; + std::cout << (float(x) + float(y))/2.0; } - else - cout << x; - - cout << "\nTo find i-th smallest element "; - cout << "\nEnter i: "; - int idx; - cin >> idx; + else{ + std::cout << x; + } + std::cout << "\nTo find i-th smallest element "; + std::cout << "\nEnter i: "; + int idx = 0; + std::cin >> idx; idx--; - cout << "i-th smallest element: " << median_of_medians(a, idx) << endl; + std::cout << idx + 1<< "-th smallest element: " << median_of_medians(a, idx) << '\n'; return 0; } From 406ad387db413690247cc77ba6ba6d34cac186c6 Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Fri, 21 Aug 2020 12:08:06 +0530 Subject: [PATCH 36/90] Suggested changes has been made. --- search/median_search.cpp | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index c67ad170b..4ef73ac08 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -1,14 +1,20 @@ -/** - * \file - * \brief [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm - * cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) - * \ingroup median search - */ +// Given an array A[1,...,n] of n numbers and an index idx, idx, where 1≤idx≤ n, 1≤idx≤ n, find the i-th smallest element of A. #include #include #include +/** + * @namespace search + * @brief Search algorithms + */ +namespace search { +/** + * @namespace median_search + * @brief Functions for [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm + * @cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) + */ +namespace median_search { /* Assume that all the elements of A are distinct def median_of_medians(A, i): @@ -43,8 +49,8 @@ * print median_of_medians(B,4) #should be 5 */ -int median_of_medians(std::vector a, int idx){ - int pivot = 0; // initialized with zero +int median_of_medians(std::vector a, int idx){ // Search the element in **a** whose index is **idx** and return element at index **idx** in **a** (a[idx]) + int pivot = 0; // initialized with zero std::vector m; int r = a.size(); for(int i = 0; i < r; i += 5){ @@ -81,9 +87,12 @@ int median_of_medians(std::vector a, int idx){ return pivot; } } +} // namespace median_search +} // namespace search -/* Main function*/ - +/** + * Main function + */ int main() { int n = 0; @@ -95,9 +104,9 @@ int main() std::cin >> a[i]; } std::cout << "Median: "; // Median defination: https://en.wikipedia.org/wiki/Median - int x = median_of_medians(a, (n - 1) / 2); + int x = search::median_search::median_of_medians(a, (n - 1) / 2); if(n % 2 == 0){ - int y = median_of_medians(a, n / 2); + int y = search::median_search::median_of_medians(a, n / 2); std::cout << (float(x) + float(y))/2.0; } else{ @@ -108,7 +117,7 @@ int main() int idx = 0; std::cin >> idx; idx--; - std::cout << idx + 1<< "-th smallest element: " << median_of_medians(a, idx) << '\n'; + std::cout << idx + 1<< "-th smallest element: " << search::median_search::median_of_medians(a, idx) << '\n'; return 0; } From b631eee388da277a66e781dd8e8bb9ebcb2f1d75 Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Fri, 21 Aug 2020 23:47:07 +0530 Subject: [PATCH 37/90] added a comment block like in #962. --- search/median_search.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index 4ef73ac08..0488a5188 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -1,5 +1,14 @@ -// Given an array A[1,...,n] of n numbers and an index idx, idx, where 1≤idx≤ n, 1≤idx≤ n, find the i-th smallest element of A. - +/** + * @file median_search.cpp + * @brief Implementation of [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm. + * @cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) + * + * @details + * Given an array A[1,...,n] of n numbers and an index idx, idx, where 1≤idx≤ n, 1≤idx≤ n, find the i-th smallest element of A. + * \note this algorithm implements median search for only arrays which have distinct elements + * + * @author [Sushil Kumar](https://github.com/Rp-sushil) + */ #include #include #include @@ -12,7 +21,6 @@ namespace search { /** * @namespace median_search * @brief Functions for [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm - * @cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) */ namespace median_search { /* Assume that all the elements of A are distinct From caed3ddadd2e2ff8d3a43062aab27a68efd6294b Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Sat, 22 Aug 2020 01:53:54 +0530 Subject: [PATCH 38/90] Changed has been made (#962 as a reference) --- search/median_search.cpp | 77 ++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index 0488a5188..28f74b17a 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -1,14 +1,43 @@ /** * @file median_search.cpp - * @brief Implementation of [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm. + * @brief Implementation of [Median search](https://en.wikipedia.org/wiki/Median_of_medians) algorithm. * @cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) * * @details - * Given an array A[1,...,n] of n numbers and an index idx, idx, where 1≤idx≤ n, 1≤idx≤ n, find the i-th smallest element of A. + * Given an array A[1,...,n] of n numbers and an index idx, idx, where 1≤idx≤ n, 1≤idx≤ n, find the i-th smallest element of A. + * median_of_medians(A, i): + * #divide A into sublists of len 5 + * sublists = [A[j:j+5] for j in range(0, len(A), 5)] + * medians = [sorted(sublist)[len(sublist)/2] for sublist in sublists] + * if len(medians) <= 5: + * pivot = sorted(medians)[len(medians)/2] + * else: + * #the pivot is the median of the medians + * pivot = median_of_medians(medians, len(medians)/2) + * #partitioning step + * low = [j for j in A if j < pivot] + * high = [j for j in A if j > pivot] + * k = len(low) + * if i < k: + * return median_of_medians(low,i) + * elif i > k: + * return median_of_medians(high,i-k-1) + * else: #pivot = k + * return pivot + * * \note this algorithm implements median search for only arrays which have distinct elements - * + * + * Here are some example lists you can use to see how the algorithm works + * A = [1,2,3,4,5,1000,8,9,99] (Contain Unique Elements) + * B = [1,2,3,4,5,6] (Contains Unique Elements) + * print median_of_medians(A, 0) #should be 1 + * print median_of_medians(A,7) #should be 99 + * print median_of_medians(B,4) #should be 5 + * + * @author Unknown author * @author [Sushil Kumar](https://github.com/Rp-sushil) */ + #include #include #include @@ -23,42 +52,14 @@ namespace search { * @brief Functions for [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm */ namespace median_search { -/* Assume that all the elements of A are distinct - def median_of_medians(A, i): - - #divide A into sublists of len 5 - sublists = [A[j:j+5] for j in range(0, len(A), 5)] - medians = [sorted(sublist)[len(sublist)/2] for sublist in sublists] - if len(medians) <= 5: - pivot = sorted(medians)[len(medians)/2] - else: - #the pivot is the median of the medians - pivot = median_of_medians(medians, len(medians)/2) - - #partitioning step - low = [j for j in A if j < pivot] - high = [j for j in A if j > pivot] - - k = len(low) - if i < k: - return median_of_medians(low,i) - elif i > k: - return median_of_medians(high,i-k-1) - else: #pivot = k - return pivot -*/ - -/* - * Here are some example lists you can use to see how the algorithm works - * A = [1,2,3,4,5,1000,8,9,99] (Contain Unique Elements) - * B = [1,2,3,4,5,6] (Contains Unique Elements) - * print median_of_medians(A, 0) #should be 1 - * print median_of_medians(A,7) #should be 99 - * print median_of_medians(B,4) #should be 5 -*/ - -int median_of_medians(std::vector a, int idx){ // Search the element in **a** whose index is **idx** and return element at index **idx** in **a** (a[idx]) +/** +* This function Search the element in **a** whose index is **idx** and return element at index **idx** in **a** (a[idx]) +* @param A(list) and idx(index) of element which we want to search +* @return corresponding element which we want to search. +*/ +int median_of_medians(const std::vector& A, const int& idx) { int pivot = 0; // initialized with zero + std::vector a(A.begin(), A.end()); std::vector m; int r = a.size(); for(int i = 0; i < r; i += 5){ From 498db114f3e053dc215ca8871ca5d8b9ca49a0b3 Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Sat, 22 Aug 2020 02:22:47 +0530 Subject: [PATCH 39/90] updated median_search (#962 as reference) --- search/median_search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index 28f74b17a..3cf1ea994 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -53,8 +53,9 @@ namespace search { */ namespace median_search { /** -* This function Search the element in **a** whose index is **idx** and return element at index **idx** in **a** (a[idx]) -* @param A(list) and idx(index) of element which we want to search +* This function search the element in an array for the given index. +* @param A an array +* @param idx the index * @return corresponding element which we want to search. */ int median_of_medians(const std::vector& A, const int& idx) { From 306de24e703f6fba5366d4dcf96f08a3211a09ad Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Sat, 22 Aug 2020 02:39:13 +0530 Subject: [PATCH 40/90] Suggested changes has been made. --- search/median_search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index 3cf1ea994..f252e8430 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -54,9 +54,9 @@ namespace search { namespace median_search { /** * This function search the element in an array for the given index. -* @param A an array -* @param idx the index -* @return corresponding element which we want to search. +* @param A array where numbers are saved +* @param idx current index in array +* @returns corresponding element which we want to search. */ int median_of_medians(const std::vector& A, const int& idx) { int pivot = 0; // initialized with zero From 37b252697da4638bebc26531f7859314bfbc2ba7 Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Sun, 23 Aug 2020 00:29:00 +0530 Subject: [PATCH 41/90] test added --- search/median_search.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index f252e8430..08cb676c9 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -4,7 +4,7 @@ * @cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) * * @details - * Given an array A[1,...,n] of n numbers and an index idx, idx, where 1≤idx≤ n, 1≤idx≤ n, find the i-th smallest element of A. + * Given an array A[1,...,n] of n numbers and an index idx, where 1≤idx≤ n, 1≤idx≤ n, find the i-th smallest element of A. * median_of_medians(A, i): * #divide A into sublists of len 5 * sublists = [A[j:j+5] for j in range(0, len(A), 5)] @@ -41,6 +41,7 @@ #include #include #include +#include /** * @namespace search @@ -100,11 +101,33 @@ int median_of_medians(const std::vector& A, const int& idx) { } // namespace median_search } // namespace search +/** + * Function to test above algorithm + */ +void test(){ + std::vector A{25,21,98,100,76,22,43,60,89,87}; + int i = 3; + assert(A[6] == search::median_search::median_of_medians(A, i)); // A[6] = 43, is the fourth smallest element. + std::cout << "test case:1 passed\n"; + + std::vector B{1,2,3,4,5,6}; + int j = 4; + assert(B[4] == search::median_search::median_of_medians(B, j)); // B[4] = 5, is the fifth smallest element. + std::cout << "test case:2 passed\n"; + + std::vector C{1,2,3,4,5,1000,8,9,99}; + int k = 3; + assert(C[3] == search::median_search::median_of_medians(C, k)); // C[3] = 4, is the fourth smallest element. + std::cout << "test case:3 passed\n"; + std::cout << "--All tests passed--\n"; +} + /** * Main function */ int main() { + test(); int n = 0; std::cout << "Enter Size of Array: "; std::cin >> n; From a1242904e1faad3c4391244477dd428f4f6b3f72 Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Sun, 23 Aug 2020 00:37:46 +0530 Subject: [PATCH 42/90] repeated constraints removed. --- search/median_search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index 08cb676c9..c20bcfeaa 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -4,7 +4,7 @@ * @cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) * * @details - * Given an array A[1,...,n] of n numbers and an index idx, where 1≤idx≤ n, 1≤idx≤ n, find the i-th smallest element of A. + * Given an array A[1,...,n] of n numbers and an index idx, where 1≤idx≤ n, find the i-th smallest element of A. * median_of_medians(A, i): * #divide A into sublists of len 5 * sublists = [A[j:j+5] for j in range(0, len(A), 5)] From b50591dfcedd55ed86ba7c119a1da3ff986305de Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Sun, 23 Aug 2020 15:49:52 +0530 Subject: [PATCH 43/90] Suggested changes has been made. --- search/median_search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/search/median_search.cpp b/search/median_search.cpp index c20bcfeaa..479de8107 100644 --- a/search/median_search.cpp +++ b/search/median_search.cpp @@ -4,7 +4,7 @@ * @cases from [here](https://brilliant.org/wiki/median-finding-algorithm/) * * @details - * Given an array A[1,...,n] of n numbers and an index idx, where 1≤idx≤ n, find the i-th smallest element of A. + * Given an array A[1,...,n] of n numbers and an index i, where 1 ≤ i ≤ n, find the i-th smallest element of A. * median_of_medians(A, i): * #divide A into sublists of len 5 * sublists = [A[j:j+5] for j in range(0, len(A), 5)] @@ -107,12 +107,12 @@ int median_of_medians(const std::vector& A, const int& idx) { void test(){ std::vector A{25,21,98,100,76,22,43,60,89,87}; int i = 3; - assert(A[6] == search::median_search::median_of_medians(A, i)); // A[6] = 43, is the fourth smallest element. + assert(A[6] == search::median_search::median_of_medians(A, i)); // A[6] = 43, is the fourth smallest element. std::cout << "test case:1 passed\n"; std::vector B{1,2,3,4,5,6}; int j = 4; - assert(B[4] == search::median_search::median_of_medians(B, j)); // B[4] = 5, is the fifth smallest element. + assert(B[4] == search::median_search::median_of_medians(B, j)); // B[4] = 5, is the fifth smallest element. std::cout << "test case:2 passed\n"; std::vector C{1,2,3,4,5,1000,8,9,99}; From 039022becf5539ad852e0ef4b10cd4ef61469883 Mon Sep 17 00:00:00 2001 From: Anushka Verma Date: Mon, 24 Aug 2020 01:19:39 +0530 Subject: [PATCH 44/90] fix typo: (rename file) fast_interger_input -> fast_integer_input --- others/{fast_interger_input.cpp => fast_integer_input.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename others/{fast_interger_input.cpp => fast_integer_input.cpp} (100%) diff --git a/others/fast_interger_input.cpp b/others/fast_integer_input.cpp similarity index 100% rename from others/fast_interger_input.cpp rename to others/fast_integer_input.cpp From 7a13a35dfd6d4563d5dab7db3547b5e8f131fc61 Mon Sep 17 00:00:00 2001 From: Anushka Verma Date: Mon, 24 Aug 2020 01:47:16 +0530 Subject: [PATCH 45/90] feat: add word break solution (backtracking) --- backtracking/word_break.cpp | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 backtracking/word_break.cpp diff --git a/backtracking/word_break.cpp b/backtracking/word_break.cpp new file mode 100644 index 000000000..b41a6aa77 --- /dev/null +++ b/backtracking/word_break.cpp @@ -0,0 +1,50 @@ +/** + * @file Word Break + * + * @details + * Given a valid sentence without any spaces between the words and + * a dictionary of valid English words, find all possible ways to + * break the sentence in individual dictionary words. + * + * @author [Anushka Verma](https://github.com/verma-anushka) + */ + +#include +using namespace std; + +/** + * Utility function + */ +void wordbreak(string s, vector& dict, int i, string ans) { + + string word=s.substr(0, i+1); + int ws=s.size(); + + // search for word in the dictionary + if(find(dict.begin(), dict.end(), word) != dict.end()) { + + if(i dict = { "mobile","samsung","sam","sung", + "man","mango","icecream","and", + "go","i","like","ice","cream" }; + string s = "ilikesamsung"; + wordbreak(s, dict, 0, ""); + return 0; +} + From 47bbc2a16e28ed92aab5269fc390de3ee16e3b16 Mon Sep 17 00:00:00 2001 From: Anushka Verma Date: Mon, 24 Aug 2020 01:54:17 +0530 Subject: [PATCH 46/90] remove word break --- backtracking/word_break.cpp | 50 ------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 backtracking/word_break.cpp diff --git a/backtracking/word_break.cpp b/backtracking/word_break.cpp deleted file mode 100644 index b41a6aa77..000000000 --- a/backtracking/word_break.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file Word Break - * - * @details - * Given a valid sentence without any spaces between the words and - * a dictionary of valid English words, find all possible ways to - * break the sentence in individual dictionary words. - * - * @author [Anushka Verma](https://github.com/verma-anushka) - */ - -#include -using namespace std; - -/** - * Utility function - */ -void wordbreak(string s, vector& dict, int i, string ans) { - - string word=s.substr(0, i+1); - int ws=s.size(); - - // search for word in the dictionary - if(find(dict.begin(), dict.end(), word) != dict.end()) { - - if(i dict = { "mobile","samsung","sam","sung", - "man","mango","icecream","and", - "go","i","like","ice","cream" }; - string s = "ilikesamsung"; - wordbreak(s, dict, 0, ""); - return 0; -} - From 149fe0cd63294d91b572cf9f0156522a29f8d644 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Tue, 25 Aug 2020 12:23:25 +0000 Subject: [PATCH 47/90] updating DIRECTORY.md --- DIRECTORY.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 605c4a1fe..4d95ad3ac 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -113,7 +113,9 @@ * [Adaline Learning](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/adaline_learning.cpp) * [Kohonen Som Topology](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/kohonen_som_topology.cpp) * [Kohonen Som Trace](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/kohonen_som_trace.cpp) + * [Neural Network](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/neural_network.cpp) * [Ordinary Least Squares Regressor](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/ordinary_least_squares_regressor.cpp) + * [Vector Ops](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/vector_ops.hpp) ## Math * [Armstrong Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/armstrong_number.cpp) @@ -182,7 +184,7 @@ * [Decimal To Binary](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/decimal_to_binary.cpp) * [Decimal To Hexadecimal](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/decimal_to_hexadecimal.cpp) * [Decimal To Roman Numeral](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/decimal_to_roman_numeral.cpp) - * [Fast Interger Input](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/fast_interger_input.cpp) + * [Fast Integer Input](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/fast_integer_input.cpp) * [Happy Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/happy_number.cpp) * [Matrix Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/matrix_exponentiation.cpp) * [Palindrome Of Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/palindrome_of_number.cpp) From de868c9faa2ac2191343c177549c03393a8f8a86 Mon Sep 17 00:00:00 2001 From: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Date: Tue, 25 Aug 2020 19:20:32 -0400 Subject: [PATCH 48/90] [enhancement] guideline for multiple implementations (#1051) * guideline for multiple implementations * updating DIRECTORY.md * fixed spelling Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 35d875d43..07d620dea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,8 @@ We are very happy that you consider implementing algorithms and data structures - Please use the directory structure of the repository. - File extension for code should be *.h *.cpp. - Don't use **bits/stdc++.h** because this is quite Linux specific and slows down the compilation process. -- Avoid using **struct** and instead use the **class** keyword. +- Organize your code using **`struct`**, **`class`** and/or **`namespace`** keywords +- If an implementation of the algorithm already exists, please refer to the [file-name section below](#new-file-name-guidelines). - You can suggest reasonable changes to existing algorithms. - Strictly use snake_case (underscore_separated) in filenames. - If you have added or modified code, please make sure the code compiles before submitting. @@ -116,6 +117,7 @@ my_new_cpp_class.cpp is correct format ``` - It will be used to dynamically create a directory of files and implementation. - File name validation will run on docker to ensure the validity. +- If an implementation of the algorithm already exists and your version is different from that implemented, please use incremental numeric digit as a suffix. For example, if `median_search.cpp` already exists in the `search` folder and you are contributing a new implementation, the filename should be `median_search2.cpp` and for a third implementation, `median_search3.cpp`. #### New Directory guidelines - We recommend adding files to existing directories as much as possible. From 2882b7bec2aad61bd42912d2262696aebec3648c Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Tue, 25 Aug 2020 16:56:49 -0700 Subject: [PATCH 49/90] fix: math/fibonacci linter warnings. (#1047) * fix: math/fibonacci linter warnings. * updating DIRECTORY.md * doxygen * unit64_t instead of unsigned int Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- math/fibonacci.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/math/fibonacci.cpp b/math/fibonacci.cpp index 493523b61..4e3c15de8 100644 --- a/math/fibonacci.cpp +++ b/math/fibonacci.cpp @@ -13,12 +13,15 @@ /** * Recursively compute sequences + * @param n input + * @returns n-th element of the Fbinacci's sequence */ -unsigned int fibonacci(unsigned int n) { +uint64_t fibonacci(uint64_t n) { /* If the input is 0 or 1 just return the same This will set the first 2 values of the sequence */ - if (n <= 1) + if (n <= 1) { return n; + } /* Add the last 2 values of the sequence to get next */ return fibonacci(n - 1) + fibonacci(n - 2); @@ -30,27 +33,27 @@ unsigned int fibonacci(unsigned int n) { * @returns `void` */ static void test() { - unsigned int test_case_1 = fibonacci(0); + uint64_t test_case_1 = fibonacci(0); assert(test_case_1 == 0); std::cout << "Passed Test 1!" << std::endl; - unsigned int test_case_2 = fibonacci(1); + uint64_t test_case_2 = fibonacci(1); assert(test_case_2 == 1); std::cout << "Passed Test 2!" << std::endl; - unsigned int test_case_3 = fibonacci(2); + uint64_t test_case_3 = fibonacci(2); assert(test_case_3 == 1); std::cout << "Passed Test 3!" << std::endl; - unsigned int test_case_4 = fibonacci(3); + uint64_t test_case_4 = fibonacci(3); assert(test_case_4 == 2); std::cout << "Passed Test 4!" << std::endl; - unsigned int test_case_5 = fibonacci(4); + uint64_t test_case_5 = fibonacci(4); assert(test_case_5 == 3); std::cout << "Passed Test 5!" << std::endl; - unsigned int test_case_6 = fibonacci(15); + uint64_t test_case_6 = fibonacci(15); assert(test_case_6 == 610); std::cout << "Passed Test 6!" << std::endl << std::endl; } @@ -58,7 +61,7 @@ static void test() { /// Main function int main() { test(); - int n; + int n = 0; std::cin >> n; assert(n >= 0); std::cout << "F(" << n << ")= " << fibonacci(n) << std::endl; From 66dcc4c394c823f5ce1a964e050077899decd48c Mon Sep 17 00:00:00 2001 From: Lownish Rai Sookha <69192570+Lownish@users.noreply.github.com> Date: Wed, 26 Aug 2020 05:27:24 +0530 Subject: [PATCH 50/90] feat: add Pigeonhole algorithm (#1028) * feat: add Pigeonhole algorithm * Executed clang-format * Used pointers and vector * Corrected clang-tidy issues * Modified code structure * Apply suggestions from code review Suggested changes applied Co-authored-by: David Leal * Added missing parameter and documentation * Added delete function * Update pigeonhole_sort.cpp * Corrected delete function Co-authored-by: David Leal * Apply suggestions from code review Co-authored-by: David Leal * Apply suggestions from code review Co-authored-by: David Leal * Changed documentation regarding array size * clang-tidy * Apply suggestions from code review Co-authored-by: David Leal * assert moved to test function * Update pigeonhole_sort.cpp * Update sorting/pigeonhole_sort.cpp Co-authored-by: David Leal * Added test function and inbuilt min function * min and max to const variables * const int* to auto * const int* to auto * Apply suggestions from code review Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Modified pigeonSort documentation * Corrected test functions Co-authored-by: lsurface Co-authored-by: David Leal Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> --- sorting/pigeonhole_sort.cpp | 133 ++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 sorting/pigeonhole_sort.cpp diff --git a/sorting/pigeonhole_sort.cpp b/sorting/pigeonhole_sort.cpp new file mode 100644 index 000000000..764365b86 --- /dev/null +++ b/sorting/pigeonhole_sort.cpp @@ -0,0 +1,133 @@ +/** + * @file + * @brief Implementation of [Pigeonhole Sort algorithm] + * (https://en.wikipedia.org/wiki/Pigeonhole_sort) + * @author [Lownish](https://github.com/Lownish) + * @details + * Pigeonhole sorting is a sorting algorithm that is suitable for sorting lists + * of elements where the number of elements and the number of possible key + * values are approximately the same. It requires O(n + Range) time where n is + * number of elements in input array and ‘Range’ is number of possible values in + * array. + * + * The time Complexity of the algorithm is \f$O(n+N)\f$. + */ + +#include //for std::is_sorted +#include //for std::array +#include //for assert +#include //for io operations + +/** + * @namespace sorting + * @brief Sorting algorithms + */ +namespace sorting { + +/** + * Pigeonhole sorting of array of size n + * The function will sort the array through Pigeonhole algorithm and print + * @param arr unsorted array of elements + * @returns sorted array of elements + */ +template +std::array pigeonSort(std::array arr) { + // Finding min and max* + auto min = std::min_element(std::begin(arr), std::end(arr)); + auto max = std::max_element(std::begin(arr), std::end(arr)); + + // Range refers to the number of holes required + int range = *max - *min + 1; + int *hole = new int[range](); + + // Copying all array values to pigeonhole + for (int i = 0; i < N; i++) { + hole[arr[i] - *min] = arr[i]; + } + + // Deleting elements from list and storing to original array + int count = 0; + for (int i = 0; i < range; i++) { + while (hole[i] != '\0') { + arr[count] = hole[i]; + hole[i] = {}; + count++; + } + } + delete[] hole; + + return arr; +} +} // namespace sorting + +/** + * Test function 1 with unsorted array + * {8, 3, 2, 7, 4, 6, 8} + * @returns none + */ +static void test_1() { + const int n = 7; + std::array test_array = {8, 3, 2, 7, 4, 6, 8}; + + test_array = sorting::pigeonSort(test_array); + + assert(std::is_sorted(std::begin(test_array), std::end(test_array))); + + // Printing sorted array + for (int i = 0; i < n; i++) { + std::cout << test_array.at(i) << " "; + } + std::cout << "\nPassed\n"; +} + +/** + * Test function 2 with unsorted array + * {802, 630, 20, 745, 52, 300, 612, 932, 78, 187} + * @returns none + */ +static void test_2() { + const int n = 10; + std::array test_array = {802, 630, 20, 745, 52, + 300, 612, 932, 78, 187}; + + test_array = sorting::pigeonSort(test_array); + + assert(std::is_sorted(std::begin(test_array), std::end(test_array))); + + // Printing sorted array + for (int i = 0; i < n; i++) { + std::cout << test_array.at(i) << " "; + } + std::cout << "\nPassed\n"; +} + +/** + * Test function 1 with unsorted array + * {11,13,12,14} + * @returns none + */ +static void test_3() { + const int n = 4; + std::array test_array = {11, 13, 12, 14}; + + test_array = sorting::pigeonSort(test_array); + + assert(std::is_sorted(std::begin(test_array), std::end(test_array))); + + // Printing sorted array + for (int i = 0; i < n; i++) { + std::cout << test_array.at(i) << " "; + } + std::cout << "\nPassed\n"; +} + +/** + * Main function + */ +int main() { + test_1(); + test_2(); + test_3(); + + return 0; +} From 63333f38ccd81d82eb2b6a5ee6786bdbe5b83a04 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Tue, 25 Aug 2020 23:58:06 +0000 Subject: [PATCH 51/90] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 4d95ad3ac..178e88499 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -241,6 +241,7 @@ * [Non Recursive Merge Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/non_recursive_merge_sort.cpp) * [Numeric String Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/numeric_string_sort.cpp) * [Odd Even Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/odd_even_sort.cpp) + * [Pigeonhole Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/pigeonhole_sort.cpp) * [Quick Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/quick_sort.cpp) * [Quick Sort 3](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/quick_sort_3.cpp) * [Radix Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/radix_sort.cpp) From c4f586b6ab269948210f455520fc6b48a8b1378d Mon Sep 17 00:00:00 2001 From: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Date: Wed, 26 Aug 2020 10:48:08 -0400 Subject: [PATCH 52/90] [bug fix] fix code formatting in CI (#1052) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: linter and spacing for is_graph_bipartite. * updating DIRECTORY.md * clang-tidy fixes for a49ec9b8d79e62be49587043cf482a9f705e3ed1 * use clang-12 * downgrade to clang11 * added clang-format confiug file * added explicit clang-format step in workflow * fix git command * commit format and lint together lint first and then format * corrected order * Revert "Merge branch 'is_graph_bipartite' into fix_clang" This reverts commit d4d406017502cc745a9a77b4d462dc7a4aea6522, reversing changes made to 2ccc3a364e4a6491e62ad836a237eb1603ddef6a. Co-authored-by: Filip Hlásek Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .clang-format | 167 +++++++++++++++++++++++++ .github/workflows/awesome_workflow.yml | 16 +-- 2 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..2711cbb0b --- /dev/null +++ b/.clang-format @@ -0,0 +1,167 @@ +--- +Language: Cpp +AccessModifierOffset: -3 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +... + diff --git a/.github/workflows/awesome_workflow.yml b/.github/workflows/awesome_workflow.yml index 9cff1c48d..78ea62c7b 100644 --- a/.github/workflows/awesome_workflow.yml +++ b/.github/workflows/awesome_workflow.yml @@ -16,7 +16,7 @@ jobs: - name: requirements run: | sudo apt -qq -y update - sudo apt -qq install clang-tidy-10 + sudo apt -qq install clang-tidy-10 clang-format-10 # checks are passing with less errors when used with this version. # The default installs v6.0 which did not work out well in my tests - name: Setup Git Specs @@ -44,7 +44,7 @@ jobs: git "mv" "${fname}" ${new_fname} fi done - git commit -am "formatting filenames $GITHUB_SHA" || true + git commit -am "formatting filenames ${GITHUB_SHA::8}" || true - name: Update DIRECTORY.md shell: python @@ -124,15 +124,9 @@ jobs: subprocess.run(["clang-tidy-10", "--fix", "-p=build", "--extra-arg=-std=c++11", *cpp_files, "--"], check=True, text=True, stderr=subprocess.STDOUT) - # for cpp_file in cpp_files: - # subprocess.run(["clang-tidy-10", "--fix", "-p=build", cpp_file, "--"], - # check=True, text=True, stderr=subprocess.STDOUT) - # print("g++:") - # compile_exts = tuple(".c .c++ .cc .cpp .cu .cxx".split()) - # compile_files = [file for file in cpp_files if file.lower().endswith(compile_exts)] - # for cpp_file in cpp_files: - # subprocess.run(["g++", cpp_file], check=True, text=True) + subprocess.run(["clang-format-10", "-i", "-style=file", *cpp_files], + check=True, text=True, stderr=subprocess.STDOUT) upper_files = [file for file in cpp_files if file != file.lower()] if upper_files: @@ -155,7 +149,7 @@ jobs: - name: Commit and push changes run: | git diff DIRECTORY.md - git commit -am "clang-tidy fixes for $GITHUB_SHA" || true + git commit -am "clang-format and clang-tidy fixes for ${GITHUB_SHA::8}" || true git push --force origin HEAD:$GITHUB_REF || true build: From 44f4f4d6c3a0f4e3f4e2ed40bbbdf20380148dae Mon Sep 17 00:00:00 2001 From: Deep Raval Date: Wed, 26 Aug 2020 23:26:31 +0530 Subject: [PATCH 53/90] fix: error handling in opening of external files (neural_network.cpp). (#1044) * Error Handdling of Files * exit -> std::exit * Improved Overall Error handling and reporting * test commit added spaces in include to try to trigger GitHub Actions * updating DIRECTORY.md * clang-format and clang-tidy fixes for 51e943d0 Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- machine_learning/neural_network.cpp | 1518 ++++++++++++++------------- machine_learning/vector_ops.hpp | 348 +++--- 2 files changed, 970 insertions(+), 896 deletions(-) diff --git a/machine_learning/neural_network.cpp b/machine_learning/neural_network.cpp index aad469b14..b477f5e2e 100644 --- a/machine_learning/neural_network.cpp +++ b/machine_learning/neural_network.cpp @@ -1,763 +1,802 @@ /** - * @file + * @file * @author [Deep Raval](https://github.com/imdeep2905) - * - * @brief Implementation of [Multilayer Perceptron] (https://en.wikipedia.org/wiki/Multilayer_perceptron). - * + * + * @brief Implementation of [Multilayer Perceptron] + * (https://en.wikipedia.org/wiki/Multilayer_perceptron). + * * @details - * A multilayer perceptron (MLP) is a class of feedforward artificial neural network (ANN). The term MLP is used ambiguously, - * sometimes loosely to any feedforward ANN, sometimes strictly to refer to networks composed of multiple layers of perceptrons - * (with threshold activation). Multilayer perceptrons are sometimes colloquially referred to as "vanilla" neural networks, - * especially when they have a single hidden layer. - * - * An MLP consists of at least three layers of nodes: an input layer, a hidden layer and an output layer. Except for the - * input nodes, each node is a neuron that uses a nonlinear activation function. MLP utilizes a supervised learning technique - * called backpropagation for training. Its multiple layers and non-linear activation distinguish MLP from a linear - * perceptron. It can distinguish data that is not linearly separable. - * - * See [Backpropagation](https://en.wikipedia.org/wiki/Backpropagation) for training algorithm. - * - * \note This implementation uses mini-batch gradient descent as optimizer and MSE as loss function. Bias is also not included. + * A multilayer perceptron (MLP) is a class of feedforward artificial neural + * network (ANN). The term MLP is used ambiguously, sometimes loosely to any + * feedforward ANN, sometimes strictly to refer to networks composed of multiple + * layers of perceptrons (with threshold activation). Multilayer perceptrons are + * sometimes colloquially referred to as "vanilla" neural networks, especially + * when they have a single hidden layer. + * + * An MLP consists of at least three layers of nodes: an input layer, a hidden + * layer and an output layer. Except for the input nodes, each node is a neuron + * that uses a nonlinear activation function. MLP utilizes a supervised learning + * technique called backpropagation for training. Its multiple layers and + * non-linear activation distinguish MLP from a linear perceptron. It can + * distinguish data that is not linearly separable. + * + * See [Backpropagation](https://en.wikipedia.org/wiki/Backpropagation) for + * training algorithm. + * + * \note This implementation uses mini-batch gradient descent as optimizer and + * MSE as loss function. Bias is also not included. */ -#include "vector_ops.hpp" // Custom header file for vector operations - +#include +#include +#include +#include +#include #include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include + +#include "vector_ops.hpp" // Custom header file for vector operations /** \namespace machine_learning * \brief Machine learning algorithms */ namespace machine_learning { - /** \namespace neural_network - * \brief Neural Network or Multilayer Perceptron - */ - namespace neural_network { - /** \namespace activations - * \brief Various activation functions used in Neural network - */ - namespace activations { - /** - * Sigmoid function - * @param X Value - * @return Returns sigmoid(x) - */ - double sigmoid (const double &x) { - return 1.0 / (1.0 + std::exp(-x)); - } +/** \namespace neural_network + * \brief Neural Network or Multilayer Perceptron + */ +namespace neural_network { +/** \namespace activations + * \brief Various activation functions used in Neural network + */ +namespace activations { +/** + * Sigmoid function + * @param X Value + * @return Returns sigmoid(x) + */ +double sigmoid(const double &x) { return 1.0 / (1.0 + std::exp(-x)); } - /** - * Derivative of sigmoid function - * @param X Value - * @return Returns derivative of sigmoid(x) - */ - double dsigmoid (const double &x) { - return x * (1 - x); - } +/** + * Derivative of sigmoid function + * @param X Value + * @return Returns derivative of sigmoid(x) + */ +double dsigmoid(const double &x) { return x * (1 - x); } - /** - * Relu function - * @param X Value - * @returns relu(x) - */ - double relu (const double &x) { - return std::max(0.0, x); - } +/** + * Relu function + * @param X Value + * @returns relu(x) + */ +double relu(const double &x) { return std::max(0.0, x); } - /** - * Derivative of relu function - * @param X Value - * @returns derivative of relu(x) - */ - double drelu (const double &x) { - return x >= 0.0 ? 1.0 : 0.0; - } +/** + * Derivative of relu function + * @param X Value + * @returns derivative of relu(x) + */ +double drelu(const double &x) { return x >= 0.0 ? 1.0 : 0.0; } - /** - * Tanh function - * @param X Value - * @return Returns tanh(x) - */ - double tanh (const double &x) { - return 2 / (1 + std::exp(-2 * x)) - 1; - } +/** + * Tanh function + * @param X Value + * @return Returns tanh(x) + */ +double tanh(const double &x) { return 2 / (1 + std::exp(-2 * x)) - 1; } - /** - * Derivative of Sigmoid function - * @param X Value - * @return Returns derivative of tanh(x) - */ - double dtanh (const double &x) { - return 1 - x * x; +/** + * Derivative of Sigmoid function + * @param X Value + * @return Returns derivative of tanh(x) + */ +double dtanh(const double &x) { return 1 - x * x; } +} // namespace activations +/** \namespace util_functions + * \brief Various utility functions used in Neural network + */ +namespace util_functions { +/** + * Square function + * @param X Value + * @return Returns x * x + */ +double square(const double &x) { return x * x; } +/** + * Identity function + * @param X Value + * @return Returns x + */ +double identity_function(const double &x) { return x; } +} // namespace util_functions +/** \namespace layers + * \brief This namespace contains layers used + * in MLP. + */ +namespace layers { +/** + * neural_network::layers::DenseLayer class is used to store all necessary + * information about the layers (i.e. neurons, activation and kernal). This + * class is used by NeuralNetwork class to store layers. + * + */ +class DenseLayer { + public: + // To store activation function and it's derivative + double (*activation_function)(const double &); + double (*dactivation_function)(const double &); + int neurons; // To store number of neurons (used in summary) + std::string activation; // To store activation name (used in summary) + std::vector> kernal; // To store kernal (aka weights) + + /** + * Constructor for neural_network::layers::DenseLayer class + * @param neurons number of neurons + * @param activation activation function for layer + * @param kernal_shape shape of kernal + * @param random_kernal flag for whether to intialize kernal randomly + */ + DenseLayer(const int &neurons, const std::string &activation, + const std::pair &kernal_shape, + const bool &random_kernal) { + // Choosing activation (and it's derivative) + if (activation == "sigmoid") { + activation_function = neural_network::activations::sigmoid; + dactivation_function = neural_network::activations::sigmoid; + } else if (activation == "relu") { + activation_function = neural_network::activations::relu; + dactivation_function = neural_network::activations::drelu; + } else if (activation == "tanh") { + activation_function = neural_network::activations::tanh; + dactivation_function = neural_network::activations::dtanh; + } else if (activation == "none") { + // Set identity function in casse of none is supplied + activation_function = + neural_network::util_functions::identity_function; + dactivation_function = + neural_network::util_functions::identity_function; + } else { + // If supplied activation is invalid + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Invalid argument. Expected {none, sigmoid, relu, " + "tanh} got "; + std::cerr << activation << std::endl; + std::exit(EXIT_FAILURE); + } + this->activation = activation; // Setting activation name + this->neurons = neurons; // Setting number of neurons + // Initialize kernal according to flag + if (random_kernal) { + uniform_random_initialization(kernal, kernal_shape, -1.0, 1.0); + } else { + unit_matrix_initialization(kernal, kernal_shape); + } + } + /** + * Constructor for neural_network::layers::DenseLayer class + * @param neurons number of neurons + * @param activation activation function for layer + * @param kernal values of kernal (useful in loading model) + */ + DenseLayer(const int &neurons, const std::string &activation, + const std::vector> &kernal) { + // Choosing activation (and it's derivative) + if (activation == "sigmoid") { + activation_function = neural_network::activations::sigmoid; + dactivation_function = neural_network::activations::sigmoid; + } else if (activation == "relu") { + activation_function = neural_network::activations::relu; + dactivation_function = neural_network::activations::drelu; + } else if (activation == "tanh") { + activation_function = neural_network::activations::tanh; + dactivation_function = neural_network::activations::dtanh; + } else if (activation == "none") { + // Set identity function in casse of none is supplied + activation_function = + neural_network::util_functions::identity_function; + dactivation_function = + neural_network::util_functions::identity_function; + } else { + // If supplied activation is invalid + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Invalid argument. Expected {none, sigmoid, relu, " + "tanh} got "; + std::cerr << activation << std::endl; + std::exit(EXIT_FAILURE); + } + this->activation = activation; // Setting activation name + this->neurons = neurons; // Setting number of neurons + this->kernal = kernal; // Setting supplied kernal values + } + + /** + * Copy Constructor for class DenseLayer. + * + * @param model instance of class to be copied. + */ + DenseLayer(const DenseLayer &layer) = default; + + /** + * Destructor for class DenseLayer. + */ + ~DenseLayer() = default; + + /** + * Copy assignment operator for class DenseLayer + */ + DenseLayer &operator=(const DenseLayer &layer) = default; + + /** + * Move constructor for class DenseLayer + */ + DenseLayer(DenseLayer &&) = default; + + /** + * Move assignment operator for class DenseLayer + */ + DenseLayer &operator=(DenseLayer &&) = default; +}; +} // namespace layers +/** + * NeuralNetwork class is implements MLP. This class is + * used by actual user to create and train networks. + * + */ +class NeuralNetwork { + private: + std::vector layers; // To store layers + /** + * Private Constructor for class NeuralNetwork. This constructor + * is used internally to load model. + * @param config vector containing pair (neurons, activation) + * @param kernals vector containing all pretrained kernals + */ + NeuralNetwork( + const std::vector> &config, + const std::vector>> &kernals) { + // First layer should not have activation + if (config.begin()->second != "none") { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr + << "First layer can't have activation other than none got " + << config.begin()->second; + std::cerr << std::endl; + std::exit(EXIT_FAILURE); + } + // Network should have atleast two layers + if (config.size() <= 1) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Invalid size of network, "; + std::cerr << "Atleast two layers are required"; + std::exit(EXIT_FAILURE); + } + // Reconstructing all pretrained layers + for (size_t i = 0; i < config.size(); i++) { + layers.emplace_back(neural_network::layers::DenseLayer( + config[i].first, config[i].second, kernals[i])); + } + std::cout << "INFO: Network constructed successfully" << std::endl; + } + /** + * Private function to get detailed predictions (i.e. + * activated neuron values). This function is used in + * backpropagation, single predict and batch predict. + * @param X input vector + */ + std::vector>> + __detailed_single_prediction(const std::vector> &X) { + std::vector>> details; + std::vector> current_pass = X; + details.emplace_back(X); + for (const auto &l : layers) { + current_pass = multiply(current_pass, l.kernal); + current_pass = apply_function(current_pass, l.activation_function); + details.emplace_back(current_pass); + } + return details; + } + + public: + /** + * Default Constructor for class NeuralNetwork. This constructor + * is used to create empty variable of type NeuralNetwork class. + */ + NeuralNetwork() = default; + + /** + * Constructor for class NeuralNetwork. This constructor + * is used by user. + * @param config vector containing pair (neurons, activation) + */ + explicit NeuralNetwork( + const std::vector> &config) { + // First layer should not have activation + if (config.begin()->second != "none") { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr + << "First layer can't have activation other than none got " + << config.begin()->second; + std::cerr << std::endl; + std::exit(EXIT_FAILURE); + } + // Network should have atleast two layers + if (config.size() <= 1) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Invalid size of network, "; + std::cerr << "Atleast two layers are required"; + std::exit(EXIT_FAILURE); + } + // Separately creating first layer so it can have unit matrix + // as kernal. + layers.push_back(neural_network::layers::DenseLayer( + config[0].first, config[0].second, + {config[0].first, config[0].first}, false)); + // Creating remaining layers + for (size_t i = 1; i < config.size(); i++) { + layers.push_back(neural_network::layers::DenseLayer( + config[i].first, config[i].second, + {config[i - 1].first, config[i].first}, true)); + } + std::cout << "INFO: Network constructed successfully" << std::endl; + } + + /** + * Copy Constructor for class NeuralNetwork. + * + * @param model instance of class to be copied. + */ + NeuralNetwork(const NeuralNetwork &model) = default; + + /** + * Destructor for class NeuralNetwork. + */ + ~NeuralNetwork() = default; + + /** + * Copy assignment operator for class NeuralNetwork + */ + NeuralNetwork &operator=(const NeuralNetwork &model) = default; + + /** + * Move constructor for class NeuralNetwork + */ + NeuralNetwork(NeuralNetwork &&) = default; + + /** + * Move assignment operator for class NeuralNetwork + */ + NeuralNetwork &operator=(NeuralNetwork &&) = default; + + /** + * Function to get X and Y from csv file (where X = data, Y = label) + * @param file_name csv file name + * @param last_label flag for whether label is in first or last column + * @param normalize flag for whether to normalize data + * @param slip_lines number of lines to skip + * @return returns pair of X and Y + */ + std::pair>>, + std::vector>>> + get_XY_from_csv(const std::string &file_name, const bool &last_label, + const bool &normalize, const int &slip_lines = 1) { + std::ifstream in_file; // Ifstream to read file + in_file.open(file_name.c_str(), std::ios::in); // Open file + // If there is any problem in opening file + if (!in_file.is_open()) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Unable to open file: " << file_name << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector>> X, + Y; // To store X and Y + std::string line; // To store each line + // Skip lines + for (int i = 0; i < slip_lines; i++) { + std::getline(in_file, line, '\n'); // Ignore line + } + // While file has information + while (!in_file.eof() && std::getline(in_file, line, '\n')) { + std::valarray x_data, + y_data; // To store single sample and label + std::stringstream ss(line); // Constructing stringstream from line + std::string token; // To store each token in line (seprated by ',') + while (std::getline(ss, token, ',')) { // For each token + // Insert numerical value of token in x_data + x_data = insert_element(x_data, std::stod(token)); } - } // namespace activations - /** \namespace util_functions - * \brief Various utility functions used in Neural network - */ - namespace util_functions { - /** - * Square function - * @param X Value - * @return Returns x * x - */ - double square(const double &x) { - return x * x; + // If label is in last column + if (last_label) { + y_data.resize(this->layers.back().neurons); + // If task is classification + if (y_data.size() > 1) { + y_data[x_data[x_data.size() - 1]] = 1; + } + // If task is regrssion (of single value) + else { + y_data[0] = x_data[x_data.size() - 1]; + } + x_data = pop_back(x_data); // Remove label from x_data + } else { + y_data.resize(this->layers.back().neurons); + // If task is classification + if (y_data.size() > 1) { + y_data[x_data[x_data.size() - 1]] = 1; + } + // If task is regrssion (of single value) + else { + y_data[0] = x_data[x_data.size() - 1]; + } + x_data = pop_front(x_data); // Remove label from x_data } - /** - * Identity function - * @param X Value - * @return Returns x - */ - double identity_function(const double &x) { - return x; + // Push collected X_data and y_data in X and Y + X.push_back({x_data}); + Y.push_back({y_data}); + } + // Normalize training data if flag is set + if (normalize) { + // Scale data between 0 and 1 using min-max scaler + X = minmax_scaler(X, 0.01, 1.0); + } + in_file.close(); // Closing file + return make_pair(X, Y); // Return pair of X and Y + } + + /** + * Function to get prediction of model on single sample. + * @param X array of feature vectors + * @return returns predictions as vector + */ + std::vector> single_predict( + const std::vector> &X) { + // Get activations of all layers + auto activations = this->__detailed_single_prediction(X); + // Return activations of last layer (actual predicted values) + return activations.back(); + } + + /** + * Function to get prediction of model on batch + * @param X array of feature vectors + * @return returns predicted values as vector + */ + std::vector>> batch_predict( + const std::vector>> &X) { + // Store predicted values + std::vector>> predicted_batch( + X.size()); + for (size_t i = 0; i < X.size(); i++) { // For every sample + // Push predicted values + predicted_batch[i] = this->single_predict(X[i]); + } + return predicted_batch; // Return predicted values + } + + /** + * Function to fit model on supplied data + * @param X array of feature vectors + * @param Y array of target values + * @param epochs number of epochs (default = 100) + * @param learning_rate learning rate (default = 0.01) + * @param batch_size batch size for gradient descent (default = 32) + * @param shuffle flag for whether to shuffle data (default = true) + */ + void fit(const std::vector>> &X_, + const std::vector>> &Y_, + const int &epochs = 100, const double &learning_rate = 0.01, + const size_t &batch_size = 32, const bool &shuffle = true) { + std::vector>> X = X_, Y = Y_; + // Both label and input data should have same size + if (X.size() != Y.size()) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "X and Y in fit have different sizes" << std::endl; + std::exit(EXIT_FAILURE); + } + std::cout << "INFO: Training Started" << std::endl; + for (int epoch = 1; epoch <= epochs; epoch++) { // For every epoch + // Shuffle X and Y if flag is set + if (shuffle) { + equal_shuffle(X, Y); } - } // namespace util_functions - /** \namespace layers - * \brief This namespace contains layers used - * in MLP. - */ - namespace layers { - /** - * neural_network::layers::DenseLayer class is used to store all necessary information about - * the layers (i.e. neurons, activation and kernal). This class - * is used by NeuralNetwork class to store layers. - * - */ - class DenseLayer { - public: - // To store activation function and it's derivative - double (*activation_function)(const double &); - double (*dactivation_function)(const double &); - int neurons; // To store number of neurons (used in summary) - std::string activation; // To store activation name (used in summary) - std::vector > kernal; // To store kernal (aka weights) - - /** - * Constructor for neural_network::layers::DenseLayer class - * @param neurons number of neurons - * @param activation activation function for layer - * @param kernal_shape shape of kernal - * @param random_kernal flag for whether to intialize kernal randomly - */ - DenseLayer(const int &neurons, - const std::string &activation, - const std::pair &kernal_shape, - const bool &random_kernal) { - // Choosing activation (and it's derivative) - if (activation == "sigmoid") { - activation_function = neural_network::activations::sigmoid; - dactivation_function = neural_network::activations::sigmoid; - } - else if (activation == "relu") { - activation_function = neural_network::activations::relu; - dactivation_function = neural_network::activations::drelu; - } - else if (activation == "tanh") { - activation_function = neural_network::activations::tanh; - dactivation_function = neural_network::activations::dtanh; - } - else if (activation == "none") { - // Set identity function in casse of none is supplied - activation_function = neural_network::util_functions::identity_function; - dactivation_function = neural_network::util_functions::identity_function; - } - else { - // If supplied activation is invalid - std::cerr << "ERROR: Invalid argument for layer -> constructor -> activation, "; - std::cerr << "Expected from {none, sigmoid, relu, tanh} got "; - std::cerr << activation << std::endl; - std::exit(EXIT_FAILURE); - } - this -> activation = activation; // Setting activation name - this -> neurons = neurons; // Setting number of neurons - // Initialize kernal according to flag - if(random_kernal) { - uniform_random_initialization(kernal, kernal_shape, -1.0, 1.0); - } - else { - unit_matrix_initialization(kernal, kernal_shape); - } + auto start = + std::chrono::high_resolution_clock::now(); // Start clock + double loss = 0, + acc = 0; // Intialize performance metrics with zero + // For each starting index of batch + for (size_t batch_start = 0; batch_start < X.size(); + batch_start += batch_size) { + for (size_t i = batch_start; + i < std::min(X.size(), batch_start + batch_size); i++) { + std::vector> grad, cur_error, + predicted; + auto activations = this->__detailed_single_prediction(X[i]); + // Gradients vector to store gradients for all layers + // They will be averaged and applied to kernal + std::vector>> gradients; + gradients.resize(this->layers.size()); + // First intialize gradients to zero + for (size_t i = 0; i < gradients.size(); i++) { + zeroes_initialization( + gradients[i], get_shape(this->layers[i].kernal)); } - /** - * Constructor for neural_network::layers::DenseLayer class - * @param neurons number of neurons - * @param activation activation function for layer - * @param kernal values of kernal (useful in loading model) - */ - DenseLayer (const int &neurons, - const std::string &activation, - const std::vector > &kernal) { - // Choosing activation (and it's derivative) - if (activation == "sigmoid") { - activation_function = neural_network::activations::sigmoid; - dactivation_function = neural_network::activations::sigmoid; - } - else if (activation == "relu") { - activation_function = neural_network::activations::relu; - dactivation_function = neural_network::activations::drelu; - } - else if (activation == "tanh") { - activation_function = neural_network::activations::tanh; - dactivation_function = neural_network::activations::dtanh; - } - else if (activation == "none") { - // Set identity function in casse of none is supplied - activation_function = neural_network::util_functions::identity_function; - dactivation_function = neural_network::util_functions::identity_function; - } - else { - // If supplied activation is invalid - std::cerr << "ERROR: Invalid argument for layer -> constructor -> activation, "; - std::cerr << "Expected from {none, sigmoid, relu, tanh} got "; - std::cerr << activation << std::endl; - std::exit(EXIT_FAILURE); - } - this -> activation = activation; // Setting activation name - this -> neurons = neurons; // Setting number of neurons - this -> kernal = kernal; // Setting supplied kernal values + predicted = activations.back(); // Predicted vector + cur_error = predicted - Y[i]; // Absoulute error + // Calculating loss with MSE + loss += sum(apply_function( + cur_error, neural_network::util_functions::square)); + // If prediction is correct + if (argmax(predicted) == argmax(Y[i])) { + acc += 1; } - - /** - * Copy Constructor for class DenseLayer. - * - * @param model instance of class to be copied. - */ - DenseLayer(const DenseLayer &layer) = default; + // For every layer (except first) starting from last one + for (size_t j = this->layers.size() - 1; j >= 1; j--) { + // Backpropogating errors + cur_error = hadamard_product( + cur_error, + apply_function( + activations[j + 1], + this->layers[j].dactivation_function)); + // Calculating gradient for current layer + grad = multiply(transpose(activations[j]), cur_error); + // Change error according to current kernal values + cur_error = multiply(cur_error, + transpose(this->layers[j].kernal)); + // Adding gradient values to collection of gradients + gradients[j] = gradients[j] + grad / double(batch_size); + } + // Applying gradients + for (size_t j = this->layers.size() - 1; j >= 1; j--) { + // Updating kernal (aka weights) + this->layers[j].kernal = this->layers[j].kernal - + gradients[j] * learning_rate; + } + } + } + auto stop = + std::chrono::high_resolution_clock::now(); // Stoping the clock + // Calculate time taken by epoch + auto duration = + std::chrono::duration_cast(stop - + start); + loss /= X.size(); // Averaging loss + acc /= X.size(); // Averaging accuracy + std::cout.precision(4); // set output precision to 4 + // Printing training stats + std::cout << "Training: Epoch " << epoch << '/' << epochs; + std::cout << ", Loss: " << loss; + std::cout << ", Accuracy: " << acc; + std::cout << ", Taken time: " << duration.count() / 1e6 + << " seconds"; + std::cout << std::endl; + } + return; + } - /** - * Destructor for class DenseLayer. - */ - ~DenseLayer() = default; + /** + * Function to fit model on data stored in csv file + * @param file_name csv file name + * @param last_label flag for whether label is in first or last column + * @param epochs number of epochs + * @param learning_rate learning rate + * @param normalize flag for whether to normalize data + * @param slip_lines number of lines to skip + * @param batch_size batch size for gradient descent (default = 32) + * @param shuffle flag for whether to shuffle data (default = true) + */ + void fit_from_csv(const std::string &file_name, const bool &last_label, + const int &epochs, const double &learning_rate, + const bool &normalize, const int &slip_lines = 1, + const size_t &batch_size = 32, + const bool &shuffle = true) { + // Getting training data from csv file + auto data = + this->get_XY_from_csv(file_name, last_label, normalize, slip_lines); + // Fit the model on training data + this->fit(data.first, data.second, epochs, learning_rate, batch_size, + shuffle); + return; + } - /** - * Copy assignment operator for class DenseLayer - */ - DenseLayer& operator = (const DenseLayer &layer) = default; + /** + * Function to evaluate model on supplied data + * @param X array of feature vectors (input data) + * @param Y array of target values (label) + */ + void evaluate(const std::vector>> &X, + const std::vector>> &Y) { + std::cout << "INFO: Evaluation Started" << std::endl; + double acc = 0, loss = 0; // intialize performance metrics with zero + for (size_t i = 0; i < X.size(); i++) { // For every sample in input + // Get predictions + std::vector> pred = + this->single_predict(X[i]); + // If predicted class is correct + if (argmax(pred) == argmax(Y[i])) { + acc += 1; // Increment accuracy + } + // Calculating loss - Mean Squared Error + loss += sum(apply_function((Y[i] - pred), + neural_network::util_functions::square) * + 0.5); + } + acc /= X.size(); // Averaging accuracy + loss /= X.size(); // Averaging loss + // Prinitng performance of the model + std::cout << "Evaluation: Loss: " << loss; + std::cout << ", Accuracy: " << acc << std::endl; + return; + } - /** - * Move constructor for class DenseLayer - */ - DenseLayer(DenseLayer &&) = default; + /** + * Function to evaluate model on data stored in csv file + * @param file_name csv file name + * @param last_label flag for whether label is in first or last column + * @param normalize flag for whether to normalize data + * @param slip_lines number of lines to skip + */ + void evaluate_from_csv(const std::string &file_name, const bool &last_label, + const bool &normalize, const int &slip_lines = 1) { + // Getting training data from csv file + auto data = + this->get_XY_from_csv(file_name, last_label, normalize, slip_lines); + // Evaluating model + this->evaluate(data.first, data.second); + return; + } - /** - * Move assignment operator for class DenseLayer - */ - DenseLayer& operator = (DenseLayer &&) = default; - }; - } // namespace layers + /** + * Function to save current model. + * @param file_name file name to save model (*.model) + */ + void save_model(const std::string &_file_name) { + std::string file_name = _file_name; + // Adding ".model" extension if it is not already there in name + if (file_name.find(".model") == file_name.npos) { + file_name += ".model"; + } + std::ofstream out_file; // Ofstream to write in file + // Open file in out|trunc mode + out_file.open(file_name.c_str(), + std::ofstream::out | std::ofstream::trunc); + // If there is any problem in opening file + if (!out_file.is_open()) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Unable to open file: " << file_name << std::endl; + std::exit(EXIT_FAILURE); + } /** - * NeuralNetwork class is implements MLP. This class is - * used by actual user to create and train networks. - * - */ - class NeuralNetwork { - private: - std::vector layers; // To store layers - /** - * Private Constructor for class NeuralNetwork. This constructor - * is used internally to load model. - * @param config vector containing pair (neurons, activation) - * @param kernals vector containing all pretrained kernals - */ - NeuralNetwork(const std::vector > &config, - const std::vector >> &kernals) { - // First layer should not have activation - if(config.begin() -> second != "none") { - std::cerr << "ERROR: First layer can't have activation other than none"; - std::cerr << std::endl; - std::exit(EXIT_FAILURE); - } - // Network should have atleast two layers - if(config.size() <= 1) { - std::cerr << "ERROR: Invalid size of network, "; - std::cerr << "Atleast two layers are required"; - std::exit(EXIT_FAILURE); - } - // Reconstructing all pretrained layers - for(size_t i = 0; i < config.size(); i++) { - layers.emplace_back(neural_network::layers::DenseLayer(config[i].first, - config[i].second, - kernals[i])); - } - std::cout << "INFO: Network constructed successfully" << std::endl; + Format in which model is saved: + + total_layers + neurons(1st neural_network::layers::DenseLayer) activation_name(1st + neural_network::layers::DenseLayer) kernal_shape(1st + neural_network::layers::DenseLayer) kernal_values + . + . + . + neurons(Nth neural_network::layers::DenseLayer) activation_name(Nth + neural_network::layers::DenseLayer) kernal_shape(Nth + neural_network::layers::DenseLayer) kernal_value + + For Example, pretrained model with 3 layers: +
+            3
+            4 none
+            4 4
+            1 0 0 0
+            0 1 0 0
+            0 0 1 0
+            0 0 0 1
+            6 relu
+            4 6
+            -1.88963 -3.61165 1.30757 -0.443906 -2.41039 -2.69653
+            -0.684753 0.0891452 0.795294 -2.39619 2.73377 0.318202
+            -2.91451 -4.43249 -0.804187 2.51995 -6.97524 -1.07049
+            -0.571531 -1.81689 -1.24485 1.92264 -2.81322 1.01741
+            3 sigmoid
+            6 3
+            0.390267 -0.391703 -0.0989607
+            0.499234 -0.564539 -0.28097
+            0.553386 -0.153974 -1.92493
+            -2.01336 -0.0219682 1.44145
+            1.72853 -0.465264 -0.705373
+            -0.908409 -0.740547 0.376416
+            
+ */ + // Saving model in the same format + out_file << layers.size(); + out_file << std::endl; + for (const auto &layer : this->layers) { + out_file << layer.neurons << ' ' << layer.activation << std::endl; + const auto shape = get_shape(layer.kernal); + out_file << shape.first << ' ' << shape.second << std::endl; + for (const auto &row : layer.kernal) { + for (const auto &val : row) { + out_file << val << ' '; } - /** - * Private function to get detailed predictions (i.e. - * activated neuron values). This function is used in - * backpropagation, single predict and batch predict. - * @param X input vector - */ - std::vector>> - __detailed_single_prediction (const std::vector> &X) { - std::vector >> details; - std::vector < std::valarray > current_pass = X; - details.emplace_back(X); - for(const auto &l : layers) { - current_pass = multiply(current_pass, l.kernal); - current_pass = apply_function(current_pass, l.activation_function); - details.emplace_back(current_pass); - } - return details; + out_file << std::endl; + } + } + std::cout << "INFO: Model saved successfully with name : "; + std::cout << file_name << std::endl; + out_file.close(); // Closing file + return; + } + + /** + * Function to load earlier saved model. + * @param file_name file from which model will be loaded (*.model) + * @return instance of NeuralNetwork class with pretrained weights + */ + NeuralNetwork load_model(const std::string &file_name) { + std::ifstream in_file; // Ifstream to read file + in_file.open(file_name.c_str()); // Openinig file + // If there is any problem in opening file + if (!in_file.is_open()) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Unable to open file: " << file_name << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector> config; // To store config + std::vector>> + kernals; // To store pretrained kernals + // Loading model from saved file format + size_t total_layers = 0; + in_file >> total_layers; + for (size_t i = 0; i < total_layers; i++) { + int neurons = 0; + std::string activation; + size_t shape_a = 0, shape_b = 0; + std::vector> kernal; + in_file >> neurons >> activation >> shape_a >> shape_b; + for (size_t r = 0; r < shape_a; r++) { + std::valarray row(shape_b); + for (size_t c = 0; c < shape_b; c++) { + in_file >> row[c]; } - public: - /** - * Default Constructor for class NeuralNetwork. This constructor - * is used to create empty variable of type NeuralNetwork class. - */ - NeuralNetwork() = default; + kernal.push_back(row); + } + config.emplace_back(make_pair(neurons, activation)); + ; + kernals.emplace_back(kernal); + } + std::cout << "INFO: Model loaded successfully" << std::endl; + in_file.close(); // Closing file + return NeuralNetwork( + config, kernals); // Return instance of NeuralNetwork class + } - /** - * Constructor for class NeuralNetwork. This constructor - * is used by user. - * @param config vector containing pair (neurons, activation) - */ - explicit NeuralNetwork(const std::vector > &config) { - // First layer should not have activation - if(config.begin() -> second != "none") { - std::cerr << "ERROR: First layer can't have activation other than none"; - std::cerr << std::endl; - std::exit(EXIT_FAILURE); - } - // Network should have atleast two layers - if(config.size() <= 1) { - std::cerr << "ERROR: Invalid size of network, "; - std::cerr << "Atleast two layers are required"; - std::exit(EXIT_FAILURE); - } - // Separately creating first layer so it can have unit matrix - // as kernal. - layers.push_back(neural_network::layers::DenseLayer(config[0].first, - config[0].second, - {config[0].first, config[0].first}, - false)); - // Creating remaining layers - for(size_t i = 1; i < config.size(); i++) { - layers.push_back(neural_network::layers::DenseLayer(config[i].first, - config[i].second, - {config[i - 1].first, config[i].first}, - true)); - } - std::cout << "INFO: Network constructed successfully" << std::endl; - } - - /** - * Copy Constructor for class NeuralNetwork. - * - * @param model instance of class to be copied. - */ - NeuralNetwork(const NeuralNetwork &model) = default; - - /** - * Destructor for class NeuralNetwork. - */ - ~NeuralNetwork() = default; - - /** - * Copy assignment operator for class NeuralNetwork - */ - NeuralNetwork& operator = (const NeuralNetwork &model) = default; - - /** - * Move constructor for class NeuralNetwork - */ - NeuralNetwork(NeuralNetwork &&) = default; - - /** - * Move assignment operator for class NeuralNetwork - */ - NeuralNetwork& operator = (NeuralNetwork &&) = default; - - /** - * Function to get X and Y from csv file (where X = data, Y = label) - * @param file_name csv file name - * @param last_label flag for whether label is in first or last column - * @param normalize flag for whether to normalize data - * @param slip_lines number of lines to skip - * @return returns pair of X and Y - */ - std::pair>>, std::vector>>> - get_XY_from_csv(const std::string &file_name, - const bool &last_label, - const bool &normalize, - const int &slip_lines = 1) { - std::ifstream in_file; // Ifstream to read file - in_file.open(file_name.c_str(), std::ios::in); // Open file - std::vector >> X, Y; // To store X and Y - std::string line; // To store each line - // Skip lines - for(int i = 0; i < slip_lines; i ++) { - std::getline(in_file, line, '\n'); // Ignore line - } - // While file has information - while(!in_file.eof() && std::getline(in_file, line, '\n')) - { - std::valarray x_data, y_data; // To store single sample and label - std::stringstream ss(line); // Constructing stringstream from line - std::string token; // To store each token in line (seprated by ',') - while(std::getline(ss, token, ',')) { // For each token - // Insert numerical value of token in x_data - x_data = insert_element(x_data, std::stod(token)); - } - // If label is in last column - if(last_label) { - y_data.resize(this -> layers.back().neurons); - // If task is classification - if(y_data.size() > 1) { - y_data[x_data[x_data.size() - 1]] = 1; - } - // If task is regrssion (of single value) - else { - y_data[0] = x_data[x_data.size() - 1]; - } - x_data = pop_back(x_data); // Remove label from x_data - } - else { - y_data.resize(this -> layers.back().neurons); - // If task is classification - if(y_data.size() > 1) { - y_data[x_data[x_data.size() - 1]] = 1; - } - // If task is regrssion (of single value) - else { - y_data[0] = x_data[x_data.size() - 1]; - } - x_data = pop_front(x_data); // Remove label from x_data - } - // Push collected X_data and y_data in X and Y - X.push_back({x_data}); - Y.push_back({y_data}); - } - in_file.close(); - // Normalize training data if flag is set - if(normalize) { - // Scale data between 0 and 1 using min-max scaler - X = minmax_scaler(X, 0.01, 1.0); - } - return make_pair(X, Y); // Return pair of X and Y - } - - /** - * Function to get prediction of model on single sample. - * @param X array of feature vectors - * @return returns predictions as vector - */ - std::vector> - single_predict (const std::vector> &X) { - // Get activations of all layers - auto activations = this -> __detailed_single_prediction(X); - // Return activations of last layer (actual predicted values) - return activations.back(); - } - - /** - * Function to get prediction of model on batch - * @param X array of feature vectors - * @return returns predicted values as vector - */ - std::vector < std::vector >> - batch_predict (const std::vector >> &X) { - // Store predicted values - std::vector < std::vector >> predicted_batch(X.size()); - for(size_t i = 0; i < X.size(); i++) { // For every sample - // Push predicted values - predicted_batch[i] = this -> single_predict(X[i]); - } - return predicted_batch; // Return predicted values - } - - /** - * Function to fit model on supplied data - * @param X array of feature vectors - * @param Y array of target values - * @param epochs number of epochs (default = 100) - * @param learning_rate learning rate (default = 0.01) - * @param batch_size batch size for gradient descent (default = 32) - * @param shuffle flag for whether to shuffle data (default = true) - */ - void fit(const std::vector < std::vector >> &X_, - const std::vector < std::vector >> &Y_, - const int &epochs = 100, - const double &learning_rate = 0.01, - const size_t &batch_size = 32, - const bool &shuffle = true) { - std::vector < std::vector >> X = X_, Y = Y_; - // Both label and input data should have same size - if (X.size() != Y.size()) { - std::cerr << "ERROR : X and Y in fit have different sizes" << std::endl; - std::exit(EXIT_FAILURE); - } - std::cout << "INFO: Training Started" << std::endl; - for (int epoch = 1; epoch <= epochs; epoch++) { // For every epoch - // Shuffle X and Y if flag is set - if(shuffle) { - equal_shuffle(X, Y); - } - auto start = std::chrono::high_resolution_clock::now(); // Start clock - double loss = 0, acc = 0; // Intialize performance metrics with zero - // For each starting index of batch - for(size_t batch_start = 0; batch_start < X.size(); batch_start += batch_size) { - for(size_t i = batch_start; i < std::min(X.size(), batch_start + batch_size); i++) { - std::vector > grad, cur_error, predicted; - auto activations = this -> __detailed_single_prediction(X[i]); - // Gradients vector to store gradients for all layers - // They will be averaged and applied to kernal - std::vector>> gradients; - gradients.resize(this -> layers.size()); - // First intialize gradients to zero - for(size_t i = 0; i < gradients.size(); i++) { - zeroes_initialization(gradients[i], get_shape(this -> layers[i].kernal)); - } - predicted = activations.back(); // Predicted vector - cur_error = predicted - Y[i]; // Absoulute error - // Calculating loss with MSE - loss += sum(apply_function(cur_error, neural_network::util_functions::square)); - // If prediction is correct - if(argmax(predicted) == argmax(Y[i])) { - acc += 1; - } - // For every layer (except first) starting from last one - for(size_t j = this -> layers.size() - 1; j >= 1; j--) { - // Backpropogating errors - cur_error = hadamard_product(cur_error, - apply_function(activations[j + 1], - this -> layers[j].dactivation_function)); - // Calculating gradient for current layer - grad = multiply(transpose(activations[j]), cur_error); - // Change error according to current kernal values - cur_error = multiply(cur_error, transpose(this -> layers[j].kernal)); - // Adding gradient values to collection of gradients - gradients[j] = gradients[j] + grad / double(batch_size); - } - // Applying gradients - for(size_t j = this -> layers.size() - 1; j >= 1; j--) { - // Updating kernal (aka weights) - this -> layers[j].kernal = this -> layers[j].kernal - - gradients[j] * learning_rate; - } - } - } - auto stop = std::chrono::high_resolution_clock::now(); // Stoping the clock - // Calculate time taken by epoch - auto duration = std::chrono::duration_cast(stop - start); - loss /= X.size(); // Averaging loss - acc /= X.size(); // Averaging accuracy - std::cout.precision(4); // set output precision to 4 - // Printing training stats - std::cout << "Training: Epoch " << epoch << '/' << epochs; - std::cout << ", Loss: " << loss; - std::cout << ", Accuracy: " << acc; - std::cout << ", Taken time: " << duration.count() / 1e6 << " seconds"; - std::cout << std::endl; - } - return; - } - - /** - * Function to fit model on data stored in csv file - * @param file_name csv file name - * @param last_label flag for whether label is in first or last column - * @param epochs number of epochs - * @param learning_rate learning rate - * @param normalize flag for whether to normalize data - * @param slip_lines number of lines to skip - * @param batch_size batch size for gradient descent (default = 32) - * @param shuffle flag for whether to shuffle data (default = true) - */ - void fit_from_csv (const std::string &file_name, - const bool &last_label, - const int &epochs, - const double &learning_rate, - const bool &normalize, - const int &slip_lines = 1, - const size_t &batch_size = 32, - const bool &shuffle = true) { - // Getting training data from csv file - auto data = this -> get_XY_from_csv(file_name, last_label, normalize, slip_lines); - // Fit the model on training data - this -> fit(data.first, data.second, epochs, learning_rate, batch_size, shuffle); - return; - } - - /** - * Function to evaluate model on supplied data - * @param X array of feature vectors (input data) - * @param Y array of target values (label) - */ - void evaluate(const std::vector< std::vector >> &X, - const std::vector< std::vector >> &Y) { - std::cout << "INFO: Evaluation Started" << std::endl; - double acc = 0, loss = 0; // intialize performance metrics with zero - for(size_t i = 0; i < X.size(); i++) { // For every sample in input - // Get predictions - std::vector> pred = this -> single_predict(X[i]); - // If predicted class is correct - if(argmax(pred) == argmax(Y[i])) { - acc += 1; // Increment accuracy - } - // Calculating loss - Mean Squared Error - loss += sum(apply_function((Y[i] - pred), - neural_network::util_functions::square) * 0.5); - } - acc /= X.size(); // Averaging accuracy - loss /= X.size(); // Averaging loss - // Prinitng performance of the model - std::cout << "Evaluation: Loss: " << loss; - std::cout << ", Accuracy: " << acc << std::endl; - return; - } - - /** - * Function to evaluate model on data stored in csv file - * @param file_name csv file name - * @param last_label flag for whether label is in first or last column - * @param normalize flag for whether to normalize data - * @param slip_lines number of lines to skip - */ - void evaluate_from_csv (const std::string &file_name, - const bool &last_label, - const bool &normalize, - const int &slip_lines = 1) { - // Getting training data from csv file - auto data = this -> get_XY_from_csv(file_name, last_label, normalize, slip_lines); - // Evaluating model - this -> evaluate(data.first, data.second); - return; - } - - /** - * Function to save current model. - * @param file_name file name to save model (*.model) - */ - void save_model (const std::string &_file_name) { - std::string file_name = _file_name; - // Adding ".model" extension if it is not already there in name - if(file_name.find(".model") == file_name.npos) { - file_name += ".model"; - } - std::ofstream out_file; // Ofstream to write in file - // Open file in out|trunc mode - out_file.open(file_name.c_str(), std::ofstream::out | std::ofstream::trunc); - /** - Format in which model is saved: - - total_layers - neurons(1st neural_network::layers::DenseLayer) activation_name(1st neural_network::layers::DenseLayer) - kernal_shape(1st neural_network::layers::DenseLayer) - kernal_values - . - . - . - neurons(Nth neural_network::layers::DenseLayer) activation_name(Nth neural_network::layers::DenseLayer) - kernal_shape(Nth neural_network::layers::DenseLayer) - kernal_value - - For Example, pretrained model with 3 layers: -
-                        3
-                        4 none
-                        4 4
-                        1 0 0 0 
-                        0 1 0 0 
-                        0 0 1 0 
-                        0 0 0 1 
-                        6 relu
-                        4 6
-                        -1.88963 -3.61165 1.30757 -0.443906 -2.41039 -2.69653 
-                        -0.684753 0.0891452 0.795294 -2.39619 2.73377 0.318202 
-                        -2.91451 -4.43249 -0.804187 2.51995 -6.97524 -1.07049 
-                        -0.571531 -1.81689 -1.24485 1.92264 -2.81322 1.01741 
-                        3 sigmoid
-                        6 3
-                        0.390267 -0.391703 -0.0989607 
-                        0.499234 -0.564539 -0.28097 
-                        0.553386 -0.153974 -1.92493 
-                        -2.01336 -0.0219682 1.44145 
-                        1.72853 -0.465264 -0.705373 
-                        -0.908409 -0.740547 0.376416 
-                        
- */ - // Saving model in the same format - out_file << layers.size(); - out_file << std::endl; - for(const auto &layer : this -> layers) { - out_file << layer.neurons << ' ' << layer.activation << std::endl; - const auto shape = get_shape(layer.kernal); - out_file << shape.first << ' ' << shape.second << std::endl; - for(const auto &row : layer.kernal) { - for(const auto &val : row) { - out_file << val << ' '; - } - out_file << std::endl; - } - } - std::cout << "INFO: Model saved successfully with name : "; - std::cout << file_name << std::endl; - return; - } - - /** - * Function to load earlier saved model. - * @param file_name file from which model will be loaded (*.model) - * @return instance of NeuralNetwork class with pretrained weights - */ - NeuralNetwork load_model (const std::string &file_name) { - std::ifstream in_file; // Ifstream to read file - in_file.open(file_name.c_str()); // Openinig file - std::vector > config; // To store config - std::vector >> kernals; // To store pretrained kernals - // Loading model from saved file format - size_t total_layers = 0; - in_file >> total_layers; - for(size_t i = 0; i < total_layers; i++) { - int neurons = 0; - std::string activation; - size_t shape_a = 0, shape_b = 0; - std::vector> kernal; - in_file >> neurons >> activation >> shape_a >> shape_b; - for(size_t r = 0; r < shape_a; r++) { - std::valarray row(shape_b); - for(size_t c = 0; c < shape_b; c++) { - in_file >> row[c]; - } - kernal.push_back(row); - } - config.emplace_back(make_pair(neurons, activation));; - kernals.emplace_back(kernal); - } - std::cout << "INFO: Model loaded successfully" << std::endl; - return NeuralNetwork(config, kernals); // Return instance of NeuralNetwork class - } - - /** - * Function to print summary of the network. - */ - void summary () { - // Printing Summary - std::cout << "===============================================================" << std::endl; - std::cout << "\t\t+ MODEL SUMMARY +\t\t\n"; - std::cout << "===============================================================" << std::endl; - for(size_t i = 1; i <= layers.size(); i++) { // For every layer - std::cout << i << ")"; - std::cout << " Neurons : " << layers[i - 1].neurons; // number of neurons - std::cout << ", Activation : " << layers[i - 1].activation; // activation - std::cout << ", Kernal Shape : " << get_shape(layers[i - 1].kernal); // kernal shape - std::cout << std::endl; - } - std::cout << "===============================================================" << std::endl; - return; - } - - }; - } // namespace neural_network -} // namespace machine_learning + /** + * Function to print summary of the network. + */ + void summary() { + // Printing Summary + std::cout + << "===============================================================" + << std::endl; + std::cout << "\t\t+ MODEL SUMMARY +\t\t\n"; + std::cout + << "===============================================================" + << std::endl; + for (size_t i = 1; i <= layers.size(); i++) { // For every layer + std::cout << i << ")"; + std::cout << " Neurons : " + << layers[i - 1].neurons; // number of neurons + std::cout << ", Activation : " + << layers[i - 1].activation; // activation + std::cout << ", Kernal Shape : " + << get_shape(layers[i - 1].kernal); // kernal shape + std::cout << std::endl; + } + std::cout + << "===============================================================" + << std::endl; + return; + } +}; +} // namespace neural_network +} // namespace machine_learning /** * Function to test neural network @@ -766,19 +805,24 @@ namespace machine_learning { static void test() { // Creating network with 3 layers for "iris.csv" machine_learning::neural_network::NeuralNetwork myNN = - machine_learning::neural_network::NeuralNetwork({ - {4, "none"}, // First layer with 3 neurons and "none" as activation - {6, "relu"}, // Second layer with 6 neurons and "relu" as activation - {3, "sigmoid"} // Third layer with 3 neurons and "sigmoid" as activation - }); + machine_learning::neural_network::NeuralNetwork({ + {4, "none"}, // First layer with 3 neurons and "none" as activation + {6, + "relu"}, // Second layer with 6 neurons and "relu" as activation + {3, "sigmoid"} // Third layer with 3 neurons and "sigmoid" as + // activation + }); // Printing summary of model myNN.summary(); // Training Model myNN.fit_from_csv("iris.csv", true, 100, 0.3, false, 2, 32, true); // Testing predictions of model - assert(machine_learning::argmax(myNN.single_predict({{5,3.4,1.6,0.4}})) == 0); - assert(machine_learning::argmax(myNN.single_predict({{6.4,2.9,4.3,1.3}})) == 1); - assert(machine_learning::argmax(myNN.single_predict({{6.2,3.4,5.4,2.3}})) == 2); + assert(machine_learning::argmax( + myNN.single_predict({{5, 3.4, 1.6, 0.4}})) == 0); + assert(machine_learning::argmax( + myNN.single_predict({{6.4, 2.9, 4.3, 1.3}})) == 1); + assert(machine_learning::argmax( + myNN.single_predict({{6.2, 3.4, 5.4, 2.3}})) == 2); return; } diff --git a/machine_learning/vector_ops.hpp b/machine_learning/vector_ops.hpp index bb70b5c4f..2e519b4b6 100644 --- a/machine_learning/vector_ops.hpp +++ b/machine_learning/vector_ops.hpp @@ -1,20 +1,21 @@ /** * @file vector_ops.hpp * @author [Deep Raval](https://github.com/imdeep2905) - * - * @brief Various functions for vectors associated with [NeuralNetwork (aka Multilayer Perceptron)] + * + * @brief Various functions for vectors associated with [NeuralNetwork (aka + * Multilayer Perceptron)] * (https://en.wikipedia.org/wiki/Multilayer_perceptron). - * + * */ #ifndef VECTOR_OPS_FOR_NN #define VECTOR_OPS_FOR_NN -#include #include -#include -#include #include +#include #include +#include +#include /** * @namespace machine_learning @@ -31,18 +32,18 @@ template std::ostream &operator<<(std::ostream &out, std::vector> const &A) { // Setting output precision to 4 in case of floating point numbers - out.precision(4); - for(const auto &a : A) { // For each row in A - for(const auto &x : a) { // For each element in row - std::cerr << x << ' '; // print element + out.precision(4); + for (const auto &a : A) { // For each row in A + for (const auto &x : a) { // For each element in row + std::cout << x << ' '; // print element } - std::cerr << std::endl; + std::cout << std::endl; } return out; } /** - * Overloaded operator "<<" to print a pair + * Overloaded operator "<<" to print a pair * @tparam T typename of the pair * @param out std::ostream to output * @param A Pair to be printed @@ -52,7 +53,7 @@ std::ostream &operator<<(std::ostream &out, const std::pair &A) { // Setting output precision to 4 in case of floating point numbers out.precision(4); // printing pair in the form (p, q) - std::cerr << "(" << A.first << ", " << A.second << ")"; + std::cout << "(" << A.first << ", " << A.second << ")"; return out; } @@ -66,10 +67,10 @@ template std::ostream &operator<<(std::ostream &out, const std::valarray &A) { // Setting output precision to 4 in case of floating point numbers out.precision(4); - for(const auto &a : A) { // For every element in the vector. - std::cerr << a << ' '; // Print element + for (const auto &a : A) { // For every element in the vector. + std::cout << a << ' '; // Print element } - std::cerr << std::endl; + std::cout << std::endl; return out; } @@ -81,14 +82,14 @@ std::ostream &operator<<(std::ostream &out, const std::valarray &A) { * @return new resultant vector */ template -std::valarray insert_element(const std::valarray &A, const T &ele) { - std::valarray B; // New 1D vector to store resultant vector - B.resize(A.size() + 1); // Resizing it accordingly - for(size_t i = 0; i < A.size(); i++) { // For every element in A - B[i] = A[i]; // Copy element in B +std::valarray insert_element(const std::valarray &A, const T &ele) { + std::valarray B; // New 1D vector to store resultant vector + B.resize(A.size() + 1); // Resizing it accordingly + for (size_t i = 0; i < A.size(); i++) { // For every element in A + B[i] = A[i]; // Copy element in B } - B[B.size() - 1] = ele; // Inserting new element in last position - return B; // Return resultant vector + B[B.size() - 1] = ele; // Inserting new element in last position + return B; // Return resultant vector } /** @@ -98,13 +99,14 @@ std::valarray insert_element(const std::valarray &A, const T &ele) { * @return new resultant vector */ template -std::valarray pop_front(const std::valarray &A) { - std::valarray B; // New 1D vector to store resultant vector - B.resize(A.size() - 1); // Resizing it accordingly - for(size_t i = 1; i < A.size(); i ++) { // // For every (except first) element in A - B[i - 1] = A[i]; // Copy element in B with left shifted position +std::valarray pop_front(const std::valarray &A) { + std::valarray B; // New 1D vector to store resultant vector + B.resize(A.size() - 1); // Resizing it accordingly + for (size_t i = 1; i < A.size(); + i++) { // // For every (except first) element in A + B[i - 1] = A[i]; // Copy element in B with left shifted position } - return B; // Return resultant vector + return B; // Return resultant vector } /** @@ -114,13 +116,14 @@ std::valarray pop_front(const std::valarray &A) { * @return new resultant vector */ template -std::valarray pop_back(const std::valarray &A) { - std::valarray B; // New 1D vector to store resultant vector - B.resize(A.size() - 1); // Resizing it accordingly - for(size_t i = 0; i < A.size() - 1; i ++) { // For every (except last) element in A - B[i] = A[i]; // Copy element in B +std::valarray pop_back(const std::valarray &A) { + std::valarray B; // New 1D vector to store resultant vector + B.resize(A.size() - 1); // Resizing it accordingly + for (size_t i = 0; i < A.size() - 1; + i++) { // For every (except last) element in A + B[i] = A[i]; // Copy element in B } - return B; // Return resultant vector + return B; // Return resultant vector } /** @@ -130,16 +133,17 @@ std::valarray pop_back(const std::valarray &A) { * @param B Second 3D vector */ template -void equal_shuffle(std::vector < std::vector > > &A, - std::vector < std::vector > > &B) { +void equal_shuffle(std::vector>> &A, + std::vector>> &B) { // If two vectors have different sizes - if(A.size() != B.size()) - { - std::cerr << "ERROR : Can not equally shuffle two vectors with different sizes: "; + if (A.size() != B.size()) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr + << "Can not equally shuffle two vectors with different sizes: "; std::cerr << A.size() << " and " << B.size() << std::endl; std::exit(EXIT_FAILURE); } - for(size_t i = 0; i < A.size(); i++) { // For every element in A and B + for (size_t i = 0; i < A.size(); i++) { // For every element in A and B // Genrating random index < size of A and B std::srand(std::chrono::system_clock::now().time_since_epoch().count()); size_t random_index = std::rand() % A.size(); @@ -159,26 +163,26 @@ void equal_shuffle(std::vector < std::vector > > &A, * @param high upper limit on value */ template -void uniform_random_initialization(std::vector> &A, - const std::pair &shape, - const T &low, - const T &high) { - A.clear(); // Making A empty +void uniform_random_initialization(std::vector> &A, + const std::pair &shape, + const T &low, const T &high) { + A.clear(); // Making A empty // Uniform distribution in range [low, high] - std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count()); - std::uniform_real_distribution distribution(low, high); - for(size_t i = 0; i < shape.first; i++) { // For every row - std::valarray row; // Making empty row which will be inserted in vector + std::default_random_engine generator( + std::chrono::system_clock::now().time_since_epoch().count()); + std::uniform_real_distribution distribution(low, high); + for (size_t i = 0; i < shape.first; i++) { // For every row + std::valarray + row; // Making empty row which will be inserted in vector row.resize(shape.second); - for(auto &r : row) { // For every element in row - r = distribution(generator); // copy random number - } - A.push_back(row); // Insert new row in vector + for (auto &r : row) { // For every element in row + r = distribution(generator); // copy random number + } + A.push_back(row); // Insert new row in vector } return; } - /** * Function to Intialize 2D vector as unit matrix * @tparam T typename of the vector @@ -186,15 +190,15 @@ void uniform_random_initialization(std::vector> &A, * @param shape required shape */ template -void unit_matrix_initialization(std::vector> &A, - const std::pair &shape - ) { - A.clear(); // Making A empty - for(size_t i = 0; i < shape.first; i++) { - std::valarray row; // Making empty row which will be inserted in vector +void unit_matrix_initialization(std::vector> &A, + const std::pair &shape) { + A.clear(); // Making A empty + for (size_t i = 0; i < shape.first; i++) { + std::valarray + row; // Making empty row which will be inserted in vector row.resize(shape.second); - row[i] = T(1); // Insert 1 at ith position - A.push_back(row); // Insert new row in vector + row[i] = T(1); // Insert 1 at ith position + A.push_back(row); // Insert new row in vector } return; } @@ -206,14 +210,14 @@ void unit_matrix_initialization(std::vector> &A, * @param shape required shape */ template -void zeroes_initialization(std::vector> &A, - const std::pair &shape - ) { - A.clear(); // Making A empty - for(size_t i = 0; i < shape.first; i++) { - std::valarray row; // Making empty row which will be inserted in vector - row.resize(shape.second); // By default all elements are zero - A.push_back(row); // Insert new row in vector +void zeroes_initialization(std::vector> &A, + const std::pair &shape) { + A.clear(); // Making A empty + for (size_t i = 0; i < shape.first; i++) { + std::valarray + row; // Making empty row which will be inserted in vector + row.resize(shape.second); // By default all elements are zero + A.push_back(row); // Insert new row in vector } return; } @@ -226,11 +230,11 @@ void zeroes_initialization(std::vector> &A, */ template T sum(const std::vector> &A) { - T cur_sum = 0; // Initially sum is zero - for(const auto &a : A) { // For every row in A - cur_sum += a.sum(); // Add sum of that row to current sum + T cur_sum = 0; // Initially sum is zero + for (const auto &a : A) { // For every row in A + cur_sum += a.sum(); // Add sum of that row to current sum } - return cur_sum; // Return sum + return cur_sum; // Return sum } /** @@ -242,52 +246,59 @@ T sum(const std::vector> &A) { template std::pair get_shape(const std::vector> &A) { const size_t sub_size = (*A.begin()).size(); - for(const auto &a : A) { + for (const auto &a : A) { // If supplied vector don't have same shape in all rows - if(a.size() != sub_size) { - std::cerr << "ERROR: (get_shape) Supplied vector is not 2D Matrix" << std::endl; + if (a.size() != sub_size) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Supplied vector is not 2D Matrix" << std::endl; std::exit(EXIT_FAILURE); } } - return std::make_pair(A.size(), sub_size); // Return shape as pair + return std::make_pair(A.size(), sub_size); // Return shape as pair } /** * Function to scale given 3D vector using min-max scaler * @tparam T typename of the vector * @param A 3D vector which will be scaled - * @param low new minimum value + * @param low new minimum value * @param high new maximum value * @return new scaled 3D vector */ template -std::vector>> -minmax_scaler(const std::vector>> &A, const T &low, const T &high) { - std::vector>> B = A; // Copying into new vector B - const auto shape = get_shape(B[0]); // Storing shape of B's every element - // As this function is used for scaling training data vector should be of shape (1, X) - if(shape.first != 1) { - std::cerr << "ERROR: (MinMax Scaling) Supplied vector is not supported for minmax scaling, shape: "; +std::vector>> minmax_scaler( + const std::vector>> &A, const T &low, + const T &high) { + std::vector>> B = + A; // Copying into new vector B + const auto shape = get_shape(B[0]); // Storing shape of B's every element + // As this function is used for scaling training data vector should be of + // shape (1, X) + if (shape.first != 1) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr + << "Supplied vector is not supported for minmax scaling, shape: "; std::cerr << shape << std::endl; std::exit(EXIT_FAILURE); } - for(size_t i = 0; i < shape.second; i++) { - T min = B[0][0][i], max = B[0][0][i]; - for(size_t j = 0; j < B.size(); j++) { + for (size_t i = 0; i < shape.second; i++) { + T min = B[0][0][i], max = B[0][0][i]; + for (size_t j = 0; j < B.size(); j++) { // Updating minimum and maximum values min = std::min(min, B[j][0][i]); max = std::max(max, B[j][0][i]); } - for(size_t j = 0; j < B.size(); j++) { + for (size_t j = 0; j < B.size(); j++) { // Applying min-max scaler formula - B[j][0][i] = ((B[j][0][i] - min) / (max - min)) * (high - low) + low; + B[j][0][i] = + ((B[j][0][i] - min) / (max - min)) * (high - low) + low; } } - return B; // Return new resultant 3D vector + return B; // Return new resultant 3D vector } /** - * Function to get index of maximum element in 2D vector + * Function to get index of maximum element in 2D vector * @tparam T typename of the vector * @param A 2D vector for which maximum index is required * @return index of maximum element @@ -295,13 +306,16 @@ minmax_scaler(const std::vector>> &A, const T &low, template size_t argmax(const std::vector> &A) { const auto shape = get_shape(A); - // As this function is used on predicted (or target) vector, shape should be (1, X) - if(shape.first != 1) { - std::cerr << "ERROR: (argmax) Supplied vector is ineligible for argmax" << std::endl; - std::exit(EXIT_FAILURE); + // As this function is used on predicted (or target) vector, shape should be + // (1, X) + if (shape.first != 1) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Supplied vector is ineligible for argmax" << std::endl; + std::exit(EXIT_FAILURE); } // Return distance of max element from first element (i.e. index) - return std::distance(std::begin(A[0]), std::max_element(std::begin(A[0]), std::end(A[0]))); + return std::distance(std::begin(A[0]), + std::max_element(std::begin(A[0]), std::end(A[0]))); } /** @@ -311,46 +325,51 @@ size_t argmax(const std::vector> &A) { * @param func Function to be applied * @return new resultant vector */ -template -std::vector > apply_function(const std::vector > &A, - T (*func) (const T &)) { - std::vector> B = A; // New vector to store resultant vector - for(auto &b : B) { // For every row in vector - b = b.apply(func); // Apply function to that row +template +std::vector> apply_function( + const std::vector> &A, T (*func)(const T &)) { + std::vector> B = + A; // New vector to store resultant vector + for (auto &b : B) { // For every row in vector + b = b.apply(func); // Apply function to that row } - return B; // Return new resultant 2D vector + return B; // Return new resultant 2D vector } /** - * Overloaded operator "*" to multiply given 2D vector with scaler + * Overloaded operator "*" to multiply given 2D vector with scaler * @tparam T typename of both vector and the scaler * @param A 2D vector to which scaler will be multiplied * @param val Scaler value which will be multiplied * @return new resultant vector */ template -std::vector > operator * (const std::vector> &A, const T& val) { - std::vector> B = A; // New vector to store resultant vector - for(auto &b : B) { // For every row in vector - b = b * val; // Multiply row with scaler +std::vector> operator*(const std::vector> &A, + const T &val) { + std::vector> B = + A; // New vector to store resultant vector + for (auto &b : B) { // For every row in vector + b = b * val; // Multiply row with scaler } - return B; // Return new resultant 2D vector + return B; // Return new resultant 2D vector } /** - * Overloaded operator "/" to divide given 2D vector with scaler + * Overloaded operator "/" to divide given 2D vector with scaler * @tparam T typename of the vector and the scaler * @param A 2D vector to which scaler will be divided * @param val Scaler value which will be divided * @return new resultant vector */ template -std::vector > operator / (const std::vector> &A, const T& val) { - std::vector> B = A; // New vector to store resultant vector - for(auto &b : B) { // For every row in vector - b = b / val; // Divide row with scaler +std::vector> operator/(const std::vector> &A, + const T &val) { + std::vector> B = + A; // New vector to store resultant vector + for (auto &b : B) { // For every row in vector + b = b / val; // Divide row with scaler } - return B; // Return new resultant 2D vector + return B; // Return new resultant 2D vector } /** @@ -360,125 +379,136 @@ std::vector > operator / (const std::vector> * @return new resultant vector */ template -std::vector > transpose(const std::vector> &A) { - const auto shape = get_shape(A); // Current shape of vector - std::vector > B; // New vector to store result +std::vector> transpose( + const std::vector> &A) { + const auto shape = get_shape(A); // Current shape of vector + std::vector> B; // New vector to store result // Storing transpose values of A in B - for(size_t j = 0; j < shape.second; j++) { - std::valarray row; + for (size_t j = 0; j < shape.second; j++) { + std::valarray row; row.resize(shape.first); - for(size_t i = 0; i < shape.first; i++) { + for (size_t i = 0; i < shape.first; i++) { row[i] = A[i][j]; } B.push_back(row); } - return B; // Return new resultant 2D vector + return B; // Return new resultant 2D vector } /** * Overloaded operator "+" to add two 2D vectors * @tparam T typename of the vector - * @param A First 2D vector + * @param A First 2D vector * @param B Second 2D vector * @return new resultant vector */ template -std::vector > operator + (const std::vector> &A, const std::vector> &B) { +std::vector> operator+( + const std::vector> &A, + const std::vector> &B) { const auto shape_a = get_shape(A); const auto shape_b = get_shape(B); // If vectors don't have equal shape - if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) { - std::cerr << "ERROR: (vector addition) Supplied vectors have different shapes "; + if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Supplied vectors have different shapes "; std::cerr << shape_a << " and " << shape_b << std::endl; std::exit(EXIT_FAILURE); } - std::vector> C; - for(size_t i = 0; i < A.size(); i++) { // For every row - C.push_back(A[i] + B[i]); // Elementwise addition + std::vector> C; + for (size_t i = 0; i < A.size(); i++) { // For every row + C.push_back(A[i] + B[i]); // Elementwise addition } - return C; // Return new resultant 2D vector + return C; // Return new resultant 2D vector } /** * Overloaded operator "-" to add subtract 2D vectors * @tparam T typename of the vector - * @param A First 2D vector + * @param A First 2D vector * @param B Second 2D vector * @return new resultant vector */ template -std::vector > operator - (const std::vector> &A, const std::vector> &B) { +std::vector> operator-( + const std::vector> &A, + const std::vector> &B) { const auto shape_a = get_shape(A); const auto shape_b = get_shape(B); // If vectors don't have equal shape - if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) { - std::cerr << "ERROR: (vector subtraction) Supplied vectors have different shapes "; + if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Supplied vectors have different shapes "; std::cerr << shape_a << " and " << shape_b << std::endl; std::exit(EXIT_FAILURE); } - std::vector> C; // Vector to store result - for(size_t i = 0; i < A.size(); i++) { // For every row - C.push_back(A[i] - B[i]); // Elementwise substraction + std::vector> C; // Vector to store result + for (size_t i = 0; i < A.size(); i++) { // For every row + C.push_back(A[i] - B[i]); // Elementwise substraction } - return C; // Return new resultant 2D vector + return C; // Return new resultant 2D vector } /** * Function to multiply two 2D vectors * @tparam T typename of the vector - * @param A First 2D vector + * @param A First 2D vector * @param B Second 2D vector * @return new resultant vector */ template -std::vector > multiply(const std::vector> &A, const std::vector> &B) { +std::vector> multiply(const std::vector> &A, + const std::vector> &B) { const auto shape_a = get_shape(A); const auto shape_b = get_shape(B); // If vectors are not eligible for multiplication - if(shape_a.second != shape_b.first ) { - std::cerr << "ERROR: (multiply) Supplied vectors are not eligible for multiplication "; + if (shape_a.second != shape_b.first) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Vectors are not eligible for multiplication "; std::cerr << shape_a << " and " << shape_b << std::endl; std::exit(EXIT_FAILURE); } - std::vector> C; // Vector to store result - // Normal matrix multiplication + std::vector> C; // Vector to store result + // Normal matrix multiplication for (size_t i = 0; i < shape_a.first; i++) { std::valarray row; row.resize(shape_b.second); - for(size_t j = 0; j < shape_b.second; j++) { - for(size_t k = 0; k < shape_a.second; k++) { + for (size_t j = 0; j < shape_b.second; j++) { + for (size_t k = 0; k < shape_a.second; k++) { row[j] += A[i][k] * B[k][j]; } } C.push_back(row); } - return C; // Return new resultant 2D vector + return C; // Return new resultant 2D vector } /** * Function to get hadamard product of two 2D vectors * @tparam T typename of the vector - * @param A First 2D vector + * @param A First 2D vector * @param B Second 2D vector * @return new resultant vector */ template -std::vector > hadamard_product(const std::vector> &A, const std::vector> &B) { +std::vector> hadamard_product( + const std::vector> &A, + const std::vector> &B) { const auto shape_a = get_shape(A); const auto shape_b = get_shape(B); // If vectors are not eligible for hadamard product - if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) { - std::cerr << "ERROR: (hadamard_product) Supplied vectors have different shapes "; + if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) { + std::cerr << "ERROR (" << __func__ << ") : "; + std::cerr << "Vectors have different shapes "; std::cerr << shape_a << " and " << shape_b << std::endl; std::exit(EXIT_FAILURE); } - std::vector> C; // Vector to store result - for(size_t i = 0; i < A.size(); i++) { - C.push_back(A[i] * B[i]); // Elementwise multiplication + std::vector> C; // Vector to store result + for (size_t i = 0; i < A.size(); i++) { + C.push_back(A[i] * B[i]); // Elementwise multiplication } - return C; // Return new resultant 2D vector + return C; // Return new resultant 2D vector } } // namespace machine_learning - #endif From f0c218c7899b9c833dd4684dcc099a1ad1acd642 Mon Sep 17 00:00:00 2001 From: David Leal Date: Wed, 26 Aug 2020 12:57:21 -0500 Subject: [PATCH 54/90] [fix/docs]: Improve backtracking/nqueen_print_all_solutions.cpp (#1049) * updating DIRECTORY.md * updating DIRECTORY.md * [fix/docs]: Improve backtracking/nqueen_print_all_solutions.cpp Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- backtracking/nqueen_print_all_solutions.cpp | 83 +++++++++++++++++---- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/backtracking/nqueen_print_all_solutions.cpp b/backtracking/nqueen_print_all_solutions.cpp index e6736da1e..5aa5c71a2 100644 --- a/backtracking/nqueen_print_all_solutions.cpp +++ b/backtracking/nqueen_print_all_solutions.cpp @@ -1,50 +1,103 @@ +/** + * @file + * @brief [Eight Queens](https://en.wikipedia.org/wiki/Eight_queens_puzzle) + * puzzle, printing all solutions + * + * @author [Himani Negi](https://github.com/Himani2000) + * @author [David Leal](https://github.com/Panquesito7) + * + */ #include -#define n 4 +#include -void PrintSol(int Board[n][n]) { +/** + * @namespace backtracking + * @brief Backtracking algorithms + */ +namespace backtracking { +/** + * @namespace n_queens_all_solutions + * @brief Functions for [Eight + * Queens](https://en.wikipedia.org/wiki/Eight_queens_puzzle) puzzle with all solutions. + */ +namespace n_queens_all_solutions { +/** + * Utility function to print matrix + * @tparam n number of matrix size + * @param board matrix where numbers are saved + */ +template +void PrintSol(const std::array, n>& board) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { - std::cout << Board[i][j] << " "; + std::cout << board[i][j] << " "; } std::cout << std::endl; } std::cout << std::endl; } -bool CanIMove(int Board[n][n], int row, int col) { +/** + * Check if a queen can be placed on matrix + * @tparam n number of matrix size + * @param board matrix where numbers are saved + * @param row current index in rows + * @param col current index in columns + * @returns `true` if queen can be placed on matrix + * @returns `false` if queen can't be placed on matrix + */ +template +bool CanIMove(const std::array, n>& board, int row, int col) { /// check in the row for (int i = 0; i < col; i++) { - if (Board[row][i] == 1) + if (board[row][i] == 1) { return false; + } } /// check the first diagonal for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) { - if (Board[i][j] == 1) + if (board[i][j] == 1) { return false; + } } /// check the second diagonal for (int i = row, j = col; i <= n - 1 && j >= 0; i++, j--) { - if (Board[i][j] == 1) + if (board[i][j] == 1) { return false; + } } return true; } -void NQueenSol(int Board[n][n], int col) { +/** + * Solve n queens problem + * @tparam n number of matrix size + * @param board matrix where numbers are saved + * @param col current index in columns + */ +template +void NQueenSol(std::array, n> board, int col) { if (col >= n) { - PrintSol(Board); + PrintSol(board); return; } for (int i = 0; i < n; i++) { - if (CanIMove(Board, i, col)) { - Board[i][col] = 1; - NQueenSol(Board, col + 1); - Board[i][col] = 0; + if (CanIMove(board, i, col)) { + board[i][col] = 1; + NQueenSol(board, col + 1); + board[i][col] = 0; } } } +} // namespace n_queens_all_solutions +} // namespace backtracking +/** + * Main function + */ int main() { - int Board[n][n] = {0}; - NQueenSol(Board, 0); + const int n = 4; + std::array, n> board{0}; + + backtracking::n_queens_all_solutions::NQueenSol(board, 0); } From 3239fcc19ea01f297707ee1f854bc6a6382f9e24 Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Thu, 27 Aug 2020 07:26:49 -0700 Subject: [PATCH 55/90] fix: linter and spacing for is_graph_bipartite. (#1037) * fix: linter and spacing for is_graph_bipartite. * updating DIRECTORY.md * clang-tidy fixes for a49ec9b8d79e62be49587043cf482a9f705e3ed1 * clang-format and clang-tidy fixes for 40a56d2f * Address reviewer's comments. * Fix docs wording. Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- graph/is_graph_bipartite.cpp | 294 ++++++++++++++++++----------------- 1 file changed, 151 insertions(+), 143 deletions(-) diff --git a/graph/is_graph_bipartite.cpp b/graph/is_graph_bipartite.cpp index baadf71fa..2772117fa 100644 --- a/graph/is_graph_bipartite.cpp +++ b/graph/is_graph_bipartite.cpp @@ -1,163 +1,171 @@ /** - * @file + * @file * - * @brief Algorithm to check whether a graph is [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph) - * - * @details - * A graph is a collection of nodes also called vertices and these vertices - * are connected by edges.A bipartite graph is a graph whose vertices can be - * divided into two disjoint and independent sets U and V such that every edge - * connects a vertex in U to one in V. - * - * The given Algorithm will determine whether the given graph is bipartite or not - * - *
- * 	Example - Here is a graph g1 with 5 vertices and is bipartite
- *	
- *		1   4
- *	   / \ / \
- *	  2   3   5
- *	
- *	Example - Here is a graph G2 with 3 vertices and is not bipartite
- *	
- *		1 --- 2
- *		 \   /
- *		   3
- *	
- *	
+ * @brief Algorithm to check whether a graph is + * [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph) + * + * @details + * A graph is a collection of nodes also called vertices and these vertices + * are connected by edges. A graph is bipartite if its vertices can be + * divided into two disjoint and independent sets U and V such that every edge + * connects a vertex in U to one in V. + * + * The algorithm implemented in this file determines whether the given graph + * is bipartite or not. + * + *
+ *  Example - Here is a graph g1 with 5 vertices and is bipartite
+ *
+ *     1   4
+ *    / \ / \
+ *   2   3   5
+ *
+ * Example - Here is a graph G2 with 3 vertices and is not bipartite
+ *
+ *   1 --- 2
+ *    \   /
+ *      3
+ *
+ * 
+ * + * @author [Akshat Vaya](https://github.com/AkVaya) * - * @author [Akshat Vaya](https://github.com/AkVaya) - * */ #include -#include #include +#include /** * @namespace graph * @brief Graph algorithms */ -namespace graph{ - /** - * @namespace is_graph_bipartite - * @brief Functions for checking whether a graph is bipartite or not - */ - namespace is_graph_bipartite{ - /** - * @brief Class for representing graph as an adjacency list. - */ - class Graph { - private: - int n; /// size of the graph - - std::vector > adj; /// adj stores the graph as an adjacency list - - std::vector side; ///stores the side of the vertex - - static const int nax = 5e5 + 1; - - - public: - /** - * @brief Constructor that initializes the graph on creation - */ - explicit Graph(int size = nax){ - n = size; - adj.resize(n); - side.resize(n,-1); - } - - void addEdge(int u, int v); /// function to add edges to our graph - - bool is_bipartite(); /// function to check whether the graph is bipartite or not - - }; - /** - * @brief Function that add an edge between two nodes or vertices of graph - * - * @param u is a node or vertex of graph - * @param v is a node or vertex of graph - */ - void Graph::addEdge(int u, int v) { - adj[u-1].push_back(v-1); - adj[v-1].push_back(u-1); - } - /** - * @brief function that checks whether the graph is bipartite or not - * the function returns true if the graph is a bipartite graph - * the function returns false if the graph is not a bipartite graph - * - * @details - * Here, side refers to the two disjoint subsets of the bipartite graph. - * Initially, the values of side are set to -1 which is an unassigned state. A for loop is run for every vertex of the graph. - * If the current edge has no side assigned to it, then a Breadth First Search operation is performed. - * If two neighbours have the same side then the graph will not be bipartite and the value of check becomes false. - * If and only if each pair of neighbours have different sides, the value of check will be true and hence the graph bipartite. - * - */ - bool Graph::is_bipartite(){ - bool check = true; - std::queue q; - for (int current_edge = 0; current_edge < n; ++current_edge) - { - if(side[current_edge] == -1){ - q.push(current_edge); - side[current_edge] = 0; - while(q.size()){ - int current = q.front(); - q.pop(); - for(auto neighbour : adj[current]){ - if(side[neighbour] == -1){ - side[neighbour] = (1 ^ side[current]); - q.push(neighbour); - } - else{ - check &= (side[neighbour] != side[current]); - } - } - } - } - } - return check; - } - } /// namespace is_graph_bipartite -} /// namespace graph +namespace graph { /** - * Function to test the above algorithm - * @returns none + * @namespace is_graph_bipartite + * @brief Functions for checking whether a graph is bipartite or not */ -static void test(){ - graph::is_graph_bipartite::Graph G1(5); /// creating graph G1 with 5 vertices - /// adding edges to the graphs as per the illustrated example - G1.addEdge(1,2); - G1.addEdge(1,3); - G1.addEdge(3,4); - G1.addEdge(4,5); +namespace is_graph_bipartite { +/** + * @brief Class for representing graph as an adjacency list. + */ +class Graph { + private: + int n; ///< size of the graph - graph::is_graph_bipartite::Graph G2(3); /// creating graph G2 with 3 vertices - /// adding edges to the graphs as per the illustrated example - G2.addEdge(1,2); - G2.addEdge(1,3); - G2.addEdge(2,3); + std::vector > + adj; ///< adj stores the graph as an adjacency list - /// checking whether the graphs are bipartite or not - if(G1.is_bipartite()){ - std::cout<<"The given graph G1 is a bipartite graph\n"; - } - else{ - std::cout<<"The given graph G1 is not a bipartite graph\n"; - } - if(G2.is_bipartite()){ - std::cout<<"The given graph G2 is a bipartite graph\n"; - } - else{ - std::cout<<"The given graph G2 is not a bipartite graph\n"; - } + std::vector side; ///< stores the side of the vertex + + public: + /** + * @brief Constructor that initializes the graph on creation + * @param size number of vertices of the graph + */ + explicit Graph(int size) { + n = size; + adj.resize(n); + side.resize(n, -1); + } + + void addEdge(int u, int v); /// function to add edges to our graph + + bool + is_bipartite(); /// function to check whether the graph is bipartite or not +}; + +/** + * @brief Function that add an edge between two nodes or vertices of graph + * + * @param u is a node or vertex of graph + * @param v is a node or vertex of graph + */ +void Graph::addEdge(int u, int v) { + adj[u - 1].push_back(v - 1); + adj[v - 1].push_back(u - 1); } + +/** + * @brief function that checks whether the graph is bipartite or not + * the function returns true if the graph is a bipartite graph + * the function returns false if the graph is not a bipartite graph + * + * @details + * Here, side refers to the two disjoint subsets of the bipartite graph. + * Initially, the values of side are set to -1 which is an unassigned state. A + * for loop is run for every vertex of the graph. If the current edge has no + * side assigned to it, then a Breadth First Search operation is performed. If + * two neighbours have the same side then the graph will not be bipartite and + * the value of check becomes false. If and only if each pair of neighbours have + * different sides, the value of check will be true and hence the graph + * bipartite. + * + * @returns `true` if th graph is bipartite + * @returns `false` otherwise + */ +bool Graph::is_bipartite() { + bool check = true; + std::queue q; + for (int current_edge = 0; current_edge < n; ++current_edge) { + if (side[current_edge] == -1) { + q.push(current_edge); + side[current_edge] = 0; + while (q.size()) { + int current = q.front(); + q.pop(); + for (auto neighbour : adj[current]) { + if (side[neighbour] == -1) { + side[neighbour] = (1 ^ side[current]); + q.push(neighbour); + } else { + check &= (side[neighbour] != side[current]); + } + } + } + } + } + return check; +} +} // namespace is_graph_bipartite +} // namespace graph + +/** + * Function to test the above algorithm + * @returns none + */ +static void test() { + graph::is_graph_bipartite::Graph G1( + 5); /// creating graph G1 with 5 vertices + /// adding edges to the graphs as per the illustrated example + G1.addEdge(1, 2); + G1.addEdge(1, 3); + G1.addEdge(3, 4); + G1.addEdge(4, 5); + + graph::is_graph_bipartite::Graph G2( + 3); /// creating graph G2 with 3 vertices + /// adding edges to the graphs as per the illustrated example + G2.addEdge(1, 2); + G2.addEdge(1, 3); + G2.addEdge(2, 3); + + /// checking whether the graphs are bipartite or not + if (G1.is_bipartite()) { + std::cout << "The given graph G1 is a bipartite graph\n"; + } else { + std::cout << "The given graph G1 is not a bipartite graph\n"; + } + if (G2.is_bipartite()) { + std::cout << "The given graph G2 is a bipartite graph\n"; + } else { + std::cout << "The given graph G2 is not a bipartite graph\n"; + } +} + /** * Main function */ -int main(){ - test(); ///Testing - return 0; +int main() { + test(); /// Testing + return 0; } From 79fb528dad8b8ae9a13ad31d17c9018a13d5d49a Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Thu, 27 Aug 2020 07:28:31 -0700 Subject: [PATCH 56/90] fix: clang-format for graph/ (#1056) * fix: clang-format for graph/ * remove graph.h --- graph/CMakeLists.txt | 10 ++--- graph/breadth_first_search.cpp | 7 +-- .../bridge_finding_with_tarjan_algorithm.cpp | 9 ++-- graph/depth_first_search_with_stack.cpp | 6 +-- graph/kosaraju.cpp | 17 +++---- graph/kruskal.cpp | 6 +-- graph/lowest_common_ancestor.cpp | 45 +++++++++---------- ...th_ford_fulkerson_and_edmond_karp_algo.cpp | 8 ++-- graph/prim.cpp | 4 +- graph/topological_sort.cpp | 8 ++-- graph/topological_sort_by_kahns_algo.cpp | 7 +-- 11 files changed, 67 insertions(+), 60 deletions(-) diff --git a/graph/CMakeLists.txt b/graph/CMakeLists.txt index 02b8f4840..7f6ea0e69 100644 --- a/graph/CMakeLists.txt +++ b/graph/CMakeLists.txt @@ -1,8 +1,8 @@ -# If necessary, use the RELATIVE flag, otherwise each source file may be listed -# with full pathname. RELATIVE may makes it easier to extract an executable name -# automatically. -file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp ) -# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c ) +#If necessary, use the RELATIVE flag, otherwise each source file may be listed +#with full pathname.RELATIVE may makes it easier to extract an executable name +#automatically. +file(GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp) +#file(GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c ) # AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES) foreach( testsourcefile ${APP_SOURCES} ) # I used a simple string replace, to cut off .cpp. diff --git a/graph/breadth_first_search.cpp b/graph/breadth_first_search.cpp index 0f4f46160..a03a13bcc 100644 --- a/graph/breadth_first_search.cpp +++ b/graph/breadth_first_search.cpp @@ -89,11 +89,12 @@ void add_undirected_edge(std::vector> *graph, int u, int v) { * * @param graph Adjacency list representation of graph * @param start vertex from where traversing starts - * @returns a binary vector indicating which vertices were visited during the search. + * @returns a binary vector indicating which vertices were visited during the + * search. * */ -std::vector breadth_first_search(const std::vector> &graph, - int start) { +std::vector breadth_first_search( + const std::vector> &graph, int start) { /// vector to keep track of visited vertices std::vector visited(graph.size(), false); /// a queue that stores vertices that need to be further explored diff --git a/graph/bridge_finding_with_tarjan_algorithm.cpp b/graph/bridge_finding_with_tarjan_algorithm.cpp index a9f76c033..c4443dd5d 100644 --- a/graph/bridge_finding_with_tarjan_algorithm.cpp +++ b/graph/bridge_finding_with_tarjan_algorithm.cpp @@ -27,14 +27,14 @@ class Solution { bridge.push_back({itr, current_node}); } } - out_time[current_node] = std::min(out_time[current_node], out_time[itr]); + out_time[current_node] = + std::min(out_time[current_node], out_time[itr]); } } public: std::vector> search_bridges( - int n, - const std::vector>& connections) { + int n, const std::vector>& connections) { timer = 0; graph.resize(n); in_time.assign(n, 0); @@ -73,7 +73,8 @@ int main() { * I assumed that the graph is bi-directional and connected. * */ - std::vector> bridges = s1.search_bridges(number_of_node, node); + std::vector> bridges = + s1.search_bridges(number_of_node, node); std::cout << bridges.size() << " bridges found!\n"; for (auto& itr : bridges) { std::cout << itr[0] << " --> " << itr[1] << '\n'; diff --git a/graph/depth_first_search_with_stack.cpp b/graph/depth_first_search_with_stack.cpp index 2d5752322..9810edeb1 100644 --- a/graph/depth_first_search_with_stack.cpp +++ b/graph/depth_first_search_with_stack.cpp @@ -1,14 +1,14 @@ #include #include -#include #include +#include constexpr int WHITE = 0; constexpr int GREY = 1; constexpr int BLACK = 2; constexpr int INF = 99999; -void dfs(const std::vector< std::list > &graph, int start) { +void dfs(const std::vector > &graph, int start) { std::vector checked(graph.size(), WHITE); checked[start] = GREY; std::stack stack; @@ -33,7 +33,7 @@ void dfs(const std::vector< std::list > &graph, int start) { int main() { int n = 0; std::cin >> n; - std::vector< std::list > graph(INF); + std::vector > graph(INF); for (int i = 0; i < n; ++i) { int u = 0, w = 0; std::cin >> u >> w; diff --git a/graph/kosaraju.cpp b/graph/kosaraju.cpp index 5949c0bb8..3b7fdacbb 100644 --- a/graph/kosaraju.cpp +++ b/graph/kosaraju.cpp @@ -3,9 +3,8 @@ */ #include -#include #include - +#include /** * Iterative function/method to print graph: @@ -13,7 +12,7 @@ * @param V number of vertices * @return void **/ -void print(const std::vector< std::vector > &a, int V) { +void print(const std::vector > &a, int V) { for (int i = 0; i < V; i++) { if (!a[i].empty()) { std::cout << "i=" << i << "-->"; @@ -35,7 +34,8 @@ void print(const std::vector< std::vector > &a, int V) { * @param adj adjacency list representation of the graph * @return void **/ -void push_vertex(int v, std::stack *st, std::vector *vis, const std::vector< std::vector > &adj) { +void push_vertex(int v, std::stack *st, std::vector *vis, + const std::vector > &adj) { (*vis)[v] = true; for (auto i = adj[v].begin(); i != adj[v].end(); i++) { if ((*vis)[*i] == false) { @@ -52,7 +52,8 @@ void push_vertex(int v, std::stack *st, std::vector *vis, const std:: * @param grev graph with reversed edges * @return void **/ -void dfs(int v, std::vector *vis, const std::vector< std::vector > &grev) { +void dfs(int v, std::vector *vis, + const std::vector > &grev) { (*vis)[v] = true; // cout<0)) i.e. it returns the count of (number of) strongly connected components (SCCs) in the graph. (variable 'count_scc' within function) **/ -int kosaraju(int V, const std::vector< std::vector > &adj) { +int kosaraju(int V, const std::vector > &adj) { std::vector vis(V, false); std::stack st; for (int v = 0; v < V; v++) { @@ -81,7 +82,7 @@ int kosaraju(int V, const std::vector< std::vector > &adj) { } } // making new graph (grev) with reverse edges as in adj[]: - std::vector< std::vector > grev(V); + std::vector > grev(V); for (int i = 0; i < V + 1; i++) { for (auto j = adj[i].begin(); j != adj[i].end(); j++) { grev[*j].push_back(i); @@ -114,7 +115,7 @@ int main() { int a = 0, b = 0; // a->number of nodes, b->directed edges. std::cin >> a >> b; int m = 0, n = 0; - std::vector< std::vector > adj(a + 1); + std::vector > adj(a + 1); for (int i = 0; i < b; i++) // take total b inputs of 2 vertices each // required to form an edge. { diff --git a/graph/kruskal.cpp b/graph/kruskal.cpp index e179131a1..39f695cb0 100644 --- a/graph/kruskal.cpp +++ b/graph/kruskal.cpp @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include //#include // using namespace boost::multiprecision; const int mx = 1e6 + 5; @@ -12,7 +12,7 @@ ll node, edge; std::vector>> edges; void initial() { for (int i = 0; i < node + edge; ++i) { - parent[i] = i; + parent[i] = i; } } diff --git a/graph/lowest_common_ancestor.cpp b/graph/lowest_common_ancestor.cpp index 11029a5b8..7d5ab42b4 100644 --- a/graph/lowest_common_ancestor.cpp +++ b/graph/lowest_common_ancestor.cpp @@ -9,7 +9,8 @@ * Algorithm: https://cp-algorithms.com/graph/lca_binary_lifting.html * * Complexity: - * - Precomputation: \f$O(N \log N)\f$ where \f$N\f$ is the number of vertices in the tree + * - Precomputation: \f$O(N \log N)\f$ where \f$N\f$ is the number of vertices + * in the tree * - Query: \f$O(\log N)\f$ * - Space: \f$O(N \log N)\f$ * @@ -34,11 +35,11 @@ * lowest_common_ancestor(x, y) = lowest_common_ancestor(y, x) */ +#include #include +#include #include #include -#include -#include /** * \namespace graph @@ -50,7 +51,7 @@ namespace graph { * Its vertices are indexed 0, 1, ..., N - 1. */ class Graph { - public: + public: /** * \brief Populate the adjacency list for each vertex in the graph. * Assumes that evey edge is a pair of valid vertex indices. @@ -58,7 +59,7 @@ class Graph { * @param N number of vertices in the graph * @param undirected_edges list of graph's undirected edges */ - Graph(size_t N, const std::vector< std::pair > &undirected_edges) { + Graph(size_t N, const std::vector > &undirected_edges) { neighbors.resize(N); for (auto &edge : undirected_edges) { neighbors[edge.first].push_back(edge.second); @@ -70,19 +71,18 @@ class Graph { * Function to get the number of vertices in the graph * @return the number of vertices in the graph. */ - int number_of_vertices() const { - return neighbors.size(); - } + int number_of_vertices() const { return neighbors.size(); } /** \brief for each vertex it stores a list indicies of its neighbors */ - std::vector< std::vector > neighbors; + std::vector > neighbors; }; /** - * Representation of a rooted tree. For every vertex its parent is precalculated. + * Representation of a rooted tree. For every vertex its parent is + * precalculated. */ class RootedTree : public Graph { - public: + public: /** * \brief Constructs the tree by calculating parent for every vertex. * Assumes a valid description of a tree is provided. @@ -90,7 +90,8 @@ class RootedTree : public Graph { * @param undirected_edges list of graph's undirected edges * @param root_ index of the root vertex */ - RootedTree(const std::vector< std::pair > &undirected_edges, int root_) + RootedTree(const std::vector > &undirected_edges, + int root_) : Graph(undirected_edges.size() + 1, undirected_edges), root(root_) { populate_parents(); } @@ -106,7 +107,7 @@ class RootedTree : public Graph { /** \brief Index of the root vertex. */ int root; - protected: + protected: /** * \brief Calculate the parents for all the vertices in the tree. * Implements the breadth first search algorithm starting from the root @@ -135,7 +136,6 @@ class RootedTree : public Graph { } } } - }; /** @@ -143,13 +143,13 @@ class RootedTree : public Graph { * queries of the lowest common ancestor of two given vertices in the tree. */ class LowestCommonAncestor { - public: + public: /** * \brief Stores the tree and precomputs "up lifts". * @param tree_ rooted tree. */ - explicit LowestCommonAncestor(const RootedTree& tree_) : tree(tree_) { - populate_up(); + explicit LowestCommonAncestor(const RootedTree &tree_) : tree(tree_) { + populate_up(); } /** @@ -196,16 +196,16 @@ class LowestCommonAncestor { } /* \brief reference to the rooted tree this structure allows to query */ - const RootedTree& tree; + const RootedTree &tree; /** * \brief for every vertex stores a list of its ancestors by powers of two * For each vertex, the first element of the corresponding list contains * the index of its parent. The i-th element of the list is an index of * the (2^i)-th ancestor of the vertex. */ - std::vector< std::vector > up; + std::vector > up; - protected: + protected: /** * Populate the "up" structure. See above. */ @@ -241,9 +241,8 @@ static void tests() { * | * 9 */ - std::vector< std::pair > edges = { - {7, 1}, {1, 5}, {1, 3}, {3, 6}, {6, 2}, {2, 9}, {6, 8}, {4, 3}, {0, 4} - }; + std::vector > edges = { + {7, 1}, {1, 5}, {1, 3}, {3, 6}, {6, 2}, {2, 9}, {6, 8}, {4, 3}, {0, 4}}; graph::RootedTree t(edges, 3); graph::LowestCommonAncestor lca(t); assert(lca.lowest_common_ancestor(7, 4) == 3); diff --git a/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp b/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp index 87b2fa101..20571cdc5 100644 --- a/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp +++ b/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp @@ -16,7 +16,7 @@ // std::max capacity of node in graph const int MAXN = 505; class Graph { - std::vector< std::vector > residual_capacity, capacity; + std::vector > residual_capacity, capacity; int total_nodes = 0; int total_edges = 0, source = 0, sink = 0; std::vector parent; @@ -50,8 +50,10 @@ class Graph { void set_graph() { std::cin >> total_nodes >> total_edges >> source >> sink; parent = std::vector(total_nodes, -1); - capacity = residual_capacity = std::vector< std::vector >(total_nodes, std::vector(total_nodes)); - for (int start = 0, destination = 0, capacity_ = 0, i = 0; i < total_edges; ++i) { + capacity = residual_capacity = std::vector >( + total_nodes, std::vector(total_nodes)); + for (int start = 0, destination = 0, capacity_ = 0, i = 0; + i < total_edges; ++i) { std::cin >> start >> destination >> capacity_; residual_capacity[start][destination] = capacity_; capacity[start][destination] = capacity_; diff --git a/graph/prim.cpp b/graph/prim.cpp index 0432926ef..f1def6161 100644 --- a/graph/prim.cpp +++ b/graph/prim.cpp @@ -5,7 +5,7 @@ using PII = std::pair; -int prim(int x, const std::vector< std::vector > &graph) { +int prim(int x, const std::vector > &graph) { // priority queue to maintain edges with respect to weights std::priority_queue, std::greater > Q; std::vector marked(graph.size(), false); @@ -40,7 +40,7 @@ int main() { return 0; } - std::vector< std::vector > graph(nodes); + std::vector > graph(nodes); // Edges with their nodes & weight for (int i = 0; i < edges; ++i) { diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 3827b9bcf..5de8ed69e 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -2,7 +2,8 @@ #include #include -int number_of_vertices, number_of_edges; // For number of Vertices (V) and number of edges (E) +int number_of_vertices, + number_of_edges; // For number of Vertices (V) and number of edges (E) std::vector> graph; std::vector visited; std::vector topological_order; @@ -28,7 +29,8 @@ void topological_sort() { reverse(topological_order.begin(), topological_order.end()); } int main() { - std::cout << "Enter the number of vertices and the number of directed edges\n"; + std::cout + << "Enter the number of vertices and the number of directed edges\n"; std::cin >> number_of_vertices >> number_of_edges; int x = 0, y = 0; graph.resize(number_of_vertices, std::vector()); @@ -41,7 +43,7 @@ int main() { std::cout << "Topological Order : \n"; for (int v : topological_order) { std::cout << v + 1 - << ' '; // converting zero based indexing back to one based. + << ' '; // converting zero based indexing back to one based. } std::cout << '\n'; return 0; diff --git a/graph/topological_sort_by_kahns_algo.cpp b/graph/topological_sort_by_kahns_algo.cpp index c29c0fc2d..d491fa80f 100644 --- a/graph/topological_sort_by_kahns_algo.cpp +++ b/graph/topological_sort_by_kahns_algo.cpp @@ -4,7 +4,7 @@ #include #include -std::vector topoSortKahn(int N, const std::vector< std::vector > &adj); +std::vector topoSortKahn(int N, const std::vector > &adj); int main() { int nodes = 0, edges = 0; @@ -14,7 +14,7 @@ int main() { } int u = 0, v = 0; - std::vector< std::vector > graph(nodes); + std::vector > graph(nodes); // create graph // example // 6 6 @@ -32,7 +32,8 @@ int main() { } } -std::vector topoSortKahn(int V, const std::vector< std::vector > &adj) { +std::vector topoSortKahn(int V, + const std::vector > &adj) { std::vector vis(V + 1, false); std::vector deg(V + 1, 0); for (int i = 0; i < V; i++) { From eee5f9495d33c60e13db0d931e3c846e164e8672 Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Thu, 27 Aug 2020 07:29:22 -0700 Subject: [PATCH 57/90] fix: clang-format for dijkstra (#1055) --- graph/dijkstra.cpp | 186 ++++++++++++++++++++++----------------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp index ac269de18..a76214560 100644 --- a/graph/dijkstra.cpp +++ b/graph/dijkstra.cpp @@ -26,10 +26,10 @@ #include #include #include +#include #include #include #include -#include constexpr int64_t INF = std::numeric_limits::max(); @@ -39,31 +39,31 @@ constexpr int64_t INF = std::numeric_limits::max(); */ namespace graph { - /** - * @brief Function that add edge between two nodes or vertices of graph - * - * @param u any node or vertex of graph - * @param v any node or vertex of graph - */ - void addEdge(std::vector>> *adj, int u, int v, - int w) { +/** + * @brief Function that add edge between two nodes or vertices of graph + * + * @param u any node or vertex of graph + * @param v any node or vertex of graph + */ +void addEdge(std::vector>> *adj, int u, int v, + int w) { (*adj)[u - 1].push_back(std::make_pair(v - 1, w)); // (*adj)[v - 1].push_back(std::make_pair(u - 1, w)); - } +} - /** - * @brief Function runs the dijkstra algorithm for some source vertex and - * target vertex in the graph and returns the shortest distance of target - * from the source. - * - * @param adj input graph - * @param s source vertex - * @param t target vertex - * - * @return shortest distance if target is reachable from source else -1 in - * case if target is not reachable from source. - */ - int dijkstra(std::vector>> *adj, int s, int t) { +/** + * @brief Function runs the dijkstra algorithm for some source vertex and + * target vertex in the graph and returns the shortest distance of target + * from the source. + * + * @param adj input graph + * @param s source vertex + * @param t target vertex + * + * @return shortest distance if target is reachable from source else -1 in + * case if target is not reachable from source. + */ +int dijkstra(std::vector>> *adj, int s, int t) { /// n denotes the number of vertices in graph int n = adj->size(); @@ -74,7 +74,7 @@ namespace graph { /// first element of pair contains the distance /// second element of pair contains the vertex std::priority_queue, std::vector>, - std::greater>> + std::greater>> pq; /// pushing the source vertex 's' with 0 distance in min heap @@ -84,97 +84,97 @@ namespace graph { dist[s] = 0; while (!pq.empty()) { - /// second element of pair denotes the node / vertex - int currentNode = pq.top().second; + /// second element of pair denotes the node / vertex + int currentNode = pq.top().second; - /// first element of pair denotes the distance - int currentDist = pq.top().first; + /// first element of pair denotes the distance + int currentDist = pq.top().first; - pq.pop(); + pq.pop(); - /// for all the reachable vertex from the currently exploring vertex - /// we will try to minimize the distance - for (std::pair edge : (*adj)[currentNode]) { - /// minimizing distances - if (currentDist + edge.second < dist[edge.first]) { - dist[edge.first] = currentDist + edge.second; - pq.push(std::make_pair(dist[edge.first], edge.first)); + /// for all the reachable vertex from the currently exploring vertex + /// we will try to minimize the distance + for (std::pair edge : (*adj)[currentNode]) { + /// minimizing distances + if (currentDist + edge.second < dist[edge.first]) { + dist[edge.first] = currentDist + edge.second; + pq.push(std::make_pair(dist[edge.first], edge.first)); + } } - } } if (dist[t] != INF) { - return dist[t]; + return dist[t]; } return -1; - } +} } // namespace graph /** Function to test the Algorithm */ void tests() { - std::cout << "Initiatinig Predefined Tests..." << std::endl; - std::cout << "Initiating Test 1..." << std::endl; - std::vector>> adj1( - 4, std::vector>()); - graph::addEdge(&adj1, 1, 2, 1); - graph::addEdge(&adj1, 4, 1, 2); - graph::addEdge(&adj1, 2, 3, 2); - graph::addEdge(&adj1, 1, 3, 5); + std::cout << "Initiatinig Predefined Tests..." << std::endl; + std::cout << "Initiating Test 1..." << std::endl; + std::vector>> adj1( + 4, std::vector>()); + graph::addEdge(&adj1, 1, 2, 1); + graph::addEdge(&adj1, 4, 1, 2); + graph::addEdge(&adj1, 2, 3, 2); + graph::addEdge(&adj1, 1, 3, 5); - int s = 1, t = 3; - assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3); - std::cout << "Test 1 Passed..." << std::endl; + int s = 1, t = 3; + assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3); + std::cout << "Test 1 Passed..." << std::endl; - s = 4, t = 3; - std::cout << "Initiating Test 2..." << std::endl; - assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5); - std::cout << "Test 2 Passed..." << std::endl; + s = 4, t = 3; + std::cout << "Initiating Test 2..." << std::endl; + assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5); + std::cout << "Test 2 Passed..." << std::endl; - std::vector>> adj2( - 5, std::vector>()); - graph::addEdge(&adj2, 1, 2, 4); - graph::addEdge(&adj2, 1, 3, 2); - graph::addEdge(&adj2, 2, 3, 2); - graph::addEdge(&adj2, 3, 2, 1); - graph::addEdge(&adj2, 2, 4, 2); - graph::addEdge(&adj2, 3, 5, 4); - graph::addEdge(&adj2, 5, 4, 1); - graph::addEdge(&adj2, 2, 5, 3); - graph::addEdge(&adj2, 3, 4, 4); + std::vector>> adj2( + 5, std::vector>()); + graph::addEdge(&adj2, 1, 2, 4); + graph::addEdge(&adj2, 1, 3, 2); + graph::addEdge(&adj2, 2, 3, 2); + graph::addEdge(&adj2, 3, 2, 1); + graph::addEdge(&adj2, 2, 4, 2); + graph::addEdge(&adj2, 3, 5, 4); + graph::addEdge(&adj2, 5, 4, 1); + graph::addEdge(&adj2, 2, 5, 3); + graph::addEdge(&adj2, 3, 4, 4); - s = 1, t = 5; - std::cout << "Initiating Test 3..." << std::endl; - assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6); - std::cout << "Test 3 Passed..." << std::endl; - std::cout << "All Test Passed..." << std::endl << std::endl; + s = 1, t = 5; + std::cout << "Initiating Test 3..." << std::endl; + assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6); + std::cout << "Test 3 Passed..." << std::endl; + std::cout << "All Test Passed..." << std::endl << std::endl; } /** Main function */ int main() { - // running predefined tests - tests(); + // running predefined tests + tests(); - int vertices = int(), edges = int(); - std::cout << "Enter the number of vertices : "; - std::cin >> vertices; - std::cout << "Enter the number of edges : "; - std::cin >> edges; + int vertices = int(), edges = int(); + std::cout << "Enter the number of vertices : "; + std::cin >> vertices; + std::cout << "Enter the number of edges : "; + std::cin >> edges; - std::vector>> adj( - vertices, std::vector>()); + std::vector>> adj( + vertices, std::vector>()); - int u = int(), v = int(), w = int(); - while (edges--) { - std::cin >> u >> v >> w; - graph::addEdge(&adj, u, v, w); - } + int u = int(), v = int(), w = int(); + while (edges--) { + std::cin >> u >> v >> w; + graph::addEdge(&adj, u, v, w); + } - int s = int(), t = int(); - std::cin >> s >> t; - int dist = graph::dijkstra(&adj, s - 1, t - 1); - if (dist == -1) { - std::cout << "Target not reachable from source" << std::endl; - } else { - std::cout << "Shortest Path Distance : " << dist << std::endl; - } - return 0; + int s = int(), t = int(); + std::cin >> s >> t; + int dist = graph::dijkstra(&adj, s - 1, t - 1); + if (dist == -1) { + std::cout << "Target not reachable from source" << std::endl; + } else { + std::cout << "Shortest Path Distance : " << dist << std::endl; + } + return 0; } From ea3071d52a0e11870121618e0bb27a2d7ecba112 Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Thu, 27 Aug 2020 07:31:34 -0700 Subject: [PATCH 58/90] fix: clang-format for hamiltons_cycle. (#1054) --- graph/hamiltons_cycle.cpp | 138 +++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/graph/hamiltons_cycle.cpp b/graph/hamiltons_cycle.cpp index 06243dadc..1506b78d2 100644 --- a/graph/hamiltons_cycle.cpp +++ b/graph/hamiltons_cycle.cpp @@ -28,49 +28,49 @@ * @return `false` if there is no Hamiltonian cycle in the graph */ bool hamilton_cycle(const std::vector> &routes) { - const size_t n = routes.size(); - // height of dp array which is 2^n - const size_t height = 1 << n; - std::vector> dp(height, std::vector(n, false)); + const size_t n = routes.size(); + // height of dp array which is 2^n + const size_t height = 1 << n; + std::vector> dp(height, std::vector(n, false)); - // to fill in the [2^i, i] cells with true - for (size_t i = 0; i < n; ++i) { - dp[1 << i][i] = true; - } - for (size_t i = 1; i < height; i++) { - std::vector zeros, ones; - // finding positions with 1s and 0s and separate them - for (size_t pos = 0; pos < n; ++pos) { - if ((1 << pos) & i) { - ones.push_back(pos); - } else { - zeros.push_back(pos); - } + // to fill in the [2^i, i] cells with true + for (size_t i = 0; i < n; ++i) { + dp[1 << i][i] = true; } - - for (auto &o : ones) { - if (!dp[i][o]) { - continue; - } - - for (auto &z : zeros) { - if (!routes[o][z]) { - continue; + for (size_t i = 1; i < height; i++) { + std::vector zeros, ones; + // finding positions with 1s and 0s and separate them + for (size_t pos = 0; pos < n; ++pos) { + if ((1 << pos) & i) { + ones.push_back(pos); + } else { + zeros.push_back(pos); + } } - dp[i + (1 << z)][z] = true; - } - } - } - bool is_cycle = false; - for (size_t i = 0; i < n; i++) { - is_cycle |= dp[height - 1][i]; - if (is_cycle) { // if true, all subsequent loop will be true. hence - // break - break; + for (auto &o : ones) { + if (!dp[i][o]) { + continue; + } + + for (auto &z : zeros) { + if (!routes[o][z]) { + continue; + } + dp[i + (1 << z)][z] = true; + } + } } - } - return is_cycle; + + bool is_cycle = false; + for (size_t i = 0; i < n; i++) { + is_cycle |= dp[height - 1][i]; + if (is_cycle) { // if true, all subsequent loop will be true. hence + // break + break; + } + } + return is_cycle; } /** @@ -79,16 +79,16 @@ bool hamilton_cycle(const std::vector> &routes) { * @return None */ static void test1() { - std::vector> arr{ - std::vector({true, true, false, false}), - std::vector({false, true, true, false}), - std::vector({false, false, true, true}), - std::vector({false, false, false, true})}; + std::vector> arr{ + std::vector({true, true, false, false}), + std::vector({false, true, true, false}), + std::vector({false, false, true, true}), + std::vector({false, false, false, true})}; - bool ans = hamilton_cycle(arr); - std::cout << "Test 1... "; - assert(ans); - std::cout << "passed\n"; + bool ans = hamilton_cycle(arr); + std::cout << "Test 1... "; + assert(ans); + std::cout << "passed\n"; } /** @@ -101,17 +101,17 @@ static void test1() { * @return None */ static void test2() { - std::vector> arr{ - std::vector({true, true, false, false}), - std::vector({false, true, true, true}), - std::vector({false, false, true, false}), - std::vector({false, false, false, true})}; + std::vector> arr{ + std::vector({true, true, false, false}), + std::vector({false, true, true, true}), + std::vector({false, false, true, false}), + std::vector({false, false, false, true})}; - bool ans = hamilton_cycle(arr); + bool ans = hamilton_cycle(arr); - std::cout << "Test 2... "; - assert(!ans); // not a cycle - std::cout << "passed\n"; + std::cout << "Test 2... "; + assert(!ans); // not a cycle + std::cout << "passed\n"; } /** @@ -120,17 +120,17 @@ static void test2() { * @return None */ static void test3() { - std::vector> arr{ - std::vector({true, true, true, true}), - std::vector({true, true, true, true}), - std::vector({true, true, true, true}), - std::vector({true, true, true, true})}; + std::vector> arr{ + std::vector({true, true, true, true}), + std::vector({true, true, true, true}), + std::vector({true, true, true, true}), + std::vector({true, true, true, true})}; - bool ans = hamilton_cycle(arr); + bool ans = hamilton_cycle(arr); - std::cout << "Test 3... "; - assert(ans); - std::cout << "passed\n"; + std::cout << "Test 3... "; + assert(ans); + std::cout << "passed\n"; } /** @@ -140,8 +140,8 @@ static void test3() { * @param argv commandline array of arguments (ignored) */ int main(int argc, char **argv) { - test1(); - test2(); - test3(); - return 0; + test1(); + test2(); + test3(); + return 0; } From f255e3f1fae5d4ed2e92d8409aa13b8259f6a054 Mon Sep 17 00:00:00 2001 From: Raman Preet Singh Date: Thu, 27 Aug 2020 20:03:10 +0530 Subject: [PATCH 59/90] docs: Fix spelling typo (#1058) --- strings/knuth_morris_pratt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/knuth_morris_pratt.cpp b/strings/knuth_morris_pratt.cpp index d53948d98..41be1561a 100644 --- a/strings/knuth_morris_pratt.cpp +++ b/strings/knuth_morris_pratt.cpp @@ -14,7 +14,7 @@ #include #ifdef _MSC_VER -#include // use this for MS Visucal C++ +#include // use this for MS Visual C++ #else #include #endif From b4bbe9dc4e72c1d456559a0257165b0de209aa78 Mon Sep 17 00:00:00 2001 From: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Date: Thu, 27 Aug 2020 12:30:14 -0400 Subject: [PATCH 60/90] [code fix] Fixed trie_tree for code quality and docs & clang-tidy error check (#1059) * attempt to fix trie-tree code * clang-tidy fixes * remove performance-unnecessary-value-param as error - this has auto-fix * make test() static * added original author to the description * added namespace * doc updates * fix true spelling * extended to small & big caps alphabets - 52 characters * clang-format and clang-tidy fixes for edc2247a * move const parameter to after the parameters Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .clang-tidy | 2 +- data_structures/trie_tree.cpp | 266 ++++++++++++++++++++++++---------- 2 files changed, 193 insertions(+), 75 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 113d688e7..99d867ab4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,6 @@ --- Checks: '-*,google-*,clang-analyzer-*,-clang-analyzer-security.insecureAPI.*,cppcoreguidelines-*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-*,openmp-*,performance-*,portability-*,modernize-*,-modernize-use-trailing-*' -WarningsAsErrors: '*,-google-readability-*,-google-explicit-constructor,-modernize-*,modernize-avoid-c-arrays,-performance-move-const-arg,-performance-noexcept-move-constructor,-cppcoreguidelines-init-variables,-cppcoreguidelines-pro-*,-cppcoreguidelines-owning-memory,-clang-analyzer-cplusplus.Move' +WarningsAsErrors: '*,-google-readability-*,-google-explicit-constructor,-modernize-*,modernize-avoid-c-arrays,-performance-move-const-arg,-performance-noexcept-move-constructor,-performance-unnecessary-value-param,-cppcoreguidelines-init-variables,-cppcoreguidelines-pro-*,-cppcoreguidelines-owning-memory,-clang-analyzer-cplusplus.Move' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: '{ BasedOnStyle: Google, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: true, ColumnLimit: 80, AccessModifierOffset: -3, AlignConsecutiveMacros: true }' diff --git a/data_structures/trie_tree.cpp b/data_structures/trie_tree.cpp index a613ebd8f..c95438cab 100644 --- a/data_structures/trie_tree.cpp +++ b/data_structures/trie_tree.cpp @@ -1,91 +1,209 @@ -#include -#include - +/** + * @file + * @author [@Arctic2333](https://github.com/Arctic2333) + * @author [Krishna Vedala](https://github.com/kvedala) + * @brief Implementation of [Trie](https://en.wikipedia.org/wiki/Trie) data + * structure for English alphabets in small characters. + * @note the function ::data_structure::trie::deleteString might be erroneous + * @see trie_modern.cpp + */ +#include +#include #include +#include #include +#include -// structure definition -typedef struct trie { - struct trie* arr[26]; - bool isEndofWord; -} trie; +/** \namespace data_structure + * \brief Data-structure algorithms + */ +namespace data_structure { +/** + * @brief [Trie](https://en.wikipedia.org/wiki/Trie) implementation for + * small-case English alphabets `a-z` + */ +class trie { + private: + static constexpr uint8_t NUM_CHARS = 26; ///< Number of alphabets + /** @brief Recursive tree nodes as an array of shared-pointers */ + std::array, NUM_CHARS << 1> arr; + bool isEndofWord = false; ///< identifier if a node is terminal node -// create a new node for trie -trie* createNode() { - trie* nn = new trie(); - for (int i = 0; i < 26; i++) nn->arr[i] = NULL; - nn->isEndofWord = false; - return nn; -} - -// insert string into the trie -void insert(trie* root, std::string str) { - for (int i = 0; i < str.length(); i++) { - int j = str[i] - 'a'; - if (root->arr[j]) { - root = root->arr[j]; - } else { - root->arr[j] = createNode(); - root = root->arr[j]; + /** + * @brief Convert a character to integer for indexing + * + * @param ch character to index + * @return unsigned integer index + */ + uint8_t char_to_int(const char& ch) const { + if (ch >= 'A' && ch <= 'Z') { + return ch - 'A'; + } else if (ch >= 'a' && ch <= 'z') { + return ch - 'a' + NUM_CHARS; } - } - root->isEndofWord = true; -} -// search a string exists inside the trie -bool search(trie* root, std::string str, int index) { - if (index == str.length()) { - if (!root->isEndofWord) - return false; - return true; + std::cerr << "Invalid character present. Exiting..."; + std::exit(EXIT_FAILURE); + return 0; } - int j = str[index] - 'a'; - if (!root->arr[j]) - return false; - return search(root->arr[j], str, index + 1); -} -/* -removes the string if it is not a prefix of any other -string, if it is then just sets the endofword to false, else -removes the given string -*/ -bool deleteString(trie* root, std::string str, int index) { - if (index == str.length()) { - if (!root->isEndofWord) - return false; - root->isEndofWord = false; - for (int i = 0; i < 26; i++) return false; - return true; - } - int j = str[index] - 'a'; - if (!root->arr[j]) - return false; - bool var = deleteString(root, str, index + 1); - if (var) { - root->arr[j] = NULL; - if (root->isEndofWord) { - return false; - } else { - int i; - for (i = 0; i < 26; i++) - if (root->arr[i]) - return false; + /** search a string exists inside a given root trie + * @param str string to search for + * @param index start index to search from + * @returns `tre` if found + * @returns `false` if not found + */ + bool search(const std::shared_ptr& root, const std::string& str, + int index) { + if (index == str.length()) { + if (!root->isEndofWord) { + return false; + } return true; } + int j = char_to_int(str[index]); + if (!root->arr[j]) { + return false; + } + return search(root->arr[j], str, index + 1); } - /* should not return here */ - std::cout << __func__ << ":" << __LINE__ << "Should not reach this line\n"; - return false; + public: + trie() = default; ///< Class default constructor + + /** insert string into the trie + * @param str String to insert in the tree + */ + void insert(const std::string& str) { + std::shared_ptr root(nullptr); + + for (const char& ch : str) { + int j = char_to_int(ch); + if (root) { + if (root->arr[j]) { + root = root->arr[j]; + } else { + std::shared_ptr temp(new trie()); + root->arr[j] = temp; + root = temp; + } + } else if (arr[j]) { + root = arr[j]; + } else { + std::shared_ptr temp(new trie()); + arr[j] = temp; + root = temp; + } + } + root->isEndofWord = true; + } + + /** search a string exists inside the trie + * @param str string to search for + * @param index start index to search from + * @returns `true` if found + * @returns `false` if not found + */ + bool search(const std::string& str, int index) { + if (index == str.length()) { + if (!isEndofWord) { + return false; + } + return true; + } + int j = char_to_int(str[index]); + if (!arr[j]) { + return false; + } + return search(arr[j], str, index + 1); + } + + /** + * removes the string if it is not a prefix of any other + * string, if it is then just sets the ::data_structure::trie::isEndofWord + * to false, else removes the given string + * @note the function ::data_structure::trie::deleteString might be + * erroneous + * @todo review the function ::data_structure::trie::deleteString and the + * commented lines + * @param str string to remove + * @param index index to remove from + * @returns `true` if successful + * @returns `false` if unsuccessful + */ + bool deleteString(const std::string& str, int index) { + if (index == str.length()) { + if (!isEndofWord) { + return false; + } + isEndofWord = false; + // following lines - possible source of error? + // for (int i = 0; i < NUM_CHARS; i++) + // if (!arr[i]) + // return false; + return true; + } + int j = char_to_int(str[index]); + if (!arr[j]) { + return false; + } + bool var = deleteString(str, index + 1); + if (var) { + arr[j].reset(); + if (isEndofWord) { + return false; + } else { + int i = 0; + for (i = 0; i < NUM_CHARS; i++) { + if (arr[i]) { + return false; + } + } + return true; + } + } + + /* should not return here */ + std::cout << __func__ << ":" << __LINE__ + << "Should not reach this line\n"; + return false; + } +}; +} // namespace data_structure + +/** + * @brief Testing function + * @returns void + */ +static void test() { + data_structure::trie root; + root.insert("Hello"); + root.insert("World"); + + assert(!root.search("hello", 0)); + std::cout << "hello - " << root.search("hello", 0) << "\n"; + + assert(root.search("Hello", 0)); + std::cout << "Hello - " << root.search("Hello", 0) << "\n"; + + assert(!root.search("Word", 0)); + std::cout << "Word - " << root.search("Word", 0) << "\n"; + + assert(root.search("World", 0)); + std::cout << "World - " << root.search("World", 0) << "\n"; + + // Following lines of code give erroneous output + // root.deleteString("hello", 0); + // assert(!root.search("hello", 0)); + // std::cout << "hello - " << root.search("world", 0) << "\n"; } +/** + * @brief Main function + * @return 0 on exit + */ int main() { - trie* root = createNode(); - insert(root, "hello"); - insert(root, "world"); - int a = search(root, "hello", 0); - int b = search(root, "word", 0); - printf("%d %d ", a, b); + test(); + return 0; } From f4fe0c6f5bbbe5014573a0e27b1526cd07c944f3 Mon Sep 17 00:00:00 2001 From: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Date: Thu, 27 Aug 2020 17:13:27 -0400 Subject: [PATCH 61/90] [fix] rename data_structures namespace (#1061) * rename data_structures namespace * spelling correction * fix namespace doc name --- data_structures/skip_list.cpp | 14 +++++++------- data_structures/trie_tree.cpp | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/data_structures/skip_list.cpp b/data_structures/skip_list.cpp index cc679a31c..793994161 100644 --- a/data_structures/skip_list.cpp +++ b/data_structures/skip_list.cpp @@ -20,10 +20,10 @@ #include #include -/** \namespace data_structure +/** \namespace data_structures * \brief Data-structure algorithms */ -namespace data_structure { +namespace data_structures { constexpr int MAX_LEVEL = 2; ///< Maximum level of skip list constexpr float PROBABILITY = 0.5; ///< Current probability for "coin toss" @@ -198,7 +198,7 @@ class SkipList { } }; -} // namespace data_structure +} // namespace data_structures /** * Main function: @@ -208,14 +208,14 @@ class SkipList { int main() { std::srand(std::time(nullptr)); - data_structure::SkipList lst; + data_structures::SkipList lst; - for (int j = 0; j < (1 << (data_structure::MAX_LEVEL + 1)); j++) { - int k = (std::rand() % (1 << (data_structure::MAX_LEVEL + 2)) + 1); + for (int j = 0; j < (1 << (data_structures::MAX_LEVEL + 1)); j++) { + int k = (std::rand() % (1 << (data_structures::MAX_LEVEL + 2)) + 1); lst.insertElement(k, &j); } lst.displayList(); - + return 0; } diff --git a/data_structures/trie_tree.cpp b/data_structures/trie_tree.cpp index c95438cab..e966b2dfd 100644 --- a/data_structures/trie_tree.cpp +++ b/data_structures/trie_tree.cpp @@ -14,10 +14,10 @@ #include #include -/** \namespace data_structure +/** \namespace data_structures * \brief Data-structure algorithms */ -namespace data_structure { +namespace data_structures { /** * @brief [Trie](https://en.wikipedia.org/wiki/Trie) implementation for * small-case English alphabets `a-z` @@ -50,7 +50,7 @@ class trie { /** search a string exists inside a given root trie * @param str string to search for * @param index start index to search from - * @returns `tre` if found + * @returns `true` if found * @returns `false` if not found */ bool search(const std::shared_ptr& root, const std::string& str, @@ -169,14 +169,14 @@ class trie { return false; } }; -} // namespace data_structure +} // namespace data_structures /** * @brief Testing function * @returns void */ static void test() { - data_structure::trie root; + data_structures::trie root; root.insert("Hello"); root.insert("World"); From 4ed75018e38c21d2582b8dd101d83395a0301956 Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Fri, 28 Aug 2020 09:47:02 -0700 Subject: [PATCH 62/90] fix: Clean up of code examples in CONTRIBUTING to follow guidelines. (#1063) --- CONTRIBUTING.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 07d620dea..905ffe14e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,7 @@ We are very happy that you consider implementing algorithms and data structures #include -/** +/** * @namespace */ namespace name { @@ -67,42 +67,44 @@ namespace name { /** * Class documentation */ -class cls_name{ +class class_name { private: - int var1; ///< short info of this variable - char *msg; ///< short info - + int variable; ///< short info of this variable + char *message; ///< short info + public: - // other members also documented as below + // other members also documented as below } /** - * Function documentation + * Function documentation * @tparam T this is a one-line info about T * @param param1 on-line info about param1 * @param param2 on-line info about param2 * @returns `true` if ... - * @returns `false` if ... + * @returns `false` if ... */ template bool func(int param1, T param2) { - // function statements here - if(/*something bad*/) + // function statements here + if (/*something bad*/) { return false; + } return true; } /** Test function */ -void test() { - /* some statements */ - assert(func(...) == ...); // this ensures that the algorithm works as expected +static void test() { + /* desciptions of the following test */ + assert(func(...) == ...); // this ensures that the algorithm works as expected - // can have multiple checks + // can have multiple checks } /** Main function */ int main(int argc, char *argv[]) { + test(); // execute the tests // code here return 0; } @@ -110,7 +112,7 @@ int main(int argc, char *argv[]) { #### New File Name guidelines - Use lowercase words with ``"_"`` as separator -- For instance +- For instance ``` MyNewCppClass.CPP is incorrect my_new_cpp_class.cpp is correct format @@ -119,7 +121,7 @@ my_new_cpp_class.cpp is correct format - File name validation will run on docker to ensure the validity. - If an implementation of the algorithm already exists and your version is different from that implemented, please use incremental numeric digit as a suffix. For example, if `median_search.cpp` already exists in the `search` folder and you are contributing a new implementation, the filename should be `median_search2.cpp` and for a third implementation, `median_search3.cpp`. -#### New Directory guidelines +#### New Directory guidelines - We recommend adding files to existing directories as much as possible. - Use lowercase words with ``"_"`` as separator ( no spaces or ```"-"``` allowed ) - For instance From 072fc952282d6c7f70f6f19204b567fe19c9919b Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Sun, 30 Aug 2020 10:38:49 -0700 Subject: [PATCH 63/90] docs: Update clang-format instructions in CONTRIBUTING. (#1053) * docs: Update clang-format instructions in CONTRIBUTING. * Accomodate more changes in the PR section of CONTRIBUTING. * code formatting * code sample as a shell script * format build command * Add instructions for clang-format installation. * gitpod * remove a sentence. --- CONTRIBUTING.md | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 905ffe14e..dd70b7b3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -154,19 +154,33 @@ Common prefixes: ### Pull Requests - Checkout our [pull request template](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/.github/pull_request_template.md) -#### cpplint -To see if [__cpplint__](https://github.com/cpplint/cpplint) is already installed, do: -* `cpplint --version` # currently returns "cpplint 1.4.4" -If cpplint is ___not___ installed then do: -* `python3 -m pip install cpplint` # If that does not work then try... -* `py -m pip install cpplint` # If that does not work then try... -* `pip install cpplint` -Once cpplint is installed, test your file(s) with: -* `cpplint --filter=-legal my_file.cpp my_other_file.cpp` # Fix any issues and try again. +#### Building Locally +Before submitting a pull request, build the code locally or using the convenient [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/TheAlgorithms/C-Plus-Plus) service. +``` +cmake -B build -S . +``` -The [__clang-format__](https://clang.llvm.org/docs/ClangFormat.html) tool can fix whitespace related _cpplint_ issues. -* On Macs only: `brew install clang-format` # Only needs to be installed once. -* All platforms: `clang-format -i -style="{IndentWidth: 4}" my_file.cpp` +#### Static Code Analyzer +We use [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) as a static code analyzer with a configuration in [.clang-tidy](.clang-tidy). +``` +clang-tidy --fix --quiet -p build subfolder/file_to_check.cpp -- +``` + +#### Code Formatter +[__clang-format__](https://clang.llvm.org/docs/ClangFormat.html) is used for code forrmating. +* Installation (Only needs to be installed once.) + * Mac (using home-brew): `brew install clang-format` + * Mac (using macports): `sudo port install clang-10 +analyzer` + * Windows (MSYS2 64-bit): `pacman -S mingw-w64-x86_64-clang-tools-extra` + * Linux (Debian): `sudo apt-get install clang-format-10 clang-tidy-10` +* Running (all platforms): `clang-format -i -style="file" my_file.cpp` + +#### GitHub Actions +Enable GitHub Actions on your fork of the repository. +After enabling it will execute `clang-tidy` and `clang-format` after every a push (not a commit). +The result can create another commit if the actions made any changes on your behalf. +Hence, it is better to wait and check the results of GitHub Actions after every push. +Run `git pull` in your local clone if these actions made many changes in order to avoid merge conflicts. Most importantly, - Happy coding! From 0fcdbf2bdccdd4dc29ef65e623bda93d27100b02 Mon Sep 17 00:00:00 2001 From: Nimish Shah Date: Tue, 1 Sep 2020 00:25:49 +0530 Subject: [PATCH 64/90] fix: Remove repeated algorithm (#1067) * Improve code and add support for 46+ fibb numbers * Docs: Fibbonacci docs added * fix: Add c++ suggested code * fix: remove repeated algorithm --- dynamic_programming/fibonacci_top_down.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 dynamic_programming/fibonacci_top_down.cpp diff --git a/dynamic_programming/fibonacci_top_down.cpp b/dynamic_programming/fibonacci_top_down.cpp deleted file mode 100644 index 3c0c9a1a3..000000000 --- a/dynamic_programming/fibonacci_top_down.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -using namespace std; -int arr[1000000]; -int fib(int n) { - if (arr[n] == -1) { - if (n <= 1) - arr[n] = n; - else - arr[n] = fib(n - 1) + fib(n - 2); - } - return arr[n]; -} -int main(int argc, char const *argv[]) { - int n; - cout << "Enter n: "; - cin >> n; - for (int i = 0; i < n + 1; ++i) { - arr[i] = -1; - } - cout << "Fibonacci number is " << fib(n) << endl; - return 0; -} \ No newline at end of file From a9da541127da107f4b43546e99620417f7ee3f33 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 31 Aug 2020 18:56:34 +0000 Subject: [PATCH 65/90] updating DIRECTORY.md --- DIRECTORY.md | 1 - 1 file changed, 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 178e88499..567460083 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -58,7 +58,6 @@ * [Edit Distance](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/edit_distance.cpp) * [Egg Dropping Puzzle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/egg_dropping_puzzle.cpp) * [Fibonacci Bottom Up](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/fibonacci_bottom_up.cpp) - * [Fibonacci Top Down](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/fibonacci_top_down.cpp) * [Floyd Warshall](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/floyd_warshall.cpp) * [Kadane](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/kadane.cpp) * [Longest Common String](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_common_string.cpp) From 08c4a3f664fed718c16677b53740f741b8257b61 Mon Sep 17 00:00:00 2001 From: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Date: Thu, 3 Sep 2020 08:51:30 -0400 Subject: [PATCH 66/90] feat: Added merge-insertion sort from #246 (#1062) * Create Merge-insertion sort.cpp * fixed file path * working code * added documentation * replace c-style array with std::array * updating DIRECTORY.md * clang-format and clang-tidy fixes for 8b3a6634 Co-authored-by: sinkyoungdeok Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + data_structures/skip_list.cpp | 16 +-- sorting/merge_insertion_sort.cpp | 163 +++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 sorting/merge_insertion_sort.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 567460083..4370e78d8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -236,6 +236,7 @@ * [Heap Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/heap_sort.cpp) * [Insertion Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/insertion_sort.cpp) * [Library Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/library_sort.cpp) + * [Merge Insertion Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/merge_insertion_sort.cpp) * [Merge Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/merge_sort.cpp) * [Non Recursive Merge Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/non_recursive_merge_sort.cpp) * [Numeric String Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/numeric_string_sort.cpp) diff --git a/data_structures/skip_list.cpp b/data_structures/skip_list.cpp index 793994161..9cf8c430f 100644 --- a/data_structures/skip_list.cpp +++ b/data_structures/skip_list.cpp @@ -64,7 +64,7 @@ class SkipList { SkipList() { level = 0; // Header initialization - header = std::shared_ptr(new Node(-1, MAX_LEVEL)); + header = std::make_shared(-1, MAX_LEVEL); } /** @@ -75,8 +75,9 @@ class SkipList { int randomLevel() { int lvl = 0; while (static_cast(std::rand()) / RAND_MAX < PROBABILITY && - lvl < MAX_LEVEL) + lvl < MAX_LEVEL) { lvl++; + } return lvl; } @@ -93,8 +94,9 @@ class SkipList { update.fill(nullptr); for (int i = level; i >= 0; i--) { - while (x->forward[i] != nullptr && x->forward[i]->key < key) + while (x->forward[i] != nullptr && x->forward[i]->key < key) { x = x->forward[i]; + } update[i] = x; } @@ -112,7 +114,7 @@ class SkipList { } std::shared_ptr n = - std::shared_ptr(new Node(key, rlevel, value)); + std::make_shared(key, rlevel, value); for (int i = 0; i <= rlevel; i++) { n->forward[i] = update[i]->forward[i]; update[i]->forward[i] = n; @@ -135,8 +137,9 @@ class SkipList { update.fill(nullptr); for (int i = level; i >= 0; i--) { - while (x->forward[i] != nullptr && x->forward[i]->key < key) + while (x->forward[i] != nullptr && x->forward[i]->key < key) { x = x->forward[i]; + } update[i] = x; } @@ -146,8 +149,9 @@ class SkipList { if (!doesnt_exist) { for (int i = 0; i <= level; i++) { - if (update[i]->forward[i] != x) + if (update[i]->forward[i] != x) { break; + } update[i]->forward[i] = x->forward[i]; } /* Remove empty levels*/ diff --git a/sorting/merge_insertion_sort.cpp b/sorting/merge_insertion_sort.cpp new file mode 100644 index 000000000..07b2ed19d --- /dev/null +++ b/sorting/merge_insertion_sort.cpp @@ -0,0 +1,163 @@ +/** + * @file + * @author [@sinkyoungdeok](https://github.com/sinkyoungdeok) + * @author [Krishna Vedala](https://github.com/kvedala) + * @brief Algorithm that combines insertion sort and merge sort. [Wiki + * link](https://en.wikipedia.org/wiki/Merge-insertion_sort) + * + * @see Individual algorithms: insertion_sort.cpp and merge_sort.cpp + */ +#include +#include +#include +#include +#include +#include + +/** \namespace sorting + * \brief Sorting algorithms + */ +namespace sorting { +/** \namespace merge_insertion + * \brief Combined Intersion-Merge sorting algorithm + */ +namespace merge_insertion { + +/** + * @brief Insertion merge algorithm + * @see insertion_sort.cpp + * + * @tparam T array data type + * @tparam N length of array + * @param A pointer to array to sort + * @param start start index of sorting window + * @param end end index of sorting window + */ +template +static void InsertionSort(std::array *A, size_t start, size_t end) { + size_t i = 0, j = 0; + T *ptr = A->data(); + + for (i = start; i < end; i++) { + T temp = ptr[i]; + j = i; + while (j > start && temp < ptr[j - 1]) { + ptr[j] = ptr[j - 1]; + j--; + } + // for (j = i; j > start && temp < ptr[j - 1]; --j) { + // ptr[j] = ptr[j - 1]; + // } + + ptr[j] = temp; + } +} + +/** + * @brief Perform merge of data in a window + * + * @tparam T array data type + * @tparam N length of array + * @param A pointer to array to sort + * @param min start index of window + * @param max end index of window + * @param mid mid-point of window + */ +template +static void merge(std::array *array, size_t min, size_t max, size_t mid) { + size_t firstIndex = min; + size_t secondIndex = mid + 1; + + auto ptr = array->data(); + std::array tempArray{0}; + + // While there are elements in the left or right runs + for (size_t index = min; index <= max; index++) { + // If left run head exists and is <= existing right run head. + if (firstIndex <= mid && + (secondIndex > max || ptr[firstIndex] <= ptr[secondIndex])) { + tempArray[index] = ptr[firstIndex]; + firstIndex++; + } else { + tempArray[index] = ptr[secondIndex]; + secondIndex++; + } + } + + // transfer to the initial array + memcpy(ptr + min, tempArray.data() + min, (max - min) * sizeof(T)); + // for (int index = min; index <= max; index++) ptr[index] = + // tempArray[index]; +} + +/** + * @brief Final combined algorithm. + * Algorithm utilizes ::sorting::merge_insertion::InsertionSort if window length + * is less than threshold, else performs merge sort recursively using + * ::sorting::merge_insertion::mergeSort + * + * @tparam T array data type + * @tparam N length of array + * @param A pointer to array to sort + * @param min start index of sort window + * @param max end index of sort window + * @param threshold window length threshold + */ +template +void mergeSort(std::array *array, size_t min, size_t max, + size_t threshold) { + // prerequisite + if ((max - min) <= threshold) { + InsertionSort(array, min, max); + } else { + // get the middle point + size_t mid = (max + min) >> 1; + + // apply merge sort to both parts of this + mergeSort(array, min, mid, threshold); + mergeSort(array, mid, max, threshold); + + // and finally merge all that sorted stuff + merge(array, min, max, mid); + } +} + +} // namespace merge_insertion +} // namespace sorting + +/** + * @brief Function to test code using random arrays + * @returns none + */ +static void test() { + constexpr size_t size = 30; + std::array array{0}; + // input + for (int i = 0; i < size; i++) { + array[i] = std::rand() % 100 - 50; + std::cout << array[i] << " "; + } + std::cout << std::endl; + + sorting::merge_insertion::InsertionSort(&array, 0, size); + // sorting::merge_insertion::mergeSort(&array, 0, size, 10); + + // output + for (int i = 0; i < size; ++i) { + std::cout << array[i] << " "; + } + std::cout << std::endl; + + assert(std::is_sorted(std::begin(array), std::end(array))); + std::cout << "Test passed\n"; +} + +/** + * @brief Main function + * @return 0 on exit + */ +int main() { + std::srand(std::time(nullptr)); + test(); + return 0; +} From 6e77f98494898915f8ca2c91427ccb1e7cf5ad80 Mon Sep 17 00:00:00 2001 From: Bolaji Boluwatife Date: Fri, 4 Sep 2020 01:01:50 +0100 Subject: [PATCH 67/90] Revised Linked List (#999) * Addition of Test to LinkedList I noticed an infinite loop when the program asks the user to "Enter the element to be inserted:", and the user enters a wrong input such as "rr". * Revised Tests * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update data_structures/linked_list.cpp Co-authored-by: David Leal * Update data_structures/linked_list.cpp Co-authored-by: David Leal * Update data_structures/linked_list.cpp Co-authored-by: David Leal * Update data_structures/linked_list.cpp Co-authored-by: David Leal * Update data_structures/linked_list.cpp Co-authored-by: David Leal * Update data_structures/linked_list.cpp Co-authored-by: David Leal * Update data_structures/linked_list.cpp Co-authored-by: David Leal * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update data_structures/linked_list.cpp Co-authored-by: David Leal * added documentations to functions I made a few changes although I'm not sure I covered all. * Update linked_list.cpp * function documentation Co-authored-by: David Leal * function documentation Co-authored-by: David Leal * function documentation * Update linked_list.cpp * removed global variable I decided to go with the parameter approach. Is line 79(Iter& ....) and others like it considered healthy code? * removed global variable * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * fixed clang errors * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * program rewrite * Update linked_list.cpp * Update linked_list.cpp * Removed extra space * Update linked_list.cpp * Delete vdoubly_linked_list.ico * added documentation * added documentation * added documentation * use of shared_ptr * use of shared_ptr * modified linked list * Update linked_list.cpp * added string header * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * fixed documentation * fixed link class * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * Update linked_list.cpp * fixed link class * fixed runtime error Co-authored-by: David Leal --- data_structures/linked_list.cpp | 355 ++++++++++++++++++++++---------- 1 file changed, 248 insertions(+), 107 deletions(-) diff --git a/data_structures/linked_list.cpp b/data_structures/linked_list.cpp index 8eb6e586d..72003ba0c 100644 --- a/data_structures/linked_list.cpp +++ b/data_structures/linked_list.cpp @@ -1,134 +1,275 @@ +/** + * @file + * @brief Implementation of singly linked list algorithm. + * @details + * The linked list is a data structure used for holding a sequence of + * values, which can be added, removed and displayed. + * ### Algorithm + * Values can be added by iterating to the end of a list(by following + * the pointers) starting from the first link. Whichever link points to null + * is considered the last link and is pointed to the new value. + * + * Values can be removed by also iterating through the list. When the node + * containing the value is found, the node pointing to the current node is made + * to point to the node that the current node is pointing to, and then returning + * the current node to heap store. + */ #include +#include +#include -struct node { - int val; - node *next; +/** + * @namespace data_structures + * @brief Data Structures algorithms + */ +namespace data_structures { + +/** + * @namespace linked_list + * @brief Functions for singly linked list algorithm + */ +namespace linked_list { + +/** + * This function checks if the string passed consists + * of only digits. + * @param s To be checked if s contains only integers + * @returns true if there are only only digits present in the string + * @returns false if any other character is found + */ +bool isDigit(const std::string& s) { + // function statements here + for (char i : s) { + if (!isdigit(i)) { + return false; + } + } + return true; +} + +/** + * A link class containing a value and pointer to another link + */ +class link { + private: + int pvalue; ///< value of the current link + std::shared_ptr psucc; ///< pointer to the next value on the list + + public: + /** + * function returns the integer value stored in the link. + * @returns the integer value stored in the link. + */ + int val() { return pvalue; } + + /** + * function returns the pointer to next link + * @returns the pointer to the next link + * */ + std::shared_ptr& succ() { return psucc; } + + /** + * Creates link with provided value and pointer to next link + * @param value is the integer stored in the link + */ + explicit link(int value = 0) : pvalue(value), psucc(nullptr) {} }; -node *start; +/** + * A list class containing a sequence of links + */ +class list { + private: + std::shared_ptr first; ///< link before the actual first element + std::shared_ptr last; ///< last link on the list + public: + /** + * List constructor. Initializes the first and last link. + */ + list() { + // Initialize the first link + first = std::make_shared(); + // Initialize the last link with the first link + last = nullptr; + } -void insert(int x) { - node *t = start; - node *n = new node; - n->val = x; - n->next = NULL; - if (start != NULL) { - while (t->next != NULL) { - t = t->next; - } - t->next = n; + bool isEmpty(); + + void push_back(int new_elem); + void push_front(int new_elem); + void erase(int old_elem); + void display(); + std::shared_ptr search(int find_elem); + void reverse(); +}; + +/** + * function checks if list is empty + * @returns true if list is empty + * @returns false if list is not empty + */ +bool list::isEmpty() { + if (last == nullptr) { + return true; } else { - start = n; + return false; } } -void remove(int x) { - if (start == NULL) { - std::cout << "\nLinked List is empty\n"; - return; - } else if (start->val == x) { - node *temp = start; - start = start->next; - delete temp; - return; - } - - node *temp = start, *parent = start; - - while (temp != NULL && temp->val != x) { - parent = temp; - temp = temp->next; - } - - if (temp == NULL) { - std::cout << std::endl << x << " not found in list\n"; - return; - } - - parent->next = temp->next; - delete temp; -} - -void search(int x) { - node *t = start; - int found = 0; - while (t != NULL) { - if (t->val == x) { - std::cout << "\nFound"; - found = 1; - break; - } - t = t->next; - } - if (found == 0) { - std::cout << "\nNot Found"; - } -} - -void show() { - node *t = start; - while (t != NULL) { - std::cout << t->val << "\t"; - t = t->next; - } -} - -void reverse() { - node *first = start; - if (first != NULL) { - node *second = first->next; - while (second != NULL) { - node *tem = second->next; - second->next = first; - first = second; - second = tem; - } - start->next = NULL; - start = first; +/** + * function adds new element to the end of the list + * @param new_elem to be added to the end of the list + */ +void list::push_back(int new_elem) { + if (isEmpty()) { + first->succ() = std::make_shared(new_elem); + last = first->succ(); } else { - std::cout << "\nEmpty list"; + last->succ() = std::make_shared(new_elem); + last = last->succ(); } } +/** + * function adds new element to the beginning of the list + * @param new_elem to be added to front of the list + */ +void list::push_front(int new_elem) { + if (isEmpty()) { + first->succ() = std::make_shared(new_elem); + last = first->succ(); + } else { + std::shared_ptr t = std::make_shared(new_elem); + t->succ() = first->succ(); + first->succ() = t; + } +} + +/** + * function erases old element from the list + * @param old_elem to be erased from the list + */ +void list::erase(int old_elem) { + if (isEmpty()) { + std::cout << "List is Empty!"; + return; + } + std::shared_ptr t = first; + std::shared_ptr to_be_removed = nullptr; + while (t != last && t->succ()->val() != old_elem) { + t = t->succ(); + } + if (t == last) { + std::cout << "Element not found\n"; + return; + } + to_be_removed = t->succ(); + t->succ() = t->succ()->succ(); + to_be_removed.reset(); + if (t->succ() == nullptr) { + last = nullptr; + } +} + +/** + * function displays all the elements in the list + * @returns 'void' + */ +void list::display() { + if (isEmpty()) { + std::cout << "List is Empty!"; + return; + } + std::shared_ptr t = first; + while (t->succ() != nullptr) { + std::cout << t->succ()->val() << "\t"; + t = t->succ(); + } +} + +/** + * function searchs for @param find_elem in the list + * @param find_elem to be searched for in the list + */ +std::shared_ptr list::search(int find_elem) { + if (isEmpty()) { + std::cout << "List is Empty!"; + return nullptr; + } + std::shared_ptr t = first; + while (t != last && t->succ()->val() != find_elem) { + t = t->succ(); + } + if (t == last) { + std::cout << "Element not found\n"; + return nullptr; + } + std::cout << "Element was found\n"; + return t->succ(); +} +} // namespace linked_list +} // namespace data_structures + +/** + * Main function: + * Allows the user add and delete values from the list. + * Also allows user to search for and display values in the list. + * @returns 0 on exit + */ int main() { - int choice, x; + data_structures::linked_list::list l; + int choice = 0; + int x = 0; + std::string s; do { std::cout << "\n1. Insert"; std::cout << "\n2. Delete"; std::cout << "\n3. Search"; std::cout << "\n4. Print"; - std::cout << "\n5. Reverse"; std::cout << "\n0. Exit"; std::cout << "\n\nEnter you choice : "; std::cin >> choice; switch (choice) { - case 1: - std::cout << "\nEnter the element to be inserted : "; - std::cin >> x; - insert(x); - break; - case 2: - std::cout << "\nEnter the element to be removed : "; - std::cin >> x; - remove(x); - break; - case 3: - std::cout << "\nEnter the element to be searched : "; - std::cin >> x; - search(x); - break; - case 4: - show(); - std::cout << "\n"; - break; - case 5: - std::cout << "The reversed list: \n"; - reverse(); - show(); - std::cout << "\n"; - break; + case 1: + std::cout << "\nEnter the element to be inserted : "; + std::cin >> s; + + if (data_structures::linked_list::isDigit(s)) { + x = std::stoi(s); + l.push_back(x); + } else { + std::cout << "Wrong Input!\n"; + } + break; + case 2: + std::cout << "\nEnter the element to be removed : "; + std::cin >> s; + if (data_structures::linked_list::isDigit(s)) { + x = std::stoi(s); + l.erase(x); + } else { + std::cout << "Wrong Input!\n"; + } + break; + case 3: + std::cout << "\nEnter the element to be searched : "; + std::cin >> s; + if (data_structures::linked_list::isDigit(s)) { + x = std::stoi(s); + std::shared_ptr found = + l.search(x); + } else { + std::cout << "Wrong Input!\n"; + } + break; + case 4: + l.display(); + std::cout << "\n"; + break; + default: + std::cout << "Invalid Input\n" << std::endl; + break; } } while (choice != 0); - return 0; } From 60050d7b1b0407b54874846fb49d8c2eb9e2b7d6 Mon Sep 17 00:00:00 2001 From: ckontakis <36933662+ckontakis@users.noreply.github.com> Date: Fri, 4 Sep 2020 03:12:56 +0300 Subject: [PATCH 68/90] Added Horspool algorithm (#1071) * feat: add Horspool algorithm * Fixed conversion from unsigned long to int with static casting * Removed changes from CMakeLists.txt file * Update strings/horspool.cpp Co-authored-by: David Leal * Update strings/horspool.cpp Co-authored-by: David Leal * Documentation, test function and namespaces added * Documentation, test function and namespaces added * Update strings/horspool.cpp Co-authored-by: David Leal * Update strings/horspool.cpp Co-authored-by: David Leal * Update strings/horspool.cpp Co-authored-by: David Leal * Update strings/horspool.cpp Co-authored-by: David Leal * Update strings/horspool.cpp Co-authored-by: David Leal * Update strings/horspool.cpp Co-authored-by: David Leal * Removed blank rows * Commit to see if the problem with automated tests is solved Co-authored-by: David Leal --- strings/horspool.cpp | 122 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 strings/horspool.cpp diff --git a/strings/horspool.cpp b/strings/horspool.cpp new file mode 100644 index 000000000..a43be5f0d --- /dev/null +++ b/strings/horspool.cpp @@ -0,0 +1,122 @@ +/** + * @file + * @brief Horspool's algorithm that finds if a string contains a substring (https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm) + * @author [Harry Kontakis](https://github.com/ckontakis) + */ + +#include +#include +#include + +/** + * @namespace strings + * @brief Algorithms with strings + */ +namespace strings { +/** + * @namespace horspool + * @brief Functions for [Horspool's](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm) algorithm + */ +namespace horspool { +/** + * A function that finds the shift table of the given prototype string that we need in Horpool's algorithm. + * @param prototype is the substring that we use to find shift table + * @return Shift Table of Horspool's algorithm + */ +std::unordered_map findShiftTable(const std::string &prototype) { + std::unordered_map + shiftTable; // A HashMap for shift table that has characters for keys and integers for values + + for (int i = 0; i < prototype.size(); + i++) { // Checking all characters of prototype string + if (shiftTable.find(prototype[i]) == + shiftTable.end()) { // If character does not exist in HashMap + if (i != prototype.size() - 1) { + shiftTable.insert(std::make_pair( + prototype[i], prototype.size() - i - + 1)); // Insert the character as key and the size of prototype string - index of character - 1 as value + } else { + shiftTable.insert(std::make_pair( + prototype[i], + prototype.size())); // Insert the character as key and the size of prototype string as value + } + } else { + if (i != prototype.size() - 1) { + shiftTable[prototype[i]] = prototype.size() - i - 1; + } + } + } + return shiftTable; +} + +/** + * A function that implements Horspool's algorithm. + * @param text is the string that we are searching if there is a substring + * @param prototype is the substring that we are searching in text + * @returns true if text string contains prototype string + * @returns false if text string does not contain prototype string + */ +bool horspool(const std::string &text, const std::string &prototype) { + std::unordered_map shiftTable = findShiftTable( + prototype); // Initialise shift table calling findShiftTable function + + int i = static_cast( + prototype.size() - + 1); // Index that we shift in text to find the substring + while (i < text.size()) { + int j = i, k = 0; + bool flag = true; + + for (int z = static_cast(prototype.size() - 1); z >= 0 && flag; + z--) { // Checking if all characters of substring are equal with all characters of string + if (text[j] == prototype[z]) { + k++; + j--; + } else { + flag = false; // If two characters are not equal set flag to false and break from loop + } + } + + if (k == + prototype.size()) { // If all characters match then return true + return true; + } else { + if (shiftTable.find(text[i]) != shiftTable.end()) { + i += shiftTable[text[i]]; // If shift table contains the character then shift index as many steps as value + } else { + i += prototype.size(); // If character does not exist in shift table then shift index as many steps as size of prototype string + } + } + } + return false; +} +} // namespace horspool +} // namespace strings + +/** + * @brief Function with test cases for Horspool's algorithm + * @returns void + */ +static void test(){ + assert(strings::horspool::horspool("Hello World","World") == true); + assert(strings::horspool::horspool("Hello World"," World") == true); + assert(strings::horspool::horspool("Hello World","ello") == true); + assert(strings::horspool::horspool("Hello World","rld") == true); + assert(strings::horspool::horspool("Hello","Helo") == false); + assert(strings::horspool::horspool("c++_algorithms","c++_algorithms") == true); + assert(strings::horspool::horspool("c++_algorithms","c++_") == true); + assert(strings::horspool::horspool("Hello","Hello World") == false); + assert(strings::horspool::horspool("c++_algorithms","") == false); + assert(strings::horspool::horspool("c++","c") == true); + assert(strings::horspool::horspool("3458934793","4793") == true); + assert(strings::horspool::horspool("3458934793","123") == false); +} + +/** + * @brief Main Function that calls test function + * @returns 0 on exit + */ +int main(){ + test(); + return 0; +} From 508ea020d35e5740578b55f1f2952b6470971438 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Fri, 4 Sep 2020 00:14:24 +0000 Subject: [PATCH 69/90] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 4370e78d8..3e9afd3b3 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -254,5 +254,6 @@ ## Strings * [Brute Force String Searching](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/strings/brute_force_string_searching.cpp) + * [Horspool](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/strings/horspool.cpp) * [Knuth Morris Pratt](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/strings/knuth_morris_pratt.cpp) * [Rabin Karp](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/strings/rabin_karp.cpp) From 88fe6911df36533919477e930f03c0a0d3d9a8a7 Mon Sep 17 00:00:00 2001 From: Rakshit Raj Date: Wed, 9 Sep 2020 22:21:07 +0530 Subject: [PATCH 70/90] Update merge_sort.cpp (#1077) On line 133 --- corrected the size argument so that the last element is not missed --- sorting/merge_sort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sorting/merge_sort.cpp b/sorting/merge_sort.cpp index fc6d66af5..8beb4e44a 100644 --- a/sorting/merge_sort.cpp +++ b/sorting/merge_sort.cpp @@ -110,7 +110,7 @@ int main() { } mergeSort(arr, 0, size - 1); std::cout << "Sorted array : "; - show(arr, size - 1); + show(arr, size); delete[] arr; return 0; } From d4b54b8294722607f8ed1e29f2b3ff500ba787e7 Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Fri, 18 Sep 2020 10:34:18 -0700 Subject: [PATCH 71/90] feat: Define graph structure in breadth_first_search (#1076) * feat: Define graph structure in breadth_first_search * using instead of typedef --- graph/breadth_first_search.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/graph/breadth_first_search.cpp b/graph/breadth_first_search.cpp index a03a13bcc..28cad4930 100644 --- a/graph/breadth_first_search.cpp +++ b/graph/breadth_first_search.cpp @@ -54,6 +54,17 @@ * \brief Graph algorithms */ namespace graph { + +/** + * \brief Representation of the graph as an adjacency list. + * + * For every vertex, there is a list of its neighbors in the order in which + * they were added to the graph. By default, the edges are directed, but + * an undirected graph can be represented simply by storing each each as + * two directed edges in both directions. + */ +using adjacency_list = std::vector>; + /** * \brief * Adds a directed edge from vertex u to vertex v. @@ -63,7 +74,7 @@ namespace graph { * @param v second vertex * */ -void add_directed_edge(std::vector> *graph, int u, int v) { +void add_directed_edge(adjacency_list *graph, int u, int v) { (*graph)[u].push_back(v); } @@ -78,7 +89,7 @@ void add_directed_edge(std::vector> *graph, int u, int v) { * @param v second vertex * */ -void add_undirected_edge(std::vector> *graph, int u, int v) { +void add_undirected_edge(adjacency_list *graph, int u, int v) { add_directed_edge(graph, u, v); add_directed_edge(graph, v, u); } @@ -93,11 +104,10 @@ void add_undirected_edge(std::vector> *graph, int u, int v) { * search. * */ -std::vector breadth_first_search( - const std::vector> &graph, int start) { +std::vector breadth_first_search(const adjacency_list &graph, int start) { /// vector to keep track of visited vertices std::vector visited(graph.size(), false); - /// a queue that stores vertices that need to be further explored + /// queue that stores vertices that need to be further explored std::queue tracker; /// mark the starting vertex as visited @@ -117,11 +127,13 @@ std::vector breadth_first_search( } return visited; } + } // namespace graph -void tests() { +/** Test function */ +static void tests() { /// Test 1 Begin - std::vector> graph(4, std::vector()); + graph::adjacency_list graph(4, std::vector()); graph::add_undirected_edge(&graph, 0, 1); graph::add_undirected_edge(&graph, 1, 2); graph::add_undirected_edge(&graph, 2, 3); @@ -168,7 +180,7 @@ int main() { std::cout << "Enter the number of edges: "; std::cin >> edges; - std::vector> graph(vertices); + graph::adjacency_list graph(vertices); std::cout << "Enter space-separated pairs of vertices that form edges: " << std::endl; From 7f7b2b0e68bea40a25a54e0e4531ed16e5152296 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Thu, 24 Sep 2020 00:11:28 +0530 Subject: [PATCH 72/90] added kadane algorithm function (#1079) * added kadane algorithm function * added changes * third commit * fixed some problems * fixed warnings * added documentation * Update kadane_algorithm/kadane.cpp Co-authored-by: David Leal * Update kadane_algorithm/kadane.cpp Co-authored-by: David Leal * Update kadane_algorithm/kadane.cpp Co-authored-by: David Leal * added details in the documentation * Update kadane_algorithm/kadane.cpp Co-authored-by: David Leal * Update kadane_algorithm/kadane.cpp Co-authored-by: David Leal * added kadane2.cpp * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * added some things * fixed * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * changes * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * added code * added changes suggested * Update .vscode/settings.json Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal Co-authored-by: David Leal --- dynamic_programming/kadane2.cpp | 73 +++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 dynamic_programming/kadane2.cpp diff --git a/dynamic_programming/kadane2.cpp b/dynamic_programming/kadane2.cpp new file mode 100644 index 000000000..c632e6f5a --- /dev/null +++ b/dynamic_programming/kadane2.cpp @@ -0,0 +1,73 @@ +/** + * @file + * @brief Implementation of [Kadane + * Algorithm] (https://en.wikipedia.org/wiki/Kadane%27s_algorithm) + * + * @details + * Kadane algorithm is used to find the maximum sum subarray in an array and + * maximum sum subarray problem is the task of finding a contiguous subarray + * with the largest sum + * + * ### Algorithm + * The simple idea of the algorithm is to search for all positive + * contiguous segments of the array and keep track of maximum sum contiguous + * segment among all positive segments(curr_sum is used for this) + * Each time we get a positive sum we compare it with max_sum and update max_sum + * if it is greater than curr_sum + * + * @author [Ayush Singh](https://github.com/ayush523) + */ +#include +#include +#include +/** + * @namespace dynamic_programming + * @brief Dynamic Programming algorithms + */ +namespace dynamic_programming { +/** + * @namespace kadane + * @brief Functions for [Kadane](https://en.wikipedia.org/wiki/Kadane%27s_algorithm) algorithm. + */ +namespace kadane { +/** + * @brief maxSubArray function is used to calculate the maximum sum subarray + * and returns the value of maximum sum which is stored in the variable max_sum + * @tparam N number of array size + * @param n array where numbers are saved + * @returns the value of maximum subarray sum + */ +template +int maxSubArray(const std::array &n) { + int curr_sum = + 0; // declaring a variable named as curr_sum and initialized it to 0 + int max_sum = INT_MIN; // Initialized max_sum to INT_MIN + for (int i : n) { // for loop to iterate over the elements of the array + curr_sum += n[i]; + max_sum = std::max(max_sum, curr_sum); // getting the maximum value + curr_sum = std::max(curr_sum, 0); // updating the value of curr_sum + } + return max_sum; // returning the value of max_sum +} +} // namespace kadane +} // namespace dynamic_programming + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + const int N = 5; + std::array n{}; // declaring array + // taking values of elements from user + for (int i = 0; i < n.size(); i++) { + std::cout << "Enter value of n[" << i << "]" + << "\n"; + std::cin >> n[i]; + } + int max_sum = dynamic_programming::kadane::maxSubArray( + n); // calling maxSubArray function + std::cout << "Maximum subarray sum is " << max_sum; // Printing the answer + + return 0; +} From 01965739494a16cb71ad4fb11694da0420ab5715 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Wed, 23 Sep 2020 18:42:14 +0000 Subject: [PATCH 73/90] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 3e9afd3b3..60840a60d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -60,6 +60,7 @@ * [Fibonacci Bottom Up](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/fibonacci_bottom_up.cpp) * [Floyd Warshall](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/floyd_warshall.cpp) * [Kadane](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/kadane.cpp) + * [Kadane2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/kadane2.cpp) * [Longest Common String](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_common_string.cpp) * [Longest Common Subsequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_common_subsequence.cpp) * [Longest Increasing Subsequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_increasing_subsequence.cpp) From 67be3a592df0eeb8964ea5e2382a12a42c9df9f4 Mon Sep 17 00:00:00 2001 From: "@8848hg" <53469557+Himalay12@users.noreply.github.com> Date: Mon, 28 Sep 2020 02:23:47 +0530 Subject: [PATCH 74/90] fix: short a bit the code (sorting/merge_sort.cpp) (#1066) * Merge_sort modified in while loop * Merge_sort modified in while loop * Merge_sort modified in while loop * Update merge_sort.cpp --- sorting/merge_sort.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/sorting/merge_sort.cpp b/sorting/merge_sort.cpp index 8beb4e44a..54b385d89 100644 --- a/sorting/merge_sort.cpp +++ b/sorting/merge_sort.cpp @@ -43,8 +43,8 @@ void merge(int *arr, int l, int m, int r) { i = 0; j = 0; k = l; - while (i < n1 && j < n2) { - if (L[i] <= R[j]) { + while (i < n1 || j < n2) { + if (j >= n2 || (i < n1 && L[i] <= R[j])) { arr[k] = L[i]; i++; } else { @@ -54,18 +54,6 @@ void merge(int *arr, int l, int m, int r) { k++; } - while (i < n1) { - arr[k] = L[i]; - i++; - k++; - } - - while (j < n2) { - arr[k] = R[j]; - j++; - k++; - } - delete[] L; delete[] R; } From 8e14b25fa36042f89d13cc456d71f02f41ef6caa Mon Sep 17 00:00:00 2001 From: Mertcan Davulcu <54983926+mertcandav@users.noreply.github.com> Date: Wed, 30 Sep 2020 20:21:25 +0300 Subject: [PATCH 75/90] Added strand sort algorithm (#1081) * Create strand_sort.cpp * Update strand_sort.cpp * Update strand_sort.cpp * Delete strand_sort.cpp * Create strand_sort.cpp * Update sorting/strand_sort.cpp Co-authored-by: David Leal * Update strand_sort.cpp * Update strand_sort.cpp * Update sorting/strand_sort.cpp Co-authored-by: David Leal * Update sorting/strand_sort.cpp Co-authored-by: David Leal * Update strand_sort.cpp * Update sorting/strand_sort.cpp Co-authored-by: David Leal * Update sorting/strand_sort.cpp Co-authored-by: David Leal * Update sorting/strand_sort.cpp Co-authored-by: David Leal * Update sorting/strand_sort.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update sorting/strand_sort.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update sorting/strand_sort.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update strand_sort.cpp * Update strand_sort.cpp * updating DIRECTORY.md Co-authored-by: David Leal Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + sorting/strand_sort.cpp | 87 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 sorting/strand_sort.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 60840a60d..e68f68aec 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -250,6 +250,7 @@ * [Shell Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/shell_sort.cpp) * [Shell Sort2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/shell_sort2.cpp) * [Slow Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/slow_sort.cpp) + * [Strand Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/strand_sort.cpp) * [Swap Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/swap_sort.cpp) * [Tim Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/tim_sort.cpp) diff --git a/sorting/strand_sort.cpp b/sorting/strand_sort.cpp new file mode 100644 index 000000000..732a28acc --- /dev/null +++ b/sorting/strand_sort.cpp @@ -0,0 +1,87 @@ +/** + * @file strand_sort.cpp + * @brief Implementation of [Strand Sort](https://en.wikipedia.org/wiki/Strand_sort) algorithm. + * + * @details + * Strand Sort is a sorting algorithm that works in \f$O(n)\f$ time if list is already sorted and works in \f$O(n^2)\f$ in worst case. + * + * It is passed over the array to be sorted once and the ascending (sequential) numbers are taken. + * After the first iteration, the sequential sub-array is put on the empty sorted array. + * The main sequence is passed over again and a new sub-sequence is created in order. + * Now that the sorted array is not empty, the newly extracted substring is merged with the sorted array. + * Repeat types 3 and 4 until the sub-sequence and main sequence are empty. + * + * @author [Mertcan Davulcu](https://github.com/mertcandav) + */ +#include +#include + +/** + * @namespace sorting + * @brief Sorting algorithms + */ +namespace sorting { + /** + * @namespace strand + * @brief Functions for [Strand Sort](https://en.wikipedia.org/wiki/Strand_sort) algorithm + */ + namespace strand { + /** + * @brief Apply sorting + * @tparam element type of list + * @param lst List to be sorted + * @returns Sorted list instance + */ + template + std::list strand_sort(std::list lst) { + if (lst.size() < 2) { // Returns list if empty or contains only one element + return lst; // Returns list + } + std::list result; // Define new "result" named list instance. + std::list sorted; // Define new "sorted" named list instance. + while(!lst.empty()) /* if lst is not empty */ { + sorted.push_back(lst.front()); // Adds the first element of "lst" list to the bottom of the "sorted" list. + lst.pop_front(); // Remove first element of "lst" list. + for (auto it = lst.begin(); it != lst.end(); ) { // Return the loop as long as the current iterator is not equal to the last literator of the "lst" list. + if (sorted.back() <= *it) { // If the last reference of the "sorted" list is less than or equal to the current iterator reference. + sorted.push_back(*it); // Adds the iterator retrieved in the loop under the "sorted" list. + it = lst.erase(it); // Deletes the element with the current iterator and assigns the deleted element to the iterator. + } else { + it++; // Next iterator. + } + } + result.merge(sorted); // Merge "result" list with "sorted" list. + } + return result; // Returns sorted list + } + } // namespace strand +} // namespace sorting + +/** + * @brief Function for testing + * @return N/A + */ +static void test() { + std::list lst = { -333, 525, 1, 0, 94, 52, 33 }; + + std::cout << "Before: "; + for(auto item: lst) { + std::cout << item << " "; + } + + lst = sorting::strand::strand_sort(lst); // Sort list. + + std::cout << "\nAfter: "; + for(auto item: lst) { + std::cout << item << " "; + } +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); + return 0; +} From b09b3da69ac82af6a175da61b6447314d730dd99 Mon Sep 17 00:00:00 2001 From: Pardeep Bhatt Date: Wed, 30 Sep 2020 23:33:34 +0530 Subject: [PATCH 76/90] [fix]: `dynamic_programming/cut_rod.cpp` does not compile (#1085) * Create largestBST_in_binary_tree.cpp * formatting filenames b15bd1ea * updating DIRECTORY.md * Update DIRECTORY.md * updating DIRECTORY.md * fixed compilation error in cut_rod.cpp code * fixed clang-tidy warnings * Delete largestbst_in_binary_tree.cpp * removed compilation errors * Update cut_rod.cpp * added requested changes in the code * added testing * Update DIRECTORY.md * Delete largestbst_in_binary_tree.cpp * added namespaces * added kadane2 algorithm * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/kadane2.cpp Co-authored-by: David Leal * Update dynamic_programming/cut_rod.cpp Co-authored-by: David Leal * Update dynamic_programming/cut_rod.cpp Co-authored-by: David Leal * Update dynamic_programming/cut_rod.cpp Co-authored-by: David Leal * added right funtion name in comments * Update dynamic_programming/cut_rod.cpp Co-authored-by: David Leal * Update dynamic_programming/cut_rod.cpp Co-authored-by: David Leal * added documentation for template parameter * checking for github actions * clang-format and clang-tidy fixes for db70ae2f Co-authored-by: anishmo99 Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Anish Mookherjee <59157112+anishmo99@users.noreply.github.com> Co-authored-by: David Leal --- dynamic_programming/cut_rod.cpp | 111 +++++++++++++++++++++++++++----- dynamic_programming/kadane2.cpp | 3 +- 2 files changed, 97 insertions(+), 17 deletions(-) diff --git a/dynamic_programming/cut_rod.cpp b/dynamic_programming/cut_rod.cpp index 136c78dbb..9513e487e 100644 --- a/dynamic_programming/cut_rod.cpp +++ b/dynamic_programming/cut_rod.cpp @@ -1,25 +1,104 @@ -/*Given a rod of length n inches and an array of prices that -contains prices of all pieces of size smaller than n. Determine -the maximum value obtainable by cutting up the rod and selling -the pieces.*/ +/** + * @file + * @brief Implementation of cutting a rod problem + * + * @details + * Given a rod of length n inches and an array of prices that + * contains prices of all pieces of size<=n. Determine + * the maximum profit obtainable by cutting up the rod and selling + * the pieces. + * + * ### Algorithm + * The idea is to break the given rod into every smaller piece as possible + * and then check profit for each piece, by calculating maximum profit for + * smaller pieces we will build the solution for larger pieces in bottom-up + * manner. + * + * @author [Anmol](https://github.com/Anmol3299) + * @author [Pardeep](https://github.com/Pardeep009) + */ +#include +#include +#include #include -using namespace std; -int cutrod(int p[], int n) { - int r[n + 1]; - r[0] = 0; - for (int j = 0; j < n; j++) { +/** + * @namespace dynamic_programming + * @brief Dynamic Programming algorithms + */ +namespace dynamic_programming { +/** + * @namespace cut_rod + * @brief Implementation of cutting a rod problem + */ +namespace cut_rod { +/** + * @brief Cuts the rod in different pieces and + * stores the maximum profit for each piece of the rod. + * @tparam T size of the price array + * @param n size of the rod in inches + * @param price an array of prices that contains prices of all pieces of size<=n + * @return maximum profit obtainable for @param n inch rod. + */ +template +int maxProfitByCuttingRod(const std::array &price, const int n) { + int *profit = + new int[n + 1]; // profit[i] will hold maximum profit for i inch rod + + profit[0] = 0; // if length of rod is zero, then no profit + + // outer loop will select size of rod, starting from 1 inch to n inch rod. + // inner loop will evaluate the maximum profit we can get for i inch rod by + // making every possible cut on it and will store it in profit[i]. + for (size_t i = 1; i <= n; i++) { int q = INT_MIN; - for (int i = 0; i <= j; i++) { - q = max(q, p[i] + r[j - i]); + for (size_t j = 1; j <= i; j++) { + q = std::max(q, price[j - 1] + profit[i - j]); } - r[j + 1] = q; + profit[i] = q; } - return r[n]; + int ans = profit[n]; + delete[] profit; + return ans; // returning maximum profit } +} // namespace cut_rod +} // namespace dynamic_programming + +/** + * @brief Function to test above algorithm + * @returns void + */ +static void test() { + // Test 1 + const int n1 = 8; // size of rod + std::array price1 = {1, 5, 8, 9, 10, 17, 17, 20}; // price array + const int max_profit1 = + dynamic_programming::cut_rod::maxProfitByCuttingRod(price1, n1); + const int expected_max_profit1 = 22; + assert(max_profit1 == expected_max_profit1); + std::cout << "Maximum profit with " << n1 << " inch road is " << max_profit1 + << std::endl; + + // Test 2 + const int n2 = 30; // size of rod + std::array price2 = { + 1, 5, 8, 9, 10, 17, 17, 20, 24, 30, // price array + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50}; + const int max_profit2 = + dynamic_programming::cut_rod::maxProfitByCuttingRod(price2, n2); + const int expected_max_profit2 = 90; + assert(max_profit2 == expected_max_profit2); + std::cout << "Maximum profit with " << n2 << " inch road is " << max_profit2 + << std::endl; +} + +/** + * @brief Main function + * @returns 0 on exit + */ int main() { - int price[] = {1, 5, 8, 9, 10, 17, 17, 20, 24, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50}; - cout << cutrod(price, 30); + // Testing + test(); return 0; } diff --git a/dynamic_programming/kadane2.cpp b/dynamic_programming/kadane2.cpp index c632e6f5a..c4732e202 100644 --- a/dynamic_programming/kadane2.cpp +++ b/dynamic_programming/kadane2.cpp @@ -27,7 +27,8 @@ namespace dynamic_programming { /** * @namespace kadane - * @brief Functions for [Kadane](https://en.wikipedia.org/wiki/Kadane%27s_algorithm) algorithm. + * @brief Functions for + * [Kadane](https://en.wikipedia.org/wiki/Kadane%27s_algorithm) algorithm. */ namespace kadane { /** From bcf79e259a7ec76216b0430fa8230ae63da5523b Mon Sep 17 00:00:00 2001 From: Darshana Sarma <69460657+Darshana-Sarma@users.noreply.github.com> Date: Thu, 1 Oct 2020 00:18:39 +0530 Subject: [PATCH 77/90] feat: Added Postfix Evaluation (#1083) * feat:Added Postfix evaluation * updating DIRECTORY.md * Fixed global variable name and stack def * Fixed c from int to float * Fixed clang-tidy * Modified code for larger values * Added string header * Suggested changes * Fixed clang-tidy * Apply suggestions from code review Co-authored-by: David Leal * Added description for class * Fixed function calling * Apply suggestions from code review Co-authored-by: David Leal * Updated evaluate function documentation * Apply suggestions from code review Co-authored-by: David Leal * Update others/postfix_evaluation.cpp Co-authored-by: David Leal * Update others/postfix_evaluation.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal Co-authored-by: Darshana-Sarma Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> --- DIRECTORY.md | 1 + others/postfix_evaluation.cpp | 178 ++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 others/postfix_evaluation.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index e68f68aec..b38f5fdbb 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -190,6 +190,7 @@ * [Palindrome Of Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/palindrome_of_number.cpp) * [Paranthesis Matching](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/paranthesis_matching.cpp) * [Pascal Triangle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/pascal_triangle.cpp) + * [Postfix Evaluation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/postfix_evaluation.cpp) * [Primality Test](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/primality_test.cpp) * [Smallest Circle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/smallest_circle.cpp) * [Sparse Matrix](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/sparse_matrix.cpp) diff --git a/others/postfix_evaluation.cpp b/others/postfix_evaluation.cpp new file mode 100644 index 000000000..1ea77c6f8 --- /dev/null +++ b/others/postfix_evaluation.cpp @@ -0,0 +1,178 @@ +/** + * @file + * @brief Evaluation of [Postfix Expression](https://en.wikipedia.org/wiki/Reverse_Polish_notation) + * @author [Darshana Sarma](https://github.com/Darshana-Sarma) + * @details + * Create a stack to store operands (or values). + * Scan the given expression and do following for every scanned element. + * If the element is a number, push it into the stack + * If the element is a operator, pop operands for the operator from stack. + * Evaluate the operator and push the result back to the stack + * When the expression is ended, the number in the stack is the final answer + */ +#include // for all_of +#include // for std::array +#include // for assert +#include // for io operations +#include // for stof + +/** + * @namespace others + * @brief Other algorithms + */ +namespace others { +/** + * @namespace postfix_expression + * @brief Functions for Postfix Expression algorithm + */ +namespace postfix_expression { + /** + * @brief Creates an array to be used as stack for storing values + */ +class Stack { + public: + std::array stack{}; ///< Array which will be used to store numbers in the input + int stackTop = -1; ///< Represents the index of the last value added to array. -1 means array is empty +}; + +/** + * @brief Pushing operand, also called the number in the array to the stack + * @param operand float value from the input array or evaluation + * @param stack stack containing numbers + * @returns none + */ +void push(float operand, Stack *stack) { + stack->stackTop++; + stack->stack[stack->stackTop] = operand; +} + +/** + * @brief Popping operand, also called the number from the stack + * @param stack stack containing numbers + * @returns operand float on top of stack + */ +float pop(Stack *stack) { + float operand = stack->stack[stack->stackTop]; + stack->stackTop--; + return operand; +} + +/** + * @brief Checks if scanned string is a number + * @param s scanned string + * @returns bool boolean value if string is number + */ +bool is_number(const std::string &s) { + return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); +} + +/** + * @brief Evaluate answer using given last two operands from and operation + * @param a second last added operand which will be used for evaluation + * @param b last added operand which will be used for evaluation + * @param operation to be performed with respective floats + * @param stack containing numbers + * @returns none + */ +void evaluate(float a, float b, const std::string &operation, Stack *stack) { + float c = 0; + const char *op = operation.c_str(); + switch (*op) { + case '+': + c = a + b; // Addition of numbers + others::postfix_expression::push(c, stack); + break; + + case '-': + c = a - b; // Subtraction of numbers + others::postfix_expression::push(c, stack); + break; + + case '*': + c = a * b; // Multiplication of numbers + others::postfix_expression::push(c, stack); + break; + + case '/': + c = a / b; // Division of numbers + others::postfix_expression::push(c, stack); + break; + + default: + std::cout << "Operator not defined\n"; + break; + } +} + +/** + * @brief Postfix Evaluation algorithm to compute the value from given input + * array + * @tparam N number of array size + * @param input Array of characters consisting of numbers and operations + * @returns stack[stackTop] returns the top value from the stack + */ +template +float postfix_evaluation(std::array input) { + Stack stack; + int j = 0; + + while (j < N) { + std::string scan = input[j]; + if (is_number(scan)) { + push(std::stof(scan), &stack); + + } else { + float op2 = pop(&stack); + float op1 = pop(&stack); + + evaluate(op1, op2, scan, &stack); + } + j++; + } + + std::cout << stack.stack[stack.stackTop] << "\n"; + + return stack.stack[stack.stackTop]; +} +} // namespace postfix_expression +} // namespace others + + +/** + * @brief Test function 1 with input array + * {'2', '3', '1', '*', '+', '9', '-'} + * @returns none + */ +static void test_function_1() { + std::array input = {"2", "3", "1", "*", "+", "9", "-"}; + + float answer = others::postfix_expression::postfix_evaluation(input); + + assert(answer == -4); +} + +/** + * @brief Test function 2 with input array + * {'1', '2', '+', '2', '/', '5', '*', '7', '+'} + * @returns none + */ +static void test_function_2() { + std::array input = {"100", "200", "+", "2", "/", + "5", "*", "7", "+"}; + float answer = others::postfix_expression::postfix_evaluation(input); + + assert(answer == 757); +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test_function_1(); + test_function_2(); + + std::cout << "\nTest implementations passed!\n"; + + return 0; +} From 8ccd66bda32583450f228bedc21ba8ad1c3f978a Mon Sep 17 00:00:00 2001 From: Rakshaa Viswanathan <46165429+rakshaa2000@users.noreply.github.com> Date: Fri, 2 Oct 2020 17:51:16 +0530 Subject: [PATCH 78/90] Created jumpgame.cpp (#1068) * Created jumpgame.cpp An algorithm to check if you can reach the destination * Changed header files * Changed header files * Fixed warnings * Fixed bug and removed namespace std * fixed bugs final * Updated changes * added documentation Added description of the problem and a brief explanation of the algorithm. * Delete linked list without head pointer You are given a pointer/ reference to the node which is to be deleted from the linked list of N nodes. The task is to delete the node. Head pointer is not given. * fixed bugs * Author_edit_Update greedy_algorithms/jumpgame.cpp Co-authored-by: David Leal * main_func_Update greedy_algorithms/jumpgame.cpp Co-authored-by: David Leal * documentation added, testing yet to be added * Added test function * deleted linked list without head * documentation update 1 Co-authored-by: David Leal * documentation update 2 Co-authored-by: David Leal * test func update 1 Co-authored-by: David Leal * documentation updated final * Formatting update Co-authored-by: David Leal * Formatting update 1 Co-authored-by: David Leal * Formatting update 2 Co-authored-by: David Leal * Update return Co-authored-by: David Leal * Update greedy_algorithms/jumpgame.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Update greedy_algorithms/jumpgame.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Added link to the problem * documentation update 3 Co-authored-by: David Leal * documentation update 4 * Update greedy_algorithms/jumpgame.cpp Co-authored-by: David Leal * Update greedy_algorithms/jumpgame.cpp Co-authored-by: David Leal * Update greedy_algorithms/jumpgame.cpp Co-authored-by: David Leal * Revert "Update greedy_algorithms/jumpgame.cpp" This reverts commit 072f9cc946271eab3807d59a48a0958ca375458e. * Revert "Update greedy_algorithms/jumpgame.cpp" This reverts commit f308a5f6c2f02feb4b3a1122e7a3bd89b123e26d. * Revert "Update greedy_algorithms/jumpgame.cpp" This reverts commit 4b6eb0cbc95568b2bc3e5e2bc4a2f76433ef8374. Co-authored-by: David Leal Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> --- greedy_algorithms/jumpgame.cpp | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 greedy_algorithms/jumpgame.cpp diff --git a/greedy_algorithms/jumpgame.cpp b/greedy_algorithms/jumpgame.cpp new file mode 100644 index 000000000..8a8903191 --- /dev/null +++ b/greedy_algorithms/jumpgame.cpp @@ -0,0 +1,68 @@ +/** + * @file + * @brief Implementation of an algorithm to solve the [jumping game]((https://leetcode.com/problems/jump-game/)) problem + * @details + * **Problem statement:** Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Determine if you are able to reach the last index. + * This solution takes in input as a vector and output as a boolean to check if you can reach the last position. + * We name the indices good and bad based on whether we can reach the destination if we start at that position. + * We initialize the last index as lastPos. + * Here, we start from the end of the array and check if we can ever reach the first index. + * We check if the sum of the index and the maximum jump count given is greater than or equal to the lastPos. + * If yes, then that is the last position you can reach starting from the back. + * After the end of the loop, if we reach the lastPos as 0, then the destination can be reached from the start position. + * @author [Rakshaa Viswanathan](https://github.com/rakshaa2000) +*/ + +#include +#include +#include + + + /** + * @brief This function implements the above algorithm + * @param array of numbers containing the maximum jump (in steps) from that index + * @returns bool value whether final index can be reached or not + */ +bool canJump(const std::vector &nums) { + auto lastPos = nums.size() - 1; + for (auto i = nums.size() - 1; i >= 0; i--) { + if (i + nums[i] >= lastPos) { + lastPos = i; + } + } + return lastPos == 0; +} + + +/** + * @brief Function to test above algorithm + * @returns void + */ +static void test(){ + // Test 1 + std::vector num1={4,3,1,0,5}; + assert(canJump(num1)==true); + std::cout<<"Input: "; + for(auto i: num1){ + std::cout< num2={3,2,1,0,4}; + assert(canJump(num2)==false); + std::cout<<"Input: "; + for(auto i: num2){ + std::cout< Date: Fri, 2 Oct 2020 17:52:14 +0530 Subject: [PATCH 79/90] Updated stack_using_array.cpp (#1137) Added bottom function for getting bottom element from a stack . --- data_structures/stack_using_array.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/stack_using_array.cpp b/data_structures/stack_using_array.cpp index 0c0813d5e..8483d6ad7 100644 --- a/data_structures/stack_using_array.cpp +++ b/data_structures/stack_using_array.cpp @@ -26,6 +26,7 @@ void show() { } void topmost() { std::cout << "\nTopmost element: " << stack[stack_idx - 1]; } +void bottom() { std::cout << "\nBottom element: " << stack[0]; } // If we need access to first element without using pop command int main() { std::cout << "\nEnter stack_size of stack : "; std::cin >> stack_size; @@ -37,6 +38,7 @@ int main() { std::cout << "\n2. Pop"; std::cout << "\n3. Print"; std::cout << "\n4. Print topmost element:"; + std::cout << "\n5. Print Bottom element:"; std::cout << "\nEnter Your Choice : "; std::cin >> ch; if (ch == 1) { @@ -49,6 +51,8 @@ int main() { show(); } else if (ch == 4) { topmost(); + } else if(ch == 5) { + bottom(); } } while (ch != 0); From a6a3b7deec6ee0c8076be7295e3d3bd03b3bc7ad Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Fri, 2 Oct 2020 12:22:58 +0000 Subject: [PATCH 80/90] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index b38f5fdbb..08370c1b2 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -99,6 +99,7 @@ ## Greedy Algorithms * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/dijkstra.cpp) * [Huffman](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/huffman.cpp) + * [Jumpgame](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/jumpgame.cpp) * [Knapsack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/knapsack.cpp) * [Kruskals Minimum Spanning Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/kruskals_minimum_spanning_tree.cpp) * [Prims Minimum Spanning Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/prims_minimum_spanning_tree.cpp) From 04e0acc3cf5f83a6aa018575478b4eee54a9c97f Mon Sep 17 00:00:00 2001 From: Divyajyoti Ukirde <30872426+divyajyotiuk@users.noreply.github.com> Date: Mon, 5 Oct 2020 18:22:27 +0530 Subject: [PATCH 81/90] feat: added check_factorial (#1155) * feat: added check_factorial * updating DIRECTORY.md * feat: added check_factorial * review changes * review changes Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + math/check_factorial.cpp | 64 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 math/check_factorial.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 08370c1b2..7e72b439e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -122,6 +122,7 @@ * [Armstrong Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/armstrong_number.cpp) * [Binary Exponent](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/binary_exponent.cpp) * [Check Amicable Pair](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/check_amicable_pair.cpp) + * [Check Factorial](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/check_factorial.cpp) * [Check Prime](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/check_prime.cpp) * [Complex Numbers](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/complex_numbers.cpp) * [Double Factorial](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/double_factorial.cpp) diff --git a/math/check_factorial.cpp b/math/check_factorial.cpp new file mode 100644 index 000000000..2170b81a0 --- /dev/null +++ b/math/check_factorial.cpp @@ -0,0 +1,64 @@ +/** + * @file + * @brief A simple program to check if the given number is a factorial of some + * number or not. + * @author [Divyajyoti Ukirde](https://github.com/divyajyotiuk) + */ +#include +#include + +/** + * Function to check if the given number is factorial of some number or not. + * @param n number to be checked. + * @return if number is a factorial, returns true, else false. + */ + +bool is_factorial(uint64_t n) { + if (n <= 0) { + return false; + } + for (uint32_t i = 1;; i++) { + if (n % i != 0) { + break; + } + n = n / i; + } + if (n == 1) { + return true; + } else { + return false; + } +} + +/** Test function + * @returns void + */ +void tests() { + std::cout << "Test 1:\t n=50\n"; + assert(is_factorial(50) == false); + std::cout << "passed\n"; + + std::cout << "Test 2:\t n=720\n"; + assert(is_factorial(720) == true); + std::cout << "passed\n"; + + std::cout << "Test 3:\t n=0\n"; + assert(is_factorial(0) == false); + std::cout << "passed\n"; + + std::cout << "Test 4:\t n=479001600\n"; + assert(is_factorial(479001600) == true); + std::cout << "passed\n"; + + std::cout << "Test 5:\t n=-24\n"; + assert(is_factorial(-24) == false); + std::cout << "passed\n"; +} + +/** Main function + * @returns 0 on exit + */ +int main() { + tests(); + return 0; +} From 895ae31cd7742d37ac6b892278f1797dbd775568 Mon Sep 17 00:00:00 2001 From: Deep Raval Date: Sun, 11 Oct 2020 20:01:00 +0530 Subject: [PATCH 82/90] Fixed a typo (#1237) --- machine_learning/neural_network.cpp | 74 ++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/machine_learning/neural_network.cpp b/machine_learning/neural_network.cpp index b477f5e2e..f77b732bb 100644 --- a/machine_learning/neural_network.cpp +++ b/machine_learning/neural_network.cpp @@ -118,7 +118,7 @@ double identity_function(const double &x) { return x; } namespace layers { /** * neural_network::layers::DenseLayer class is used to store all necessary - * information about the layers (i.e. neurons, activation and kernal). This + * information about the layers (i.e. neurons, activation and kernel). This * class is used by NeuralNetwork class to store layers. * */ @@ -129,18 +129,18 @@ class DenseLayer { double (*dactivation_function)(const double &); int neurons; // To store number of neurons (used in summary) std::string activation; // To store activation name (used in summary) - std::vector> kernal; // To store kernal (aka weights) + std::vector> kernel; // To store kernel (aka weights) /** * Constructor for neural_network::layers::DenseLayer class * @param neurons number of neurons * @param activation activation function for layer - * @param kernal_shape shape of kernal - * @param random_kernal flag for whether to intialize kernal randomly + * @param kernel_shape shape of kernel + * @param random_kernel flag for whether to intialize kernel randomly */ DenseLayer(const int &neurons, const std::string &activation, - const std::pair &kernal_shape, - const bool &random_kernal) { + const std::pair &kernel_shape, + const bool &random_kernel) { // Choosing activation (and it's derivative) if (activation == "sigmoid") { activation_function = neural_network::activations::sigmoid; @@ -167,21 +167,21 @@ class DenseLayer { } this->activation = activation; // Setting activation name this->neurons = neurons; // Setting number of neurons - // Initialize kernal according to flag - if (random_kernal) { - uniform_random_initialization(kernal, kernal_shape, -1.0, 1.0); + // Initialize kernel according to flag + if (random_kernel) { + uniform_random_initialization(kernel, kernel_shape, -1.0, 1.0); } else { - unit_matrix_initialization(kernal, kernal_shape); + unit_matrix_initialization(kernel, kernel_shape); } } /** * Constructor for neural_network::layers::DenseLayer class * @param neurons number of neurons * @param activation activation function for layer - * @param kernal values of kernal (useful in loading model) + * @param kernel values of kernel (useful in loading model) */ DenseLayer(const int &neurons, const std::string &activation, - const std::vector> &kernal) { + const std::vector> &kernel) { // Choosing activation (and it's derivative) if (activation == "sigmoid") { activation_function = neural_network::activations::sigmoid; @@ -208,7 +208,7 @@ class DenseLayer { } this->activation = activation; // Setting activation name this->neurons = neurons; // Setting number of neurons - this->kernal = kernal; // Setting supplied kernal values + this->kernel = kernel; // Setting supplied kernel values } /** @@ -251,11 +251,11 @@ class NeuralNetwork { * Private Constructor for class NeuralNetwork. This constructor * is used internally to load model. * @param config vector containing pair (neurons, activation) - * @param kernals vector containing all pretrained kernals + * @param kernels vector containing all pretrained kernels */ NeuralNetwork( const std::vector> &config, - const std::vector>> &kernals) { + const std::vector>> &kernels) { // First layer should not have activation if (config.begin()->second != "none") { std::cerr << "ERROR (" << __func__ << ") : "; @@ -275,7 +275,7 @@ class NeuralNetwork { // Reconstructing all pretrained layers for (size_t i = 0; i < config.size(); i++) { layers.emplace_back(neural_network::layers::DenseLayer( - config[i].first, config[i].second, kernals[i])); + config[i].first, config[i].second, kernels[i])); } std::cout << "INFO: Network constructed successfully" << std::endl; } @@ -291,7 +291,7 @@ class NeuralNetwork { std::vector> current_pass = X; details.emplace_back(X); for (const auto &l : layers) { - current_pass = multiply(current_pass, l.kernal); + current_pass = multiply(current_pass, l.kernel); current_pass = apply_function(current_pass, l.activation_function); details.emplace_back(current_pass); } @@ -329,7 +329,7 @@ class NeuralNetwork { std::exit(EXIT_FAILURE); } // Separately creating first layer so it can have unit matrix - // as kernal. + // as kernel. layers.push_back(neural_network::layers::DenseLayer( config[0].first, config[0].second, {config[0].first, config[0].first}, false)); @@ -512,13 +512,13 @@ class NeuralNetwork { predicted; auto activations = this->__detailed_single_prediction(X[i]); // Gradients vector to store gradients for all layers - // They will be averaged and applied to kernal + // They will be averaged and applied to kernel std::vector>> gradients; gradients.resize(this->layers.size()); // First intialize gradients to zero for (size_t i = 0; i < gradients.size(); i++) { zeroes_initialization( - gradients[i], get_shape(this->layers[i].kernal)); + gradients[i], get_shape(this->layers[i].kernel)); } predicted = activations.back(); // Predicted vector cur_error = predicted - Y[i]; // Absoulute error @@ -539,16 +539,16 @@ class NeuralNetwork { this->layers[j].dactivation_function)); // Calculating gradient for current layer grad = multiply(transpose(activations[j]), cur_error); - // Change error according to current kernal values + // Change error according to current kernel values cur_error = multiply(cur_error, - transpose(this->layers[j].kernal)); + transpose(this->layers[j].kernel)); // Adding gradient values to collection of gradients gradients[j] = gradients[j] + grad / double(batch_size); } // Applying gradients for (size_t j = this->layers.size() - 1; j >= 1; j--) { - // Updating kernal (aka weights) - this->layers[j].kernal = this->layers[j].kernal - + // Updating kernel (aka weights) + this->layers[j].kernel = this->layers[j].kernel - gradients[j] * learning_rate; } } @@ -670,14 +670,14 @@ class NeuralNetwork { total_layers neurons(1st neural_network::layers::DenseLayer) activation_name(1st - neural_network::layers::DenseLayer) kernal_shape(1st - neural_network::layers::DenseLayer) kernal_values + neural_network::layers::DenseLayer) kernel_shape(1st + neural_network::layers::DenseLayer) kernel_values . . . neurons(Nth neural_network::layers::DenseLayer) activation_name(Nth - neural_network::layers::DenseLayer) kernal_shape(Nth - neural_network::layers::DenseLayer) kernal_value + neural_network::layers::DenseLayer) kernel_shape(Nth + neural_network::layers::DenseLayer) kernel_value For Example, pretrained model with 3 layers:
@@ -709,9 +709,9 @@ class NeuralNetwork {
         out_file << std::endl;
         for (const auto &layer : this->layers) {
             out_file << layer.neurons << ' ' << layer.activation << std::endl;
-            const auto shape = get_shape(layer.kernal);
+            const auto shape = get_shape(layer.kernel);
             out_file << shape.first << ' ' << shape.second << std::endl;
-            for (const auto &row : layer.kernal) {
+            for (const auto &row : layer.kernel) {
                 for (const auto &val : row) {
                     out_file << val << ' ';
                 }
@@ -740,7 +740,7 @@ class NeuralNetwork {
         }
         std::vector> config;  // To store config
         std::vector>>
-            kernals;  // To store pretrained kernals
+            kernels;  // To store pretrained kernels
         // Loading model from saved file format
         size_t total_layers = 0;
         in_file >> total_layers;
@@ -748,23 +748,23 @@ class NeuralNetwork {
             int neurons = 0;
             std::string activation;
             size_t shape_a = 0, shape_b = 0;
-            std::vector> kernal;
+            std::vector> kernel;
             in_file >> neurons >> activation >> shape_a >> shape_b;
             for (size_t r = 0; r < shape_a; r++) {
                 std::valarray row(shape_b);
                 for (size_t c = 0; c < shape_b; c++) {
                     in_file >> row[c];
                 }
-                kernal.push_back(row);
+                kernel.push_back(row);
             }
             config.emplace_back(make_pair(neurons, activation));
             ;
-            kernals.emplace_back(kernal);
+            kernels.emplace_back(kernel);
         }
         std::cout << "INFO: Model loaded successfully" << std::endl;
         in_file.close();  // Closing file
         return NeuralNetwork(
-            config, kernals);  // Return instance of NeuralNetwork class
+            config, kernels);  // Return instance of NeuralNetwork class
     }
 
     /**
@@ -785,8 +785,8 @@ class NeuralNetwork {
                       << layers[i - 1].neurons;  // number of neurons
             std::cout << ", Activation : "
                       << layers[i - 1].activation;  // activation
-            std::cout << ", Kernal Shape : "
-                      << get_shape(layers[i - 1].kernal);  // kernal shape
+            std::cout << ", kernel Shape : "
+                      << get_shape(layers[i - 1].kernel);  // kernel shape
             std::cout << std::endl;
         }
         std::cout

From 5a7120165e13cee17b2e8470b82675c3b2768f7f Mon Sep 17 00:00:00 2001
From: Saurav Uppoor <41644947+sauravUppoor@users.noreply.github.com>
Date: Tue, 13 Oct 2020 23:43:03 +0530
Subject: [PATCH 83/90] docs: Update comments to English from Hindi in
 sudoku_solve.cpp (#1273)

Co-authored-by: saurav 
---
 backtracking/sudoku_solve.cpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/backtracking/sudoku_solve.cpp b/backtracking/sudoku_solve.cpp
index 57c6b5274..ccde36383 100644
--- a/backtracking/sudoku_solve.cpp
+++ b/backtracking/sudoku_solve.cpp
@@ -22,7 +22,7 @@
  */
 namespace backtracking {
     /**
-     * Checks if it's possible to place a 'no'
+     * Checks if it's possible to place a number 'no'
      * @tparam V number of vertices in the array
      * @param mat matrix where numbers are saved
      * @param i current index in rows
@@ -34,14 +34,14 @@ namespace backtracking {
      */
     template 
     bool isPossible(const std::array , V> &mat, int i, int j, int no, int n) {
-        /// Row or col nahin hona chahiye
+        /// 'no' shouldn't be present in either row i or column j
         for (int x = 0; x < n; x++) {
             if (mat[x][j] == no || mat[i][x] == no) {
                 return false;
             }
         }
 
-        /// Subgrid mein nahi hona chahiye
+        /// 'no' shouldn't be present in the 3*3 subgrid
         int sx = (i / 3) * 3;
         int sy = (j / 3) * 3;
 
@@ -91,7 +91,7 @@ namespace backtracking {
     bool solveSudoku(std::array , V> &mat, int i, int j) {
         /// Base Case
         if (i == 9) {
-            /// Solve kr chuke hain for 9 rows already
+            /// Solved for 9 rows already
             backtracking::printMat(mat, 9);
             return true;
         }
@@ -109,17 +109,17 @@ namespace backtracking {
         /// Try to place every possible no
         for (int no = 1; no <= 9; no++) {
             if (backtracking::isPossible(mat, i, j, no, 9)) {
-                /// Place the no - assuming solution aa jayega
+                /// Place the 'no' - assuming a solution will exist
                 mat[i][j] = no;
-                bool aageKiSolveHui = backtracking::solveSudoku(mat, i, j + 1);
-                if (aageKiSolveHui) {
+                bool solution_found = backtracking::solveSudoku(mat, i, j + 1);
+                if (solution_found) {
                     return true;
                 }
-                /// Nahin solve hui
+                /// Couldn't find a solution
                 /// loop will place the next no.
             }
         }
-        /// Sare no try kr liey, kisi se bhi solve nahi hui
+        /// Solution couldn't be found for any of the numbers provided
         mat[i][j] = 0;
         return false;
     }

From 78fbb40e9d9e4eed4ef36c07ebcfd6e67fe40ddb Mon Sep 17 00:00:00 2001
From: David Leal 
Date: Wed, 14 Oct 2020 16:17:27 -0500
Subject: [PATCH 84/90] [fix/feat]: Added Code of Conduct (#1135)

Code of Conduct from the https://github.com/TheAlgorithms/.github repository.
---
 CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 CODE_OF_CONDUCT.md

diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..535cbef32
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at 1anuppanwar@gmail.com, dynamitechetan@gmail.com, nikhilkala8@gmail.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at 
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+

From 06b6714b0e3acc934518f257be3adc6e5694f4f8 Mon Sep 17 00:00:00 2001
From: Krishna Pal Deora <53469625+Krishnapal4050@users.noreply.github.com>
Date: Fri, 16 Oct 2020 18:11:51 +0530
Subject: [PATCH 85/90] =?UTF-8?q?Hopcroft=E2=80=93Karp=20algorithm=20imple?=
 =?UTF-8?q?mentation=20(#1087)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Hopcroft–Karp algorithm implementation

The Hopcroft–Karp algorithm is an algorithm that takes as input a bipartite graph and produces as output a maximum cardinality matching.

* Update hopcroft_karp.cpp

* fix : fixed the issues

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update hopcroft_karp.cpp

Added Global variable as private variable

* Update hopcroft_karp.cpp

* Update hopcroft_karp.cpp

* Update hopcroft_karp.cpp

* Update hopcroft_karp.cpp

* Update hopcroft_karp.cpp

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update hopcroft_karp.cpp

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* updating DIRECTORY.md

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* clang-tidy fixes for 780580f71fd7245da9ec27aa54c35fca6793883d

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* clang-tidy fixes for 03f97cb241e520c6ed6bb4427ca9977c26c303c6

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: David Leal 

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* applied suggested changes

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* applied changes

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* improved documentation

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update hopcroft_karp.cpp

* corrected code

* Update hopcroft_karp.cpp

* changed the class name

* applied suggested changes

included the HKGraph class and it's member functions inside namespace graph

* Update graph/hopcroft_karp.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update hopcroft_karp.cpp

* Update hopcroft_karp.cpp

* added sample test cases

* Update DIRECTORY.md

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Update DIRECTORY.md

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* updating DIRECTORY.md

Co-authored-by: David Leal 
Co-authored-by: @8848hg <53469557+Himalay12@users.noreply.github.com>
Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>
---
 DIRECTORY.md            |   1 +
 graph/hopcroft_karp.cpp | 325 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 326 insertions(+)
 create mode 100644 graph/hopcroft_karp.cpp

diff --git a/DIRECTORY.md b/DIRECTORY.md
index 7e72b439e..c431979aa 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -84,6 +84,7 @@
   * [Depth First Search With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search_with_stack.cpp)
   * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dijkstra.cpp)
   * [Hamiltons Cycle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hamiltons_cycle.cpp)
+  * [Hopcroft Karp](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hopcroft_karp.cpp)
   * [Is Graph Bipartite](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/is_graph_bipartite.cpp)
   * [Kosaraju](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kosaraju.cpp)
   * [Kruskal](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kruskal.cpp)
diff --git a/graph/hopcroft_karp.cpp b/graph/hopcroft_karp.cpp
new file mode 100644
index 000000000..d4e002948
--- /dev/null
+++ b/graph/hopcroft_karp.cpp
@@ -0,0 +1,325 @@
+/**
+ * @file 
+ * @brief  Implementation of [Hopcroft–Karp](https://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm) algorithm.
+ * @details 
+ * The Hopcroft–Karp algorithm is an algorithm that takes as input a bipartite graph 
+ * and produces as output a maximum cardinality matching, it runs in O(E√V) time in worst case.
+ * 
+ * ### Bipartite graph
+ * A bipartite graph (or bigraph) is a graph whose vertices can be divided into two disjoint 
+ * and independent sets U and V such that every edge connects a vertex in U to one in V. 
+ * Vertex sets U and V are usually called the parts of the graph. 
+ * Equivalently, a bipartite graph is a graph that does not contain any odd-length cycles.
+ * 
+ * ### Matching and Not-Matching edges
+ * Given a matching M, edges that are part of matching are called Matching edges and edges that are not part 
+ * of M (or connect free nodes) are called Not-Matching edges.
+ * 
+ * ### Maximum cardinality matching
+ * Given a bipartite graphs G = ( V = ( X , Y ) , E ) whose partition has the parts X and Y, 
+ * with E denoting the edges of the graph, the goal is to find a matching with as many edges as possible. 
+ * Equivalently, a matching that covers as many vertices as possible.
+ * 
+ * ### Augmenting paths
+ * Given a matching M, an augmenting path is an alternating path that starts from and ends on free vertices. 
+ * All single edge paths that start and end with free vertices are augmenting paths.
+ * 
+ * 
+ * ### Concept
+ * A matching M is not maximum if there exists an augmenting path. It is also true other way,
+ * i.e, a matching is maximum if no augmenting path exists.
+ * 
+ * 
+ * ### Algorithm
+ * 1) Initialize the Maximal Matching M as empty.
+ * 2) While there exists an Augmenting Path P
+ *   Remove matching edges of P from M and add not-matching edges of P to M
+ *   (This increases size of M by 1 as P starts and ends with a free vertex
+ *   i.e. a node that is not part of matching.)
+ * 3) Return M. 
+ * 
+ * 
+ *
+ * @author [Krishna Pal Deora](https://github.com/Krishnapal4050)
+ * 
+ */
+
+
+#include 
+#include  
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * @namespace graph 
+ * @brief Graph algorithms
+ */
+ namespace graph { 
+
+/**
+ * @brief Represents Bipartite graph for
+ * Hopcroft Karp implementation
+ */
+class HKGraph
+{
+    int m{};  ///< m is the number of vertices on left side of Bipartite Graph
+    int n{};  ///< n is the number of vertices on right side of Bipartite Graph
+    const int NIL{0};
+    const int INF{INT_MAX};
+
+    std::vector >adj;  ///< adj[u] stores adjacents of left side and 0 is used for dummy vertex
+
+    std::vector pair_u; ///< value of vertex 'u' ranges from 1 to m
+    std::vector pair_v; ///< value of vertex 'v' ranges from 1 to n
+    std::vector dist;   ///< dist represents the distance between vertex 'u' and vertex 'v'
+
+public:
+    HKGraph();		       // Default Constructor
+    HKGraph(int m, int n);     // Constructor
+    void addEdge(int u, int v); // To add edge
+    
+    bool bfs(); // Returns true if there is an augmenting path    
+    bool dfs(int u); // Adds augmenting path if there is one beginning with u  
+	
+    int hopcroftKarpAlgorithm();  // Returns size of maximum matching
+};
+
+
+/**
+ * @brief This function counts the number of augmenting paths between left and right sides of the Bipartite graph
+ * @returns size of maximum matching
+ */
+int HKGraph::hopcroftKarpAlgorithm()
+{
+
+    // pair_u[u] stores pair of u in matching on left side of Bipartite Graph.
+    // If u doesn't have any pair, then pair_u[u] is NIL
+    pair_u = std::vector(m + 1,NIL); 
+
+    // pair_v[v] stores pair of v in matching on right side of Biparite Graph.
+    // If v doesn't have any pair, then pair_u[v] is NIL
+    pair_v = std::vector(n + 1,NIL); 
+
+    dist = std::vector(m + 1);  // dist[u] stores distance of left side vertices
+
+    int result = 0;  // Initialize result
+
+    // Keep updating the result while there is an augmenting path possible.
+    while (bfs())
+    {
+        // Find a free vertex to check for a matching
+        for (int u = 1; u <= m; u++){
+
+            // If current vertex is free and there is
+            // an augmenting path from current vertex
+            // then increment the result
+            if (pair_u[u] == NIL && dfs(u)){
+                result++;
+	    }
+	}
+    }
+    return result;
+}
+
+
+/**
+ * @brief This function checks for the possibility of augmented path availability 
+ * @returns `true` if there is an augmenting path available
+ * @returns `false` if there is no augmenting path available
+ */
+bool HKGraph::bfs()
+{
+    std::queue q; // an integer queue for bfs
+
+    // First layer of vertices (set distance as 0)
+    for (int u = 1; u <= m; u++)
+    {
+        // If this is a free vertex, add it to queue
+        if (pair_u[u] == NIL){
+            
+            dist[u] = 0; // u is not matched so distance is 0
+            q.push(u);
+        }
+
+        else{
+            dist[u] = INF; // set distance as infinite so that this vertex is considered next time for availibility
+	}
+    }
+
+    
+    dist[NIL] = INF; // Initialize distance to NIL as infinite
+
+    // q is going to contain vertices of left side only.
+    while (!q.empty())
+    {
+        int u = q.front();  // dequeue a vertex
+        q.pop();
+
+        // If this node is not NIL and can provide a shorter path to NIL then
+        if (dist[u] < dist[NIL])
+        {
+            // Get all the adjacent vertices of the dequeued vertex u
+            std::list::iterator it;
+            for (it = adj[u].begin(); it != adj[u].end(); ++it)
+            {
+                int v = *it;
+
+                // If pair of v is not considered so far i.e. (v, pair_v[v]) is not yet explored edge.
+                if (dist[pair_v[v]] == INF)
+                {
+                    dist[pair_v[v]] = dist[u] + 1; 
+                    q.push(pair_v[v]);    // Consider the pair and push it to queue
+                }
+            }
+        }
+    }
+
+   
+   
+    return (dist[NIL] != INF);   // If we could come back to NIL using alternating path of distinct vertices then there is an augmenting path available
+}
+
+/**
+ * @brief This functions checks whether an augmenting path is available exists beginning with free vertex u
+ * @param u represents position of vertex
+ * @returns `true` if there is an augmenting path beginning with free vertex u
+ * @returns `false` if there is no augmenting path beginning with free vertex u
+ */
+bool HKGraph::dfs(int u)
+{
+    if (u != NIL)
+    {
+        std::list::iterator it;
+        for (it = adj[u].begin(); it != adj[u].end(); ++it)
+        {
+            
+            int v = *it; // Adjacent vertex of u
+
+            // Follow the distances set by BFS search
+            if (dist[pair_v[v]] == dist[u] + 1)
+            {
+                // If dfs for pair of v also return true then new matching possible, store the matching
+                if (dfs(pair_v[v]) == true)
+                {   
+                    pair_v[v] = u;
+                    pair_u[u] = v;
+                    return true;
+                }
+            }
+        }
+
+        
+        dist[u] = INF; // If there is no augmenting path beginning with u then set distance to infinite.
+        return false;
+    }
+    return true;
+}
+
+/**
+ * @brief Default Constructor for initialization
+ */
+HKGraph::HKGraph() = default;
+
+/**
+ * @brief Constructor for initialization
+ * @param m is the number of vertices on left side of Bipartite Graph
+ * @param n is the number of vertices on right side of Bipartite Graph
+ */
+HKGraph::HKGraph(int m, int n) {
+    this->m = m;
+    this->n = n;
+    adj = std::vector >(m + 1);
+}
+
+/**
+ * @brief function to add edge from u to v
+ * @param u is the position of first vertex
+ * @param v is the position of second vertex
+ */
+void HKGraph::addEdge(int u, int v)
+{
+    adj[u].push_back(v); // Add v to u’s list.
+}
+
+} // namespace graph
+
+using graph::HKGraph;
+
+/**
+ * Self-test implementation
+ * @returns none
+ */
+void tests(){
+     // Sample test case 1
+	     int v1a = 3, v1b = 5, e1 = 2;  // vertices of left side, right side and edges
+	     HKGraph g1(v1a, v1b); // execute the algorithm 
+
+	     g1.addEdge(0,1);
+	     g1.addEdge(1,4);
+
+	     int expected_res1 = 0; // for the above sample data, this is the expected output
+	     int res1 = g1.hopcroftKarpAlgorithm();
+
+	     assert(res1 == expected_res1); // assert check to ensure that the algorithm executed correctly for test 1
+	
+     // Sample test case 2
+     	     int v2a = 4, v2b = 4, e2 = 6;  // vertices of left side, right side and edges
+	     HKGraph g2(v2a, v2b); // execute the algorithm 
+
+             g2.addEdge(1,1);
+	     g2.addEdge(1,3);
+	     g2.addEdge(2,3);
+	     g2.addEdge(3,4);
+	     g2.addEdge(4,3);
+             g2.addEdge(4,2);
+	
+	     int expected_res2 = 0; // for the above sample data, this is the expected output
+	     int res2 = g2.hopcroftKarpAlgorithm();
+
+	     assert(res2 == expected_res2); // assert check to ensure that the algorithm executed correctly for test 2
+	
+      // Sample test case 3
+     	     int v3a = 6, v3b = 6, e3 = 4;  // vertices of left side, right side and edges
+	     HKGraph g3(v3a, v3b); // execute the algorithm 
+
+             g3.addEdge(0,1);
+	     g3.addEdge(1,4);
+	     g3.addEdge(1,5);
+	     g3.addEdge(5,0);
+
+	     int expected_res3 = 0; // for the above sample data, this is the expected output
+	     int res3 = g3.hopcroftKarpAlgorithm();
+
+	     assert(res3 == expected_res3); // assert check to ensure that the algorithm executed correctly for test 3
+	
+	
+    	
+}
+
+/**
+ * @brief Main function
+ * @returns 0 on exit
+ */
+int main()
+{
+    tests();  // perform self-tests
+
+    int v1 = 0, v2 = 0, e = 0;
+    std::cin >> v1 >> v2 >> e; // vertices of left side, right side and edges
+    HKGraph g(v1, v2);  
+    int u = 0, v = 0;
+    for (int i = 0; i < e; ++i)
+    {
+        std::cin >> u >> v;
+        g.addEdge(u, v);
+    }
+  
+    int res = g.hopcroftKarpAlgorithm();
+    std::cout << "Maximum matching is " << res <<"\n";
+
+    return 0;
+
+}

From 95650899fe09ebd261c878d2a6a4952b75a6cd25 Mon Sep 17 00:00:00 2001
From: David Leal 
Date: Fri, 16 Oct 2020 08:07:20 -0500
Subject: [PATCH 86/90] [fix/docs]: Improve backtracking/rat_maze.cpp (#1084)

* [fix/docs]: Improve backtracking/rat_maze.cpp

* test: Added tests

* test: Move tests to a separate test function
---
 backtracking/rat_maze.cpp | 105 ++++++++++++++++++++++++++++----------
 1 file changed, 78 insertions(+), 27 deletions(-)

diff --git a/backtracking/rat_maze.cpp b/backtracking/rat_maze.cpp
index fb3be4451..6dfda965c 100644
--- a/backtracking/rat_maze.cpp
+++ b/backtracking/rat_maze.cpp
@@ -1,62 +1,113 @@
-/*
-    A Maze is given as N*N binary matrix of blocks where source block is the
-   upper left most block i.e., maze[0][0] and destination block is lower
-   rightmost block i.e., maze[N-1][N-1]. A rat starts from source and has to
-   reach destination. The rat can move only in two directions: forward and down.
-   In the maze matrix, 0 means the block is dead end and 1 means the block can
-   be used in the path from source to destination.
-*/
+/**
+ * @file
+ * @brief Implements [Rat in a
+ * Maze](https://www.codesdope.com/blog/article/backtracking-to-
+ * solve-a-rat-in-a-maze-c-java-pytho/) algorithm
+ *
+ * @details
+ * A Maze is given as N*N binary matrix of blocks where source block is the
+ * upper left most block i.e., maze[0][0] and destination block is lower
+ * rightmost block i.e., maze[N-1][N-1]. A rat starts from source and has to
+ * reach destination. The rat can move only in two directions: forward and down.
+ * In the maze matrix, 0 means the block is dead end and 1 means the block can
+ * be used in the path from source to destination.
+ *
+ * @author [Vaibhav Thakkar](https://github.com/vaithak)
+ * @author [David Leal](https://github.com/Panquesito7)
+ */
+
+#include 
 #include 
-#define size 4
+#include 
 
-using namespace std;
-
-int solveMaze(int currposrow, int currposcol, int maze[size][size],
-              int soln[size][size]) {
+/**
+ * @namespace backtracking
+ * @brief Backtracking algorithms
+ */
+namespace backtracking {
+/**
+ * @namespace rat_maze
+ * @brief Functions for [Rat in a
+ * Maze](https://www.codesdope.com/blog/article/backtracking-to-
+ * solve-a-rat-in-a-maze-c-java-pytho/) algorithm
+ */
+namespace rat_maze {
+/**
+ * @brief Solve rat maze problem
+ * @tparam size number of matrix size
+ * @param currposrow current position in rows
+ * @param currposcol current position in columns
+ * @param maze matrix where numbers are saved
+ * @param soln matrix to problem solution
+ * @returns 0 on end
+ */
+template 
+bool solveMaze(int currposrow, int currposcol,
+              const std::array, size> &maze,
+              std::array, size> soln) {
     if ((currposrow == size - 1) && (currposcol == size - 1)) {
         soln[currposrow][currposcol] = 1;
         for (int i = 0; i < size; ++i) {
             for (int j = 0; j < size; ++j) {
-                cout << soln[i][j];
+                std::cout << soln[i][j] << " ";
             }
-            cout << endl;
+            std::cout << std::endl;
         }
-        return 1;
+        return true;
     } else {
         soln[currposrow][currposcol] = 1;
 
-        // if there exist a solution by moving one step ahead in a collumn
+        // if there exist a solution by moving one step ahead in a column
         if ((currposcol < size - 1) && maze[currposrow][currposcol + 1] == 1 &&
             solveMaze(currposrow, currposcol + 1, maze, soln)) {
-            return 1;
+            return true;
         }
 
         // if there exists a solution by moving one step ahead in a row
         if ((currposrow < size - 1) && maze[currposrow + 1][currposcol] == 1 &&
             solveMaze(currposrow + 1, currposcol, maze, soln)) {
-            return 1;
+            return true;
         }
 
         // the backtracking part
         soln[currposrow][currposcol] = 0;
-        return 0;
+        return false;
     }
 }
+}  // namespace rat_maze
+}  // namespace backtracking
 
-int main(int argc, char const *argv[]) {
-    int maze[size][size] = {
-        {1, 0, 1, 0}, {1, 0, 1, 1}, {1, 0, 0, 1}, {1, 1, 1, 1}};
+/**
+ * @brief Test implementations
+ * @returns void
+ */
+static void test(){
+    const int size = 4;
+    std::array, size> maze = {
+        std::array{1, 0, 1, 0}, std::array{1, 0, 1, 1},
+        std::array{1, 0, 0, 1}, std::array{1, 1, 1, 1}};
 
-    int soln[size][size];
+    std::array, size> soln{};
 
+    // Backtracking: setup matrix solution to zero
     for (int i = 0; i < size; ++i) {
         for (int j = 0; j < size; ++j) {
             soln[i][j] = 0;
         }
     }
 
-    int currposrow = 0;
-    int currposcol = 0;
-    solveMaze(currposrow, currposcol, maze, soln);
+    int currposrow = 0;  // Current position in rows
+    int currposcol = 0;  // Current position in columns
+
+    assert(backtracking::rat_maze::solveMaze(currposrow, currposcol, maze,
+                                                   soln) == 1);
+}
+
+/**
+ * @brief Main function
+ * @returns 0 on exit
+ */
+int main() {
+    test(); // run the tests
     return 0;
 }

From a55e362ee182643a8983244b06179ed022574b9a Mon Sep 17 00:00:00 2001
From: Arpan Mukherjee 
Date: Fri, 16 Oct 2020 22:08:00 +0530
Subject: [PATCH 87/90] Added Sparse Table for range query (#996)

* Added Sparse Table for range query

* Fixed Lint Warnings

* Fixed comments and Lint warnings

* Fixed comments

* Fixed comments and Lint warnings

* Fixed comments and Lint warnings

* Fixed variable reference Lint warnings

* Added documentation for functions parameters and return statements

* updating DIRECTORY.md

* Added documentation

* Applied namespace range_queries

Co-authored-by: David Leal 

* Updated wikipedia documentation link

* Resolved comments

* Fixed typo

Co-authored-by: David Leal 

* Resolved comments

* Added tests

* Cleaned up debugging statements

* Resolved comments

* Resolved Comments

* Update sparse_table.cpp

* Resolved comments

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* Fixed comments

* Update sparse_table.cpp

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: David Leal 
Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>
---
 DIRECTORY.md                   |   1 +
 range_queries/sparse_table.cpp | 101 +++++++++++++++++++++++++++++++++
 2 files changed, 102 insertions(+)
 create mode 100755 range_queries/sparse_table.cpp

diff --git a/DIRECTORY.md b/DIRECTORY.md
index c431979aa..471980365 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -213,6 +213,7 @@
   * [Heavy Light Decomposition](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/heavy_light_decomposition.cpp)
   * [Mo](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/mo.cpp)
   * [Segtree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/segtree.cpp)
+  * [Sparse Table](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/sparse_table.cpp)
 
 ## Search
   * [Binary Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/binary_search.cpp)
diff --git a/range_queries/sparse_table.cpp b/range_queries/sparse_table.cpp
new file mode 100755
index 000000000..465532f3b
--- /dev/null
+++ b/range_queries/sparse_table.cpp
@@ -0,0 +1,101 @@
+/**
+ * @file sparse_table.cpp
+ * @brief Implementation of [Sparse Table](https://en.wikipedia.org/wiki/Range_minimum_query) data structure
+ *
+ * @details
+ * Sparse Table is a data structure, that allows answering range queries.
+ * It can answer most range queries in O(logn), but its true power is answering range minimum queries
+ * or equivalent range maximum queries). For those queries it can compute the answer in O(1) time.
+ *
+ * * Running Time Complexity \n
+ * * Build : O(NlogN) \n
+ * * Range Query : O(1) \n
+*/
+
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * @namespace range_queries
+ * @brief Range Queries algorithms
+ */
+namespace range_queries {
+/**
+ * @namespace sparse_table
+ * @brief Range queries using sparse-tables
+ */
+ namespace sparse_table {
+/**
+ * This function precomputes intial log table for further use.
+ * @param n value of the size of the input array
+ * @return corresponding vector of the log table
+ */
+template
+std::vector computeLogs(const std::vector& A) {
+    int n = A.size();
+    std::vector logs(n);
+    logs[1] = 0;
+    for (int i = 2 ; i < n ; i++) {
+        logs[i] = logs[i/2] + 1;
+    }
+    return logs;
+}
+
+/**
+ * This functions builds the primary data structure sparse table
+ * @param n value of the size of the input array
+ * @param A array of the input integers
+ * @param logs array of the log table
+ * @return created sparse table data structure
+ */
+template
+std::vector > buildTable(const std::vector& A, const std::vector& logs) {
+    int n = A.size();
+    std::vector > table(20, std::vector(n+5, 0));
+    int curLen = 0;
+    for (int i = 0 ; i <= logs[n] ; i++) {
+        curLen = 1 << i;
+        for (int j = 0 ; j + curLen < n ; j++) {
+            if (curLen == 1) {
+                table[i][j] = A[j];
+            }
+            else {
+                table[i][j] = std::min(table[i-1][j], table[i-1][j + curLen/2]);
+            }
+        }
+    }
+    return table;
+}
+
+/**
+ * This function is the query function to get the range minimum value
+ * @param beg beginning index of the query range
+ * @param end ending index of the query range
+ * @param logs array of the log table
+ * @param table sparse table data structure for the input array
+ * @return minimum value for the [beg, end] range for the input array
+ */
+template
+int getMinimum(int beg, int end, const std::vector& logs, const std::vector >& table) {
+    int p = logs[end - beg + 1];
+    int pLen = 1 << p;
+    return std::min(table[p][beg], table[p][end - pLen + 1]);
+}
+}
+} // namespace range_queries
+
+/**
+ * Main function
+ */
+int main() {
+    std::vector A{1, 2, 0, 3, 9};
+    std::vector logs = range_queries::sparse_table::computeLogs(A);
+    std::vector >  table = range_queries::sparse_table::buildTable(A, logs);
+    assert(range_queries::sparse_table::getMinimum(0, 0, logs, table) == 1);
+    assert(range_queries::sparse_table::getMinimum(0, 4, logs, table) == 0);
+    assert(range_queries::sparse_table::getMinimum(2, 4, logs, table) == 0);
+    return 0;
+}
+

From 34a6faf401ffaff920bf8a9c89c665eb63095697 Mon Sep 17 00:00:00 2001
From: Akshay Anand 
Date: Sat, 17 Oct 2020 01:24:42 +0530
Subject: [PATCH 88/90] adding word break DP approach (#1278)

* adding word break DP approach

* fixing formatting

* fixing linting issues

* adding documentation and other enhancements

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* updating DIRECTORY.md

* clang-format and clang-tidy fixes for 061c21ae

* docs: fixed documentation

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* Update dynamic_programming/word_break.cpp

Co-authored-by: David Leal 

* clang-format and clang-tidy fixes for d8ab6b02

* Update dynamic_programming/word_break.cpp

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>

* clang-format and clang-tidy fixes for 05d7ca14

Co-authored-by: David Leal 
Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>
---
 DIRECTORY.md                       |   1 +
 dynamic_programming/word_break.cpp | 186 +++++++++++++++++++++++++++++
 2 files changed, 187 insertions(+)
 create mode 100644 dynamic_programming/word_break.cpp

diff --git a/DIRECTORY.md b/DIRECTORY.md
index 471980365..afe01d21a 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -69,6 +69,7 @@
   * [Searching Of Element In Dynamic Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/searching_of_element_in_dynamic_array.cpp)
   * [Shortest Common Supersequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/shortest_common_supersequence.cpp)
   * [Tree Height](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/tree_height.cpp)
+  * [Word Break](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/word_break.cpp)
 
 ## Geometry
   * [Jarvis Algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/jarvis_algorithm.cpp)
diff --git a/dynamic_programming/word_break.cpp b/dynamic_programming/word_break.cpp
new file mode 100644
index 000000000..5291f665f
--- /dev/null
+++ b/dynamic_programming/word_break.cpp
@@ -0,0 +1,186 @@
+/**
+ * @file
+ * @brief [Word Break Problem](https://leetcode.com/problems/word-break/)
+ * @details
+ * Given a non-empty string s and a dictionary wordDict containing a list of
+ * non-empty words, determine if s can be segmented into a space-separated
+ * sequence of one or more dictionary words.
+ *
+ * Note:
+ * The same word in the dictionary may be reused multiple times in the
+ * segmentation. You may assume the dictionary does not contain duplicate words.
+ *
+ * Example 1:
+ * Input: s = "leetcode", wordDict = ["leet", "code"]
+ * Output: true
+ * Explanation: Return true because "leetcode" can be segmented as "leet code".
+ *
+ * Example 2:
+ * Input: s = "applepenapple", wordDict = ["apple", "pen"]
+ * Output: true
+ * Explanation: Return true because "applepenapple" can be segmented as "apple
+ * pen apple". Note that you are allowed to reuse a dictionary word.
+ *
+ * Example 3:
+ * Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
+ * Output: false
+ *
+ * @author [Akshay Anand] (https://github.com/axayjha)
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * @namespace dynamic_programming
+ * @brief Dynamic programming algorithms
+ */
+namespace dynamic_programming {
+
+/**
+ * @namespace word_break
+ * @brief Functions for [Word Break](https://leetcode.com/problems/word-break/)
+ * problem
+ */
+namespace word_break {
+
+/**
+ * @brief Function that checks if the string passed in param is present in
+ * the the unordered_set passed
+ *
+ * @param str the string to be searched
+ * @param strSet unordered set of string, that is to be looked into
+ * @returns `true` if str is present in strSet
+ * @returns `false` if str is not present in strSet
+ */
+bool exists(const std::string &str,
+            const std::unordered_set &strSet) {
+    return strSet.find(str) != strSet.end();
+}
+
+/**
+ * @brief Function that checks if the string passed in param can be
+ * segmented from position 'pos', and then correctly go on to segment the
+ * rest of the string correctly as well to reach a solution
+ *
+ * @param s the complete string to be segmented
+ * @param strSet unordered set of string, that is to be used as the
+ * reference dictionary
+ * @param pos the index value at which we will segment string and test
+ * further if it is correctly segmented at pos
+ * @param dp the vector to memoize solution for each position
+ * @returns `true` if a valid solution/segmentation is possible by segmenting at
+ * index pos
+ * @returns `false` otherwise
+ */
+bool check(const std::string &s, const std::unordered_set &strSet,
+           int pos, std::vector *dp) {
+    if (pos == s.length()) {
+        // if we have reached till the end of the string, means we have
+        // segmented throughout correctly hence we have a solution, thus
+        // returning true
+        return true;
+    }
+
+    if (dp->at(pos) != INT_MAX) {
+        // if dp[pos] is not INT_MAX, means we must have saved a solution
+        // for the position pos; then return if the solution at pos is true
+        // or not
+        return dp->at(pos) == 1;
+    }
+
+    std::string wordTillNow =
+        "";  // string to save the prefixes of word till different positons
+
+    for (int i = pos; i < s.length(); i++) {
+        // Loop starting from pos to end, to check valid set of
+        // segmentations if any
+        wordTillNow +=
+            std::string(1, s[i]);  // storing the prefix till the position i
+
+        // if the prefix till current position is present in the dictionary
+        // and the remaining substring can also be segmented legally, then
+        // set solution at position pos in the memo, and return true
+        if (exists(wordTillNow, strSet) and check(s, strSet, i + 1, dp)) {
+            dp->at(pos) = 1;
+            return true;
+        }
+    }
+    // if function has still not returned, then there must be no legal
+    // segmentation possible after segmenting at pos
+    dp->at(pos) = 0;  // so set solution at pos as false
+    return false;     // and return no solution at position pos
+}
+
+/**
+ * @brief Function that checks if the string passed in param can be
+ * segmented into the strings present in the vector.
+ * In others words, it checks if any permutation of strings in
+ * the vector can be concatenated to form the final string.
+ *
+ * @param s the complete string to be segmented
+ * @param wordDict a vector of words to be used as dictionary to look into
+ * @returns `true` if s can be formed by a combination of strings present in
+ * wordDict
+ * @return `false` otherwise
+ */
+bool wordBreak(const std::string &s, const std::vector &wordDict) {
+    // unordered set to store words in the dictionary for constant time
+    // search
+    std::unordered_set strSet;
+    for (const auto &s : wordDict) {
+        strSet.insert(s);
+    }
+    // a vector to be used for memoization, whose value at index i will
+    // tell if the string s can be segmented (correctly) at position i.
+    // initializing it with INT_MAX (which will denote no solution)
+    std::vector dp(s.length(), INT_MAX);
+
+    // calling check method with position = 0, to check from left
+    // from where can be start segmenting the complete string in correct
+    // manner
+    return check(s, strSet, 0, &dp);
+}
+
+}  // namespace word_break
+}  // namespace dynamic_programming
+
+/**
+ * @brief Test implementations
+ * @returns void
+ */
+static void test() {
+    // the complete string
+    const std::string s = "applepenapple";
+    // the dictionary to be used
+    const std::vector wordDict = {"apple", "pen"};
+
+    assert(dynamic_programming::word_break::wordBreak(s, wordDict));
+
+    // should return true, as applepenapple can be segmented as apple + pen +
+    // apple
+    std::cout << dynamic_programming::word_break::wordBreak(s, wordDict)
+              << std::endl;
+    std::cout << "Test implementation passed!\n";
+}
+/**
+ * @brief Main function
+ * @returns 0 on exit
+ */
+int main() {
+    test();  // call the test function :)
+
+    // the complete string
+    const std::string s = "applepenapple";
+    // the dictionary to be used
+    const std::vector wordDict = {"apple", "pen"};
+
+    // should return true, as applepenapple can be segmented as apple + pen +
+    // apple
+    std::cout << dynamic_programming::word_break::wordBreak(s, wordDict)
+              << std::endl;
+}

From 5731bcd6fcbc39faf5c82200ebcaace991cba2e0 Mon Sep 17 00:00:00 2001
From: divyansh12323 <53469850+divyansh12323@users.noreply.github.com>
Date: Sat, 17 Oct 2020 01:36:21 +0530
Subject: [PATCH 89/90] feat: Add Pancake Sort algorithm (#1189)

* Create pancake_sort.cpp

* Update pancake_sort.cpp

* Update pancake_sort.cpp

* Update pancake_sort.cpp

* Update pancake_sort.cpp

* Update pancake_sort.cpp

* Update pancake_sort.cpp

* Update pancake_sort.cpp

* Update pancake_sort.cpp

* updating DIRECTORY.md

* Update pancake_sort.cpp

* Update pancake_sort.cpp

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
---
 DIRECTORY.md             |   1 +
 sorting/pancake_sort.cpp | 129 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 130 insertions(+)
 create mode 100644 sorting/pancake_sort.cpp

diff --git a/DIRECTORY.md b/DIRECTORY.md
index afe01d21a..1aaf9dade 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -248,6 +248,7 @@
   * [Non Recursive Merge Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/non_recursive_merge_sort.cpp)
   * [Numeric String Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/numeric_string_sort.cpp)
   * [Odd Even Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/odd_even_sort.cpp)
+  * [Pancake Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/pancake_sort.cpp)
   * [Pigeonhole Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/pigeonhole_sort.cpp)
   * [Quick Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/quick_sort.cpp)
   * [Quick Sort 3](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/quick_sort_3.cpp)
diff --git a/sorting/pancake_sort.cpp b/sorting/pancake_sort.cpp
new file mode 100644
index 000000000..accc5dbdb
--- /dev/null
+++ b/sorting/pancake_sort.cpp
@@ -0,0 +1,129 @@
+/**
+ * @file
+ * @brief pancake sort sorts a disordered stack of pancakes by flipping any number of pancakes using a spatula using minimum number of flips.
+ *
+ * @details
+ * Unlike a traditional sorting algorithm, which attempts to sort with the fewest comparisons possible,
+ * the goal is to sort the sequence in as few reversals as possible.
+ * Overall time complexity of pancake sort is O(n^2)
+ * For example: example 1:-
+ * Disordered pancake sizes: {2,5,3,7,8}
+ * Sorted: {2,3,5,7,8}
+ * For example: example 2:-
+ * Disordered pancake sizes: {22,51,37,73,81}
+ * Sorted: {22,37,51,73,81}
+ * @author [Divyansh Gupta](https://github.com/divyansh12323)
+ * @see more on [Pancake sort](https://en.wikipedia.org/wiki/Pancake_sorting)
+ * @see related problem at [Leetcode](https://leetcode.com/problems/pancake-sorting/)
+*/
+
+#include    // for io operations
+#include      // for std::vector
+#include   // for std::is_sorted
+#include     // for std::assert
+
+/**
+ * @namespace sorting
+ * @brief Sorting algorithms
+ */
+namespace sorting {
+/**
+ * @namespace pancake_sort
+ * @brief Functions for [Pancake sort](https://en.wikipedia.org/wiki/Pancake_sorting) algorithm
+ */
+namespace pancake_sort {
+    /**
+    * @brief This implementation is for reversing elements in a a C-style array .
+    * @param [start,end] arr our vector of elements.
+    * @param start starting index of array
+    * @param end ending index of array
+    * @returns void
+    */
+    template
+    void reverse(std::vector &arr, int start, int end) {
+        T temp; //Temporary variable
+        while (start <= end) {
+            temp = arr[start];
+            arr[start] = arr[end];
+            arr[end] = temp;
+            start++;
+            end--;
+        }
+    }
+    /**
+    * @brief This implementation is for a C-style array input that gets modified in place.
+    * @param [start,end] arr our vector of elements.
+    * @param size size of given array
+    * @returns 0 on exit
+    */
+    template
+    int pancakeSort(std::vector &arr, int size) {
+        for (int i = size; i > 1; --i) {
+            int max_index = 0, j; //intialize some variables.
+            T max_value = 0;
+            for (j = 0; j < i; j++) {
+                if (arr[j] >= max_value) {
+                    max_value = arr[j];
+                    max_index = j;
+                }
+            }
+            if (max_index != i - 1) //check for reversing
+            {
+                reverse(arr, 0, max_index);
+                reverse(arr, 0, i - 1);
+            }
+        }
+        return 0;
+    }
+}  // namespace pancake_sort
+}  // namespace sorting
+
+/**
+ * @brief Test implementations
+ * @returns void
+ */
+static void test() {
+    // example 1: vector of int
+    const int size1 = 7;
+    std::cout << "\nTest 1- as std::vector...";
+    std::vector arr1 = {23, 10, 20, 11, 12, 6, 7};
+    sorting::pancake_sort::pancakeSort(arr1, size1);
+    assert(std::is_sorted(arr1.begin(), arr1.end()));
+    std::cout << "Passed\n";
+    for (int i = 0; i < size1; i++) {
+        std::cout << arr1[i] << " ,";
+    }
+    std::cout << std::endl;
+
+    // example 2: vector of double
+    const int size2 = 8;
+    std::cout << "\nTest 2- as std::vector...";
+    std::vector arr2 = {23.56, 10.62, 200.78, 111.484, 3.9, 1.2, 61.77, 79.6};
+    sorting::pancake_sort::pancakeSort(arr2, size2);
+    assert(std::is_sorted(arr2.begin(), arr2.end()));
+    std::cout << "Passed\n";
+    for (int i = 0; i < size2; i++) {
+        std::cout << arr2[i] << ", ";
+    }
+    std::cout << std::endl;
+
+    // example 3:vector of float
+    const int size3 = 7;
+    std::cout << "\nTest 3- as std::vector...";
+    std::vector arr3 = {6.56, 12.62, 200.78, 768.484, 19.27, 68.87, 9.6};
+    sorting::pancake_sort::pancakeSort(arr3, size3);
+    assert(std::is_sorted(arr3.begin(), arr3.end()));
+    std::cout << "Passed\n";
+    for (int i = 0; i < size3; i++) {
+        std::cout << arr3[i] << ", ";
+    }
+    std::cout << std::endl;
+}
+/**
+ * @brief Main function
+ * @returns 0 on exit
+ */
+int main() {
+    test();
+    return 0;
+}

From 79b98cc90521ad83227e17bd581bc2a0aedc6f3d Mon Sep 17 00:00:00 2001
From: Krishna Vedala <7001608+kvedala@users.noreply.github.com>
Date: Fri, 16 Oct 2020 16:18:38 -0400
Subject: [PATCH 90/90] feat: guidelines for reviewers (#1302)

* Create REVIEWER_CODE.md

* added heading
---
 REVIEWER_CODE.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 REVIEWER_CODE.md

diff --git a/REVIEWER_CODE.md b/REVIEWER_CODE.md
new file mode 100644
index 000000000..933d43e68
--- /dev/null
+++ b/REVIEWER_CODE.md
@@ -0,0 +1,13 @@
+# Guidelines for reviewers and maintainers
+
+Following are some guidelines for contributors who are providing reviews to the pull-requests.
+
+1. On any given pull-request, there only one reviewer should be active at a time. Once the reviewer is done, others may add short comments or any further reviews as needed. Again, one at a time.
+2. Assigning reviewers should be avoided unless the pull-request is for a particular task the reviewer is more proficient in.
+3. Any contributor who has had their code merged into the repo can provide with reviews as they have gone through the repo standards at least once before. The reviewer will be on a first-come-first serve basis.
+4. Most repositories have a check-list in the description for pull-requests. Many times, the contributors are not following them and simply remove the checklist or checkthem without taking the time to review the checklist items. These contributors are almost always copying the code from somewhere. These should be pointed out politely and reviews should be blocked until the contributor updates the basic code structure per the checklist and the repo standards.
+5. The reviewers should label every pull-request appropriately - including "invalid" as the case may be.
+6. Some pull-requests have existing duplicate code or duplicate pull-requests or sometimes, a novice might create a new pull-request for every new commit. This is a daunting task but one of the responsibility of a reviewer.
+7. Discourage creating branches on the repo but rather fork the repo to the respective userspace and contribute from that fork.
+8. Some repos - C & C++ - have collaboration with GitPod wherein the code and the contribution can be executed and tested online with relative simplicity. It also contains tools necessary to perform debug and CI checks without installing any tools. Encourage contributors to utilize the feature. Reviewers can test the contributed algorithms online without worrying about forks and branches.
+9. There should not be any hurry to merge pull-requests. Since the repos are educational, better to get the contributions right even if it takes a bit longer to review. Encourage patience and develop debugging skills of contributors.