From 7adb0d8d5e534324f9f713b3f0dd19823b6ed768 Mon Sep 17 00:00:00 2001 From: Ayaan Khan Date: Thu, 16 Jul 2020 21:04:29 +0530 Subject: [PATCH 01/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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 c6c5d81f1be33d8a009c77702655b3df3fcc0413 Mon Sep 17 00:00:00 2001 From: rishabh-997 Date: Sat, 15 Aug 2020 01:39:18 +0530 Subject: [PATCH 28/61] Added Jarvis Algorithm to compute convex hull Fixed code formatting Fixed code formatting Fixed code formatting Added Jarvis algorithm to compute convex hull Capitalized class name Added requested changes Added requested changes Added jarvis algorithm Added jarvis algorithm to compute convex hull --- geometry/jarvis_algorithm.cpp | 178 ++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 geometry/jarvis_algorithm.cpp diff --git a/geometry/jarvis_algorithm.cpp b/geometry/jarvis_algorithm.cpp new file mode 100644 index 000000000..84f7de4bc --- /dev/null +++ b/geometry/jarvis_algorithm.cpp @@ -0,0 +1,178 @@ +/** + * @file + * @brief Implementation of [Jarvis’s Algorithm](https://en.wikipedia.org/wiki/Gift_wrapping_algorithm) algorithm. + * + * @details + * Given a set of points in the plane. the convex hull of the set + * is the smallest convex polygon that contains all the points of it. + * + * ### Algorithm + * The idea of Jarvis’s Algorithm is simple, we start from the leftmost point + * (or point with minimum x coordinate value) and we + * keep wrapping points in counterclockwise direction. + * + * The idea is to use orientation() here. Next point is selected as the + * point that beats all other points at counterclockwise orientation, i.e., + * next point is q if for any other point r, + * we have “orientation(p, q, r) = counterclockwise”. + * + * For Example, + * If points = {{0, 3}, {2, 2}, {1, 1}, {2, 1}, + {3, 0}, {0, 0}, {3, 3}}; + * + * then the convex hull is + * (0, 3), (0, 0), (3, 0), (3, 3) + * + * @author [Rishabh Agarwal](https://github.com/rishabh-997) + */ + +#include +#include +#include +#include + +/** + * @namespace geometry + * @brief Geometry algorithms + */ +namespace geometry { + /** + * Structure defining the x and y co-ordinates of the given + * point in space + */ + struct Point { + int x, y; + }; + + /** + * @namespace jarvis + * @brief Functions for [Jarvis’s Algorithm](https://en.wikipedia.org/wiki/Gift_wrapping_algorithm) algorithm + */ + namespace jarvis { + /** + * Class which can be called from main and is globally available + * throughout the code + */ + class Convexhull { + public: + std::vector points; + int size; + + /** + * Constructor of given class + * + * @param pointList list of all points in the space + * @param n number of points in space + */ + Convexhull(std::vector pointList, int n) { + points = std::move(pointList); + size = n; + } + + /** + * Creates convex hull of a set of n points. + * There must be 3 points at least for the convex hull to exist + * + * returns an vector array containing points in space + * which enclose all given points thus forming a hull + */ + std::vector getConvexHull() const { + // Initialize Result + std::vector hull; + + // Find the leftmost point + int leftmost_point = 0; + for (int i = 1; i < size; i++) { + if (points[i].x < points[leftmost_point].x) { + leftmost_point = i; + } + } + // Start from leftmost point, keep moving counterclockwise + // until reach the start point again. This loop runs O(h) + // times where h is number of points in result or output. + int p = leftmost_point, q = 0; + do { + // Add current point to result + hull.push_back(points[p]); + + // Search for a point 'q' such that orientation(p, x, q) + // is counterclockwise for all points 'x'. The idea + // is to keep track of last visited most counter clock- + // wise point in q. If any point 'i' is more counter clock- + // wise than q, then update q. + q = (p + 1) % size; + for (int i = 0; i < size; i++) { + // If i is more counterclockwise than current q, then + // update q + if (orientation(points[p], points[i], points[q]) == 2) { + q = i; + } + } + + // Now q is the most counterclockwise with respect to p + // Set p as q for next iteration, so that q is added to + // result 'hull' + p = q; + + } while (p != leftmost_point); // While we don't come to first point + + return hull; + } + + /** + * + * @param p first point selected + * @param q adjacent point for q + * @param r adjacent point for q + * @returns the geometric orientation for the three points + * 0 -> Linear + * 1 -> Clock Wise + * 2 -> Anti Clock Wise + */ + static int orientation(Point p, Point q, Point r) { + int val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); + + if (val == 0) { + return 0; + } + return (val > 0) ? 1 : 2; + } + + }; + + } // namespace jarvis +} // namespace geometry + +/** + * Test function + */ +static void test() { + std::vector points = {{0, 3}, + {2, 2}, + {1, 1}, + {2, 1}, + {3, 0}, + {0, 0}, + {3, 3} + }; + int n = points.size(); + geometry::jarvis::Convexhull hull(points, n); + std::vector actualPoint; + actualPoint = hull.getConvexHull(); + + std::vector expectedPoint = {{0, 3}, + {0, 0}, + {3, 0}, + {3, 3}}; + for (int i = 0; i < expectedPoint.size(); i++) { + assert(actualPoint[i].x == expectedPoint[i].x); + assert(actualPoint[i].y == expectedPoint[i].y); + } + std::cout << "Test implementations passed!\n"; +} + +/** Driver Code */ +int main() { + test(); + return 0; +} From e59559af0f97f0f2996a71a13b1872a8c874a9b8 Mon Sep 17 00:00:00 2001 From: rishabh-997 Date: Sat, 15 Aug 2020 13:05:28 +0530 Subject: [PATCH 29/61] Added jarvis algorithm --- geometry/jarvis_algorithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geometry/jarvis_algorithm.cpp b/geometry/jarvis_algorithm.cpp index 84f7de4bc..9a1b4ea8d 100644 --- a/geometry/jarvis_algorithm.cpp +++ b/geometry/jarvis_algorithm.cpp @@ -73,7 +73,7 @@ namespace geometry { * Creates convex hull of a set of n points. * There must be 3 points at least for the convex hull to exist * - * returns an vector array containing points in space + * @returns an vector array containing points in space * which enclose all given points thus forming a hull */ std::vector getConvexHull() const { From 686ceb3cfad6192ddfa9d174113d68afcb818ab9 Mon Sep 17 00:00:00 2001 From: Rishabh Agarwal <45125121+rishabh-997@users.noreply.github.com> Date: Sun, 16 Aug 2020 22:12:37 +0530 Subject: [PATCH 30/61] Update geometry/jarvis_algorithm.cpp Co-authored-by: David Leal --- geometry/jarvis_algorithm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/geometry/jarvis_algorithm.cpp b/geometry/jarvis_algorithm.cpp index 9a1b4ea8d..c0e906886 100644 --- a/geometry/jarvis_algorithm.cpp +++ b/geometry/jarvis_algorithm.cpp @@ -145,6 +145,7 @@ namespace geometry { /** * Test function + * @returns void */ static void test() { std::vector points = {{0, 3}, From ff4792d369ce453ebcb97b1b68ad1c39a4b8301b Mon Sep 17 00:00:00 2001 From: Rishabh Agarwal <45125121+rishabh-997@users.noreply.github.com> Date: Sun, 16 Aug 2020 22:12:56 +0530 Subject: [PATCH 31/61] Update geometry/jarvis_algorithm.cpp Co-authored-by: David Leal --- geometry/jarvis_algorithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geometry/jarvis_algorithm.cpp b/geometry/jarvis_algorithm.cpp index c0e906886..ff31207a3 100644 --- a/geometry/jarvis_algorithm.cpp +++ b/geometry/jarvis_algorithm.cpp @@ -46,7 +46,7 @@ namespace geometry { /** * @namespace jarvis - * @brief Functions for [Jarvis’s Algorithm](https://en.wikipedia.org/wiki/Gift_wrapping_algorithm) algorithm + * @brief Functions for [Jarvis’s](https://en.wikipedia.org/wiki/Gift_wrapping_algorithm) algorithm */ namespace jarvis { /** From bfed4c8fba4900a7545576858297476b5268ac81 Mon Sep 17 00:00:00 2001 From: Rishabh Agarwal <45125121+rishabh-997@users.noreply.github.com> Date: Sun, 16 Aug 2020 22:26:13 +0530 Subject: [PATCH 32/61] Update geometry/jarvis_algorithm.cpp Co-authored-by: David Leal --- geometry/jarvis_algorithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geometry/jarvis_algorithm.cpp b/geometry/jarvis_algorithm.cpp index ff31207a3..85716e2fd 100644 --- a/geometry/jarvis_algorithm.cpp +++ b/geometry/jarvis_algorithm.cpp @@ -1,6 +1,6 @@ /** * @file - * @brief Implementation of [Jarvis’s Algorithm](https://en.wikipedia.org/wiki/Gift_wrapping_algorithm) algorithm. + * @brief Implementation of [Jarvis’s](https://en.wikipedia.org/wiki/Gift_wrapping_algorithm) algorithm. * * @details * Given a set of points in the plane. the convex hull of the set 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 33/61] 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 34/61] 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 d21f56e8363c10e956bf015bd33e78a408861217 Mon Sep 17 00:00:00 2001 From: rishabh-997 Date: Mon, 17 Aug 2020 02:36:30 +0530 Subject: [PATCH 35/61] Added Jarvi algo to find convex hull --- geometry/jarvis_algorithm.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/geometry/jarvis_algorithm.cpp b/geometry/jarvis_algorithm.cpp index 85716e2fd..d471a52a4 100644 --- a/geometry/jarvis_algorithm.cpp +++ b/geometry/jarvis_algorithm.cpp @@ -36,19 +36,19 @@ * @brief Geometry algorithms */ namespace geometry { - /** - * Structure defining the x and y co-ordinates of the given - * point in space - */ - struct Point { - int x, y; - }; - /** * @namespace jarvis * @brief Functions for [Jarvis’s](https://en.wikipedia.org/wiki/Gift_wrapping_algorithm) algorithm */ namespace jarvis { + /** + * Structure defining the x and y co-ordinates of the given + * point in space + */ + struct Point { + int x, y; + }; + /** * Class which can be called from main and is globally available * throughout the code @@ -148,7 +148,7 @@ namespace geometry { * @returns void */ static void test() { - std::vector points = {{0, 3}, + std::vector points = {{0, 3}, {2, 2}, {1, 1}, {2, 1}, @@ -158,10 +158,10 @@ static void test() { }; int n = points.size(); geometry::jarvis::Convexhull hull(points, n); - std::vector actualPoint; + std::vector actualPoint; actualPoint = hull.getConvexHull(); - std::vector expectedPoint = {{0, 3}, + std::vector expectedPoint = {{0, 3}, {0, 0}, {3, 0}, {3, 3}}; From bf610fb91dad7f62aeee7c647ddf43dcbc0fe92e Mon Sep 17 00:00:00 2001 From: rishabh-997 Date: Mon, 17 Aug 2020 02:46:38 +0530 Subject: [PATCH 36/61] Added Jarvi algo to find convex hull --- geometry/jarvis_algorithm.cpp | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/geometry/jarvis_algorithm.cpp b/geometry/jarvis_algorithm.cpp index d471a52a4..ae5b1b5a6 100644 --- a/geometry/jarvis_algorithm.cpp +++ b/geometry/jarvis_algorithm.cpp @@ -26,7 +26,6 @@ * @author [Rishabh Agarwal](https://github.com/rishabh-997) */ -#include #include #include #include @@ -54,19 +53,19 @@ namespace geometry { * throughout the code */ class Convexhull { - public: std::vector points; int size; + public: /** * Constructor of given class * * @param pointList list of all points in the space * @param n number of points in space */ - Convexhull(std::vector pointList, int n) { - points = std::move(pointList); - size = n; + explicit Convexhull(const std::vector &pointList) { + points = pointList; + size = points.size(); } /** @@ -120,16 +119,18 @@ namespace geometry { } /** - * + * This function returns the geometric orientation for the three points + * in a space, ie, whether they are linear ir clockwise or + * anti-clockwise * @param p first point selected * @param q adjacent point for q * @param r adjacent point for q - * @returns the geometric orientation for the three points - * 0 -> Linear - * 1 -> Clock Wise - * 2 -> Anti Clock Wise + * + * @returns 0 -> Linear + * @returns 1 -> Clock Wise + * @returns 2 -> Anti Clock Wise */ - static int orientation(Point p, Point q, Point r) { + static int orientation(const Point &p, const Point &q, const Point &r) { int val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); if (val == 0) { @@ -149,22 +150,21 @@ namespace geometry { */ static void test() { std::vector points = {{0, 3}, - {2, 2}, - {1, 1}, - {2, 1}, - {3, 0}, - {0, 0}, - {3, 3} + {2, 2}, + {1, 1}, + {2, 1}, + {3, 0}, + {0, 0}, + {3, 3} }; - int n = points.size(); - geometry::jarvis::Convexhull hull(points, n); + geometry::jarvis::Convexhull hull(points); std::vector actualPoint; actualPoint = hull.getConvexHull(); std::vector expectedPoint = {{0, 3}, - {0, 0}, - {3, 0}, - {3, 3}}; + {0, 0}, + {3, 0}, + {3, 3}}; for (int i = 0; i < expectedPoint.size(); i++) { assert(actualPoint[i].x == expectedPoint[i].x); assert(actualPoint[i].y == expectedPoint[i].y); From 91ce393c4560302d2d38fba380e3edb185659a3b Mon Sep 17 00:00:00 2001 From: Filip Hlasek Date: Sun, 16 Aug 2020 21:23:29 -0700 Subject: [PATCH 37/61] fix: linter for kruskal (#1036) * fix: linter for kruskal * using instead of typedef. --- graph/kruskal.cpp | 113 +++++++++++++--------------------------------- 1 file changed, 31 insertions(+), 82 deletions(-) diff --git a/graph/kruskal.cpp b/graph/kruskal.cpp index b7b830668..e179131a1 100644 --- a/graph/kruskal.cpp +++ b/graph/kruskal.cpp @@ -1,73 +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; +using ll = int64_t; + +std::array parent; +ll node, edge; +std::vector>> edges; 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]]; @@ -75,41 +23,42 @@ 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 = 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); } } 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; + edges.emplace_back(make_pair(cost, std::make_pair(from, to))); totalcost += cost; } - sort(v.begin(), v.end()); - // rep(i,v.size()) - // cout< Date: Sun, 16 Aug 2020 21:43:33 -0700 Subject: [PATCH 38/61] fix: linter for kosaraju (#1035) * fix: linter for kosaraju * update doxygen parameters --- graph/kosaraju.cpp | 87 +++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/graph/kosaraju.cpp b/graph/kosaraju.cpp index 00c9d7ca0..5949c0bb8 100644 --- a/graph/kosaraju.cpp +++ b/graph/kosaraju.cpp @@ -4,77 +4,84 @@ #include #include +#include -using namespace std; /** * Iterative function/method to print graph: - * @param a[] : array of vectors (2D) - * @param V : vertices + * @param a adjacency list representation of the graph + * @param V number of vertices * @return void **/ -void print(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()) - cout << "i=" << i << "-->"; - for (int j = 0; j < a[i].size(); j++) cout << a[i][j] << " "; - if (!a[i].empty()) - cout << endl; + if (!a[i].empty()) { + std::cout << "i=" << i << "-->"; + } + for (int j : a[i]) { + std::cout << j << " "; + } + if (!a[i].empty()) { + std::cout << std::endl; + } } } /** * //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 adj[] : array of vectors to represent graph + * @param v vertices + * @param st stack passed by reference + * @param vis array to keep track of visited nodes (boolean type) + * @param adj adjacency list representation of the graph * @return void **/ -void push_vertex(int v, stack &st, bool vis[], 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 grev[] : graph with reversed edges + * @param v vertices + * @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[], 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, vector adj[]) { - bool vis[V] = {}; - stack st; +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[]: - 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, vector adj[]) { int t = st.top(); st.pop(); if (vis[t] == false) { - dfs(t, vis, grev); + dfs(t, &vis, grev); count_scc++; } } @@ -101,21 +108,21 @@ int kosaraju(int V, vector adj[]) { // All critical/corner cases have been taken care of. // Input your required values: (not hardcoded) int main() { - int t; - cin >> t; + int t = 0; + std::cin >> t; while (t--) { - int a, b; // a->number of nodes, b->directed edges. - cin >> a >> b; - int m, n; - vector adj[a + 1]; + 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); 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; } 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 39/61] 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 3741b41a8a68c0ddc71a40f37e51a5df088a565d Mon Sep 17 00:00:00 2001 From: ridhishjain Date: Mon, 17 Aug 2020 19:08:14 +0530 Subject: [PATCH 40/61] feat: add shortest common super-sequence algorithm (#1030) * main cpp file for shortest common supersequence * fixing reverse function bug * converting c style array to std::vector * removing namespace directives * adding documentation and test cases * namespace declarations * update shortest_common_supersequence.cpp * update shortest_common_supersequence.cpp * Update dynamic_programming/shortest_common_supersequence.cpp Co-authored-by: David Leal * Update dynamic_programming/shortest_common_supersequence.cpp Co-authored-by: David Leal * Update dynamic_programming/shortest_common_supersequence.cpp Co-authored-by: David Leal * update shortest_common_supersequence.cpp * update shortest_common_supersequence.cpp * update shortest_common_supersequence.cpp * Update dynamic_programming/shortest_common_supersequence.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * 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 + .../shortest_common_supersequence.cpp | 179 ++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 dynamic_programming/shortest_common_supersequence.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 76fa1154d..0349f7039 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -67,6 +67,7 @@ * [Longest Increasing Subsequence (Nlogn)](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_increasing_subsequence_(nlogn).cpp) * [Matrix Chain Multiplication](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/matrix_chain_multiplication.cpp) * [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) ## Geometry diff --git a/dynamic_programming/shortest_common_supersequence.cpp b/dynamic_programming/shortest_common_supersequence.cpp new file mode 100644 index 000000000..3064359fb --- /dev/null +++ b/dynamic_programming/shortest_common_supersequence.cpp @@ -0,0 +1,179 @@ +/** + * @file + * @brief SCS is a string Z which is the shortest supersequence of strings X and Y (may not be continuous in Z, but order is maintained). + * + * @details + * The idea is to use lookup table method as used in LCS. + * For example: example 1:- + * X: 'ABCXYZ', Y: 'ABZ' then Z will be 'ABCXYZ' (y is not continuous but in order) + * + * For example: example 2:- + * X: 'AGGTAB', Y: 'GXTXAYB' then Z will be 'AGGXTXAYB' + * @author [Ridhish Jain](https://github.com/ridhishjain) + * @see more on [SCS](https://en.wikipedia.org/wiki/Shortest_common_supersequence_problem) + * @see related problem [Leetcode](https://leetcode.com/problems/shortest-common-supersequence/) +*/ + +// header files +#include +#include +#include +#include +#include + +/** + * @namespace dynamic_programming + * @brief Dynamic Programming algorithms +*/ +namespace dynamic_programming { + + /** + * @namespace shortest_common_supersequence + * @brief Shortest Common Super Sequence algorithm + */ + namespace shortest_common_supersequence { + + /** + * Function implementing Shortest Common Super-Sequence algorithm using look-up table method. + * @param str1 first string 'X' + * @param str2 second string 'Y' + * @returns string 'Z', superSequence of X and Y + */ + std::string scs(const std::string &str1, const std::string &str2) { + + // Edge cases + // If either str1 or str2 or both are empty + if(str1.empty() && str2.empty()) { + return ""; + } + else if(str1.empty()) { + return str2; + } + else if(str2.empty()) { + return str1; + } + + // creating lookup table + std::vector > lookup(str1.length() + 1, std::vector (str2.length() + 1, 0)); + + for(int i=1; i <= str1.length(); i++) { + for(int j=1; j <= str2.length(); j++) { + if(str1[i-1] == str2[j-1]) { + lookup[i][j] = lookup[i-1][j-1] + 1; + } + else { + lookup[i][j] = std::max(lookup[i-1][j], lookup[i][j-1]); + } + } + } + + // making supersequence + // i and j are initially pointed towards end of strings + // Super-sequence will be constructed backwards + int i=str1.length(); + int j=str2.length(); + std::string s; + + while(i>0 && j>0) { + + // If the characters at i and j of both strings are same + // We only need to add them once in s + if(str1[i-1] == str2[j-1]) { + s.push_back(str1[i-1]); + i--; + j--; + } + // otherwise we check lookup table for recurrences of characters + else { + if(lookup[i-1][j] > lookup[i][j-1]) { + s.push_back(str1[i-1]); + i--; + } + else { + s.push_back(str2[j-1]); + j--; + } + } + } + + // copying remaining elements + // if j becomes 0 before i + while(i > 0) { + s.push_back(str1[i-1]); + i--; + } + + // if i becomes 0 before j + while(j > 0) { + s.push_back(str2[j-1]); + j--; + } + + // As the super sequence is constructd backwards + // reversing the string before returning gives us the correct output + reverse(s.begin(), s.end()); + return s; + } + } // namespace shortest_common_supersequence +} // namespace dynamic_programming + +/** + * Test Function + * @return void +*/ +static void test() { + // custom input vector + std::vector > scsStrings { + {"ABCXYZ", "ABZ"}, + {"ABZ", "ABCXYZ"}, + {"AGGTAB", "GXTXAYB"}, + {"X", "Y"}, + }; + + // calculated output vector by scs function + std::vector calculatedOutput(4, ""); + int i=0; + for(auto & scsString : scsStrings) { + + calculatedOutput[i] = dynamic_programming::shortest_common_supersequence::scs( + scsString[0], scsString[1] + ); + i++; + } + + // expected output vector acc to problem statement + std::vector expectedOutput { + "ABCXYZ", + "ABCXYZ", + "AGGXTXAYB", + "XY" + }; + + // Testing implementation via assert function + // It will throw error if any of the expected test fails + // Else it will give nothing + for(int i=0; i < scsStrings.size(); i++) { + assert(expectedOutput[i] == calculatedOutput[i]); + } + + std::cout << "All tests passed successfully!\n"; + return; +} + +/** Main function (driver code)*/ +int main() { + // test for implementation + test(); + + // user input + std::string s1, s2; + std::cin >> s1; + std::cin >> s2; + + std::string ans; + + // user output + ans = dynamic_programming::shortest_common_supersequence::scs(s1, s2); + std::cout << ans; + return 0; +} 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 41/61] 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 42/61] 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 43/61] 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 44/61] [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 45/61] 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 46/61] 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 47/61] 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 48/61] 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 49/61] 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 50/61] 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 51/61] 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 52/61] 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 53/61] 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 54/61] 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 55/61] 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 56/61] 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 57/61] 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 58/61] [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 59/61] 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 60/61] 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 61/61] 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)