diff --git a/docs/graphs/Graph connectivity Algorithms/Kosaraju's Algorithm.md b/docs/graphs/Graph connectivity Algorithms/Kosaraju's Algorithm.md deleted file mode 100644 index 6e5a50893..000000000 --- a/docs/graphs/Graph connectivity Algorithms/Kosaraju's Algorithm.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -id: kosarajus-algorithm -title: Kosaraju's Algorithm -sidebar_label: Kosaraju's Algorithm -description: "In this blog post, we'll delve into Kosaraju's Algorithm, a classic method for finding strongly connected components (SCCs) in a directed graph. We'll cover its implementation, time complexity, and use cases in various applications such as circuit design and social network analysis." -tags: [dsa, algorithms, graph connectivity] ---- - -# Kosaraju’s Algorithm - -## Introduction - -Kosaraju’s Algorithm is another efficient method for finding strongly connected components (SCCs) in a directed graph. It utilizes two passes of depth-first search (DFS): one on the original graph and another on the transposed graph. This algorithm effectively identifies all SCCs in linear time. - -## Implementation - -The implementation of Kosaraju's Algorithm involves the following steps: - -1. **First Pass**: - - Perform DFS on the original graph to determine the finishing order of vertices. - - Push each finished vertex onto a stack. - -2. **Transpose Graph**: - - Reverse all edges in the graph to create a transposed graph. - -3. **Second Pass**: - - Pop vertices from the stack and perform DFS on the transposed graph. - - Each DFS call identifies one strongly connected component. - -## Code in Java - -Here’s a sample implementation of Kosaraju’s Algorithm in Java: - -```java -import java.util.*; - -public class KosarajusAlgorithm { - private List> adj; - private List> transposedAdj; - - public KosarajusAlgorithm(int vertices) { - adj = new ArrayList<>(vertices); - transposedAdj = new ArrayList<>(vertices); - - for (int i = 0; i < vertices; i++) { - adj.add(new ArrayList<>()); - transposedAdj.add(new ArrayList<>()); - } - } - - public void addEdge(int u, int v) { - adj.get(u).add(v); - } - - private void dfs(int v, boolean[] visited, Stack stack) { - visited[v] = true; - - for (int neighbor : adj.get(v)) { - if (!visited[neighbor]) { - dfs(neighbor, visited, stack); - } - } - - stack.push(v); // Push finished vertex onto stack - } - - private void transposeGraph() { - for (int u = 0; u < adj.size(); u++) { - for (int v : adj.get(u)) { - transposedAdj.get(v).add(u); // Reverse edge - } - } - } - - public List> findSCCs() { - Stack stack = new Stack<>(); - - boolean[] visited = new boolean[adj.size()]; - - // First pass: fill stack with finishing order - for (int i = 0; i < adj.size(); i++) { - if (!visited[i]) { - dfs(i, visited, stack); - } - } - - transposeGraph(); // Create transposed graph - - Arrays.fill(visited, false); // Reset visited array - - List> sccs = new ArrayList<>(); - - // Second pass: process all vertices in order defined by stack - while (!stack.isEmpty()) { - int v = stack.pop(); - - if (!visited[v]) { - List scc = new ArrayList<>(); - dfsUtil(v, visited, scc); // Perform DFS on transposed graph - sccs.add(scc); // Add found SCC to result list - } - } - - return sccs; - } - - private void dfsUtil(int v, boolean[] visited, List scc) { - visited[v] = true; - scc.add(v); - - for (int neighbor : transposedAdj.get(v)) { - if (!visited[neighbor]) { - dfsUtil(neighbor, visited, scc); - } - } - } - - public static void main(String[] args) { - KosarajusAlgorithm graph = new KosarajusAlgorithm(5); - - // Example graph edges - graph.addEdge(0, 2); - graph.addEdge(2, 1); - graph.addEdge(1, 0); - graph.addEdge(0, 3); - graph.addEdge(3, 4); - - List> sccs = graph.findSCCs(); - - System.out.println("Strongly Connected Components:"); - for (List scc : sccs) { - System.out.println(scc); - } - } -} -``` - -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of Kosaraju’s Algorithm is \( O(V + E) \), where \( V \) is the number of vertices and \( E \) is the number of edges. This efficiency arises from performing two passes through all vertices and edges. - -### Space Complexity - -The space complexity is \( O(V + E) \), which accounts for: -- The storage of both the adjacency list and transposed adjacency list. -- The stack used during DFS. - -## Points to Remember - -1. **Strongly Connected Components**: Kosaraju's Algorithm efficiently identifies all SCCs in a directed graph. -2. **Two Passes**: The algorithm requires two passes through the graph—one on the original and one on the transposed version. -3. **Graph Type**: This algorithm works only on directed graphs. -4. **Applications**: Useful in various applications such as circuit design and analyzing social networks. -5. **Efficiency**: Like Tarjan's Algorithm, it operates in linear time relative to the size of the input. \ No newline at end of file diff --git "a/docs/graphs/Graph connectivity Algorithms/Tarjan\342\200\231s Algorithm.md" "b/docs/graphs/Graph connectivity Algorithms/Tarjan\342\200\231s Algorithm.md" deleted file mode 100644 index cd5f7e931..000000000 --- "a/docs/graphs/Graph connectivity Algorithms/Tarjan\342\200\231s Algorithm.md" +++ /dev/null @@ -1,136 +0,0 @@ ---- -id: tarjans-algorithm -title: Tarjan's Algorithm -sidebar_label: Tarjan's Algorithm -description: "In this blog post, we'll explore Tarjan's Algorithm, an efficient method for finding strongly connected components (SCCs) in a directed graph. We'll discuss its implementation, time complexity, and practical applications in graph theory." -tags: [dsa, algorithms, graph connectivity] ---- - -# Tarjan’s Algorithm - -## Introduction - -Tarjan’s Algorithm is an efficient method for finding strongly connected components (SCCs) in a directed graph. A strongly connected component is a maximal subgraph where every vertex is reachable from every other vertex within that component. The algorithm uses depth-first search (DFS) and maintains a low-link value to identify SCCs in linear time. - -## Implementation - -The implementation of Tarjan's Algorithm involves the following steps: - -1. **Initialize**: - - Create arrays to store discovery times and low-link values for each vertex. - - Use a stack to keep track of the vertices in the current path of DFS. - - Maintain a boolean array to track which vertices are in the stack. - -2. **Perform DFS**: - - For each unvisited vertex, perform a DFS. - - Update discovery times and low-link values. - - If the current vertex is the root of an SCC, pop vertices from the stack until the root is reached. - -## Code in Java - -Here’s a sample implementation of Tarjan’s Algorithm in Java: - -```java -import java.util.*; - -public class TarjansAlgorithm { - private int time = 0; - private List> adj; - private Stack stack; - private boolean[] onStack; - private int[] disc; - private int[] low; - private List> sccs; - - public TarjansAlgorithm(int vertices) { - adj = new ArrayList<>(vertices); - for (int i = 0; i < vertices; i++) { - adj.add(new ArrayList<>()); - } - stack = new Stack<>(); - onStack = new boolean[vertices]; - disc = new int[vertices]; - low = new int[vertices]; - Arrays.fill(disc, -1); - Arrays.fill(low, -1); - sccs = new ArrayList<>(); - } - - public void addEdge(int u, int v) { - adj.get(u).add(v); - } - - public void tarjanDFS(int u) { - disc[u] = low[u] = time++; - stack.push(u); - onStack[u] = true; - - for (int v : adj.get(u)) { - if (disc[v] == -1) { - tarjanDFS(v); - low[u] = Math.min(low[u], low[v]); - } else if (onStack[v]) { - low[u] = Math.min(low[u], disc[v]); - } - } - - if (low[u] == disc[u]) { - List scc = new ArrayList<>(); - int w; - do { - w = stack.pop(); - onStack[w] = false; - scc.add(w); - } while (w != u); - sccs.add(scc); - } - } - - public List> findSCCs() { - for (int i = 0; i < adj.size(); i++) { - if (disc[i] == -1) { - tarjanDFS(i); - } - } - return sccs; - } - - public static void main(String[] args) { - TarjansAlgorithm graph = new TarjansAlgorithm(5); - - // Example graph edges - graph.addEdge(0, 2); - graph.addEdge(2, 1); - graph.addEdge(1, 0); - graph.addEdge(0, 3); - graph.addEdge(3, 4); - - List> sccs = graph.findSCCs(); - - System.out.println("Strongly Connected Components:"); - for (List scc : sccs) { - System.out.println(scc); - } - } -} -``` - -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of Tarjan’s Algorithm is \( O(V + E) \), where \( V \) is the number of vertices and \( E \) is the number of edges. This efficiency arises from the single pass through all vertices and edges during the DFS. - -### Space Complexity - -The space complexity is \( O(V) \), which accounts for: -- The storage of the adjacency list. -- The stack used for maintaining vertices during DFS. - -## Points to Remember - -1. **Strongly Connected Components**: Tarjan's Algorithm identifies all SCCs in a directed graph efficiently. -2. **Single Pass**: The algorithm processes each vertex and edge only once. -3. **Low-Link Values**: The use of low-link values helps in determining the root of SCCs. -4. **Graph Type**: This algorithm works only on directed graphs. -5. **Applications**: Useful in various applications such as circuit design and analyzing social networks. \ No newline at end of file diff --git a/docs/graphs/Graphs.md b/docs/graphs/Graphs.md deleted file mode 100644 index 3b2a3ca27..000000000 --- a/docs/graphs/Graphs.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -id: graphs -title: Graphs -sidebar_label: Graphs -sidebar_position: 1 -description: "In this blog post, we'll explore Graph Algorithms, essential techniques used in solving complex problems across various domains such as computer science, networking, and artificial intelligence. We'll cover fundamental algorithms including traversal methods like Depth-First Search (DFS) and Breadth-First Search (BFS), shortest path algorithms like Dijkstra's and A*, and minimum spanning tree algorithms such as Prim's and Kruskal's. Additionally, we'll discuss advanced topics like network flow, graph connectivity, cycle detection, and topological sorting. With practical code examples in Python and Java, you'll gain a solid understanding of how to implement these algorithms effectively." -tags: [dsa, algorithms, graphs] ---- - -# Graphs - -Graph algorithms are essential in solving complex problems across various domains, such as computer science, networking, and artificial intelligence. Below is an overview of common graph algorithms categorized by their functionalities. - -## Applications of Graphs - -Graphs are versatile data structures that model relationships and connections in various domains. Here are some key applications of graphs: - -1. **Social Networks**: - - Graphs represent users as vertices and their relationships (friends, followers) as edges, enabling analysis of social dynamics and influence. - -2. **Transportation and Navigation**: - - Road networks can be modeled as graphs, where intersections are vertices and roads are edges. Algorithms like Dijkstra's and A* are used for route optimization. - -3. **Network Routing**: - - In computer networks, routers and switches are represented as nodes, and the connections between them as edges, facilitating efficient data transmission. - -4. **Recommendation Systems**: - - Graphs help in analyzing user-item interactions, enabling personalized recommendations based on collaborative filtering techniques. - -5. **Web Page Ranking**: - - The PageRank algorithm uses graph theory to rank web pages based on their link structures, determining the importance of pages in search engine results. - -6. **Biological Networks**: - - Graphs model biological systems, such as protein-protein interaction networks or metabolic pathways, aiding in the understanding of complex biological processes. - -7. **Game Development**: - - Graphs represent game maps and character movements, allowing for efficient pathfinding and AI decision-making in dynamic environments. - -8. **Dependency Resolution**: - - In software engineering, graphs model dependencies between modules or tasks, facilitating build order determination and resource allocation. - -9. **Circuit Design**: - - Electronic circuits can be represented as graphs where components are nodes and connections are edges, assisting in circuit analysis and optimization. - -10. **Project Management**: - - Graphs model tasks and their dependencies in project management (e.g., PERT charts), helping to identify critical paths and optimize scheduling. - -Graphs play a crucial role in various fields, making them an essential concept in computer science and related disciplines. - -## 1. Traversal Algorithms -These algorithms are used to visit all the nodes in a graph. - -### Depth-First Search (DFS) -- Explores as far as possible along each branch before backtracking, useful for pathfinding and topological sorting. - -### Breadth-First Search (BFS) -- Explores all neighbors at the present depth prior to moving on to nodes at the next depth level, ideal for shortest path finding in unweighted graphs. - -## 2. Shortest Path Algorithms -These algorithms find the shortest path between nodes in a graph. - -### Dijkstra’s Algorithm -- Finds the shortest path from a source node to all other nodes in a weighted graph with non-negative weights. - -### Bellman-Ford Algorithm -- Computes shortest paths from a single source node to all other nodes in a graph, allowing for negative weight edges. - -### A* Search Algorithm -- Uses heuristics to efficiently find the shortest path, often used in pathfinding and graph traversal. - -### Floyd-Warshall Algorithm -- A dynamic programming algorithm that finds shortest paths between all pairs of nodes in a weighted graph. - -## 3. Minimum Spanning Tree Algorithms -These algorithms find a subset of edges that connect all vertices with the minimum total edge weight. - -### Prim’s Algorithm -- Builds the minimum spanning tree by adding edges one at a time, starting from an arbitrary node. - -### Kruskal’s Algorithm -- Finds the minimum spanning tree by sorting edges and adding them one by one while avoiding cycles. - -## 4. Network Flow Algorithms -These algorithms deal with flow networks and finding maximum flow. - -### Ford-Fulkerson Method -- Computes the maximum flow in a flow network using augmenting paths. - -### Edmonds-Karp Algorithm -- An implementation of Ford-Fulkerson that uses BFS to find augmenting paths, ensuring polynomial time complexity. - -## 5. Graph Connectivity Algorithms -These algorithms determine the connectivity properties of a graph. - -### Tarjan’s Algorithm -- Finds strongly connected components in a directed graph using DFS. - -### Kosaraju’s Algorithm -- Another method for finding strongly connected components using two passes of DFS. - -## 6. Cycle Detection Algorithms -These algorithms identify cycles within graphs. - -### Cycle Detection in Directed Graphs -- Uses DFS to detect cycles by tracking visited nodes and recursion stack. - -### Cycle Detection in Undirected Graphs -- Uses DFS or Union-Find to detect cycles by checking for back edges. - -## 7. Topological Sorting Algorithms -These algorithms provide an ordering of vertices for directed acyclic graphs (DAGs). - -### Kahn’s Algorithm -- Uses indegrees of vertices to produce a topological sort iteratively. - -### Depth-First Search Based Topological Sort -- Uses DFS to produce a topological order by finishing times of vertices. - -## 8. Graph Coloring Algorithms -These algorithms assign colors to vertices such that no two adjacent vertices share the same color. - -### Greedy Coloring Algorithm -- Assigns colors sequentially while ensuring no two adjacent vertices have the same color. - -## 9. Matching Algorithms -These algorithms find matches in bipartite graphs or general graphs. - -### Hopcroft-Karp Algorithm -- Efficiently finds maximum matching in bipartite graphs using BFS and DFS. - -## 10. Miscellaneous Algorithms -Other notable graph algorithms include: - -### Johnson’s Algorithm -- Finds all pairs shortest paths in sparse graphs using both Dijkstra’s and Bellman-Ford algorithms. - -### PageRank Algorithm -- An algorithm used by Google Search to rank web pages based on their importance as determined by link structures. - -## Conclusion - -Graph algorithms are diverse and powerful tools for solving complex problems across various domains. Understanding these algorithms can significantly enhance your problem-solving skills in computer science and related fields. If you need more detailed explanations or examples of any specific algorithm, feel free to ask. \ No newline at end of file diff --git a/docs/graphs/Minimum Spanning Tree Algorithms/Kruskal's Algorithm.md b/docs/graphs/Minimum Spanning Tree Algorithms/Kruskal's Algorithm.md deleted file mode 100644 index 74f6c117a..000000000 --- a/docs/graphs/Minimum Spanning Tree Algorithms/Kruskal's Algorithm.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -id: kruskals-algorithm -title: Kruskal's Algorithm -sidebar_label: Kruskal's Algorithm -description: "In this blog post, we'll delve into Kruskal's Algorithm, a classic greedy algorithm used to find the minimum spanning tree (MST) of a weighted undirected graph. We'll cover its implementation, time complexity, and use cases in various applications." -tags: [dsa, algorithms, minimum spanning tree] ---- - -# Kruskal’s Algorithm - -## Introduction - -Kruskal’s Algorithm is a classic greedy algorithm used to find the minimum spanning tree (MST) of a weighted, undirected graph. Like Prim's Algorithm, Kruskal's aims to connect all vertices with the minimum total edge weight while avoiding cycles. The algorithm works by sorting all edges in non-decreasing order of their weights and adding them one by one to the MST, ensuring that no cycles are formed. - -Kruskal's Algorithm is particularly useful for sparse graphs, where the number of edges is much lower than the maximum possible number of edges. - -## Implementation - -The implementation of Kruskal’s Algorithm involves the following steps: - -1. **Sort Edges**: - - Sort all the edges in non-decreasing order based on their weights. - -2. **Initialize**: - - Create a disjoint-set (union-find) data structure to keep track of which vertices are in which components. - -3. **Process Edges**: - - Iterate through the sorted edges, and for each edge: - - Check if adding this edge would form a cycle using the union-find structure. - - If it does not form a cycle, add it to the MST. - -4. **Repeat** until you have added \( V - 1 \) edges (where \( V \) is the number of vertices). - -## Code in Java - -Here’s a sample implementation of Kruskal’s Algorithm in Java: - -```java -import java.util.*; - -public class KruskalsAlgorithm { - - static class Edge implements Comparable { - int src, dest, weight; - - Edge(int src, int dest, int weight) { - this.src = src; - this.dest = dest; - this.weight = weight; - } - - @Override - public int compareTo(Edge other) { - return Integer.compare(this.weight, other.weight); - } - } - - static class DisjointSet { - int[] parent, rank; - - DisjointSet(int n) { - parent = new int[n]; - rank = new int[n]; - for (int i = 0; i < n; i++) { - parent[i] = i; - rank[i] = 0; - } - } - - int find(int u) { - if (parent[u] != u) { - parent[u] = find(parent[u]); // Path compression - } - return parent[u]; - } - - void union(int u, int v) { - int rootU = find(u); - int rootV = find(v); - if (rootU != rootV) { - // Union by rank - if (rank[rootU] > rank[rootV]) { - parent[rootV] = rootU; - } else if (rank[rootU] < rank[rootV]) { - parent[rootU] = rootV; - } else { - parent[rootV] = rootU; - rank[rootU]++; - } - } - } - } - - public static void kruskalsAlgorithm(int vertices, List edges) { - Collections.sort(edges); // Sort edges by weight - DisjointSet ds = new DisjointSet(vertices); - List mstEdges = new ArrayList<>(); - - for (Edge edge : edges) { - int u = edge.src; - int v = edge.dest; - - // Check if including this edge creates a cycle - if (ds.find(u) != ds.find(v)) { - ds.union(u, v); - mstEdges.add(edge); - } - } - - // Print the resulting MST - System.out.println("Edges in Minimum Spanning Tree:"); - for (Edge edge : mstEdges) { - System.out.println(edge.src + " -- " + edge.dest + " == " + edge.weight); - } - } - - public static void main(String[] args) { - int vertices = 5; - List edges = new ArrayList<>(); - - // Example graph edges - edges.add(new Edge(0, 1, 10)); - edges.add(new Edge(0, 2, 6)); - edges.add(new Edge(0, 3, 5)); - edges.add(new Edge(1, 3, 15)); - edges.add(new Edge(2, 3, 4)); - - kruskalsAlgorithm(vertices, edges); - } -} -``` - -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of Kruskal’s Algorithm is \( O(E \log E) \), where \( E \) is the number of edges. This complexity arises from: -- Sorting the edges: \( O(E \log E) \). -- The union-find operations can be performed in nearly constant time due to path compression and union by rank. - -### Space Complexity - -The space complexity is \( O(V + E) \), which accounts for: -- The storage of the edge list. -- The disjoint-set data structure used to keep track of connected components. - -## Points to Remember - -1. **Greedy Approach**: Kruskal's Algorithm employs a greedy strategy by always choosing the smallest edge that does not form a cycle. - -2. **Disconnected Graphs**: Kruskal's can work on disconnected graphs and will produce a minimum spanning forest instead of a single MST. - -3. **Multiple MSTs**: There can be multiple minimum spanning trees for a given graph if there are equal-weight edges. - -4. **Use Cases**: Common applications include network design problems like connecting cities with roads or laying out electrical grids. - -5. **Comparison with Prim's Algorithm**: While both algorithms find an MST, Kruskal's is often more efficient for sparse graphs while Prim's is better suited for dense graphs. \ No newline at end of file diff --git a/docs/graphs/Minimum Spanning Tree Algorithms/Prim's Algorithm.md b/docs/graphs/Minimum Spanning Tree Algorithms/Prim's Algorithm.md deleted file mode 100644 index edfa2d23e..000000000 --- a/docs/graphs/Minimum Spanning Tree Algorithms/Prim's Algorithm.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -id: prims-algorithm -title: Prim's Algorithm -sidebar_label: Prim's Algorithm -description: "In this blog post, we'll explore Prim's Algorithm, a fundamental graph algorithm used to find the minimum spanning tree (MST) of a weighted undirected graph. We'll discuss its implementation, time complexity, and practical applications in network design." -tags: [dsa, algorithms, minimum spanning tree] ---- - -# Prim’s Algorithm - -## Introduction - -Prim’s Algorithm is a classic greedy algorithm used to find the minimum spanning tree (MST) of a weighted, undirected graph. A minimum spanning tree is a subset of the edges that connects all vertices in the graph without any cycles and with the minimum possible total edge weight. Prim's Algorithm is particularly useful in network design, such as designing least-cost networks for telecommunications or computer networks. - -The algorithm starts with a single vertex and grows the MST by repeatedly adding the smallest edge that connects a vertex in the tree to a vertex outside the tree. - -## Implementation - -The implementation of Prim’s Algorithm involves the following steps: - -1. **Initialize**: - - Create a priority queue (or min-heap) to store edges based on their weights. - - Create an array to track which vertices are included in the MST. - - Start from an arbitrary vertex and add its edges to the priority queue. - -2. **Process Edges**: - - While there are edges in the priority queue: - - Extract the edge with the minimum weight. - - If it connects a vertex not yet included in the MST, add it to the MST and mark the vertex as included. - - Add all edges from this new vertex to the priority queue. - -3. **Repeat** until all vertices are included in the MST. - -## Code in Java - -Here’s a sample implementation of Prim’s Algorithm in Java: - -```java -import java.util.*; - -public class PrimsAlgorithm { - - static class Edge implements Comparable { - int src, dest, weight; - - Edge(int src, int dest, int weight) { - this.src = src; - this.dest = dest; - this.weight = weight; - } - - @Override - public int compareTo(Edge other) { - return Integer.compare(this.weight, other.weight); - } - } - - public static void primsAlgorithm(int vertices, List edges) { - // Create a priority queue to store edges - PriorityQueue pq = new PriorityQueue<>(); - boolean[] inMST = new boolean[vertices]; - List mstEdges = new ArrayList<>(); - - // Start from vertex 0 - inMST = true; - for (Edge edge : edges) { - if (edge.src == 0 || edge.dest == 0) { - pq.offer(edge); - } - } - - while (!pq.isEmpty()) { - Edge minEdge = pq.poll(); - - // If it connects to an unvisited vertex - if (!inMST[minEdge.dest]) { - mstEdges.add(minEdge); - inMST[minEdge.dest] = true; - - // Add all edges from this vertex to the priority queue - for (Edge edge : edges) { - if (edge.src == minEdge.dest && !inMST[edge.dest]) { - pq.offer(edge); - } else if (edge.dest == minEdge.dest && !inMST[edge.src]) { - pq.offer(edge); - } - } - } - } - - // Print the resulting MST - System.out.println("Edges in Minimum Spanning Tree:"); - for (Edge edge : mstEdges) { - System.out.println(edge.src + " -- " + edge.dest + " == " + edge.weight); - } - } - - public static void main(String[] args) { - int vertices = 5; - List edges = new ArrayList<>(); - - // Example graph edges - edges.add(new Edge(0, 1, 2)); - edges.add(new Edge(0, 3, 6)); - edges.add(new Edge(1, 2, 3)); - edges.add(new Edge(1, 3, 8)); - edges.add(new Edge(1, 4, 5)); - edges.add(new Edge(2, 4, 7)); - edges.add(new Edge(3, 4, 9)); - - primsAlgorithm(vertices, edges); - } -} -``` - -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of Prim’s Algorithm depends on how we implement the priority queue: -- Using an adjacency matrix and a simple array: \( O(V^2) \) -- Using an adjacency list and a binary heap: \( O(E \log V) \), where \( E \) is the number of edges and \( V \) is the number of vertices. - -### Space Complexity - -The space complexity is \( O(V + E) \), which accounts for: -- The storage of the adjacency list. -- The priority queue used to store edges. - -## Points to Remember - -1. **Greedy Approach**: Prim's Algorithm uses a greedy approach by always choosing the smallest edge that expands the growing MST. - -2. **Connected Graphs**: Prim's Algorithm works only on connected graphs. If the graph is disconnected, it will only produce an MST for one of its components. - -3. **Multiple MSTs**: There can be multiple minimum spanning trees for a given graph if there are equal-weight edges. - -4. **Use Cases**: Common applications include network design problems like laying out electrical grids or computer networks. - -5. **Comparison with Kruskal's Algorithm**: While both algorithms find an MST, Prim's is often more efficient for dense graphs while Kruskal's is better suited for sparse graphs. \ No newline at end of file diff --git a/docs/graphs/Shortest Path Algorithms/A(star) algorithm.md b/docs/graphs/Shortest Path Algorithms/A(star) algorithm.md deleted file mode 100644 index 432704013..000000000 --- a/docs/graphs/Shortest Path Algorithms/A(star) algorithm.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -id: a-star -title: A* Algorithm -sidebar_label: A* Algorithm -description: "In this blog post, we'll explore the A* Algorithm, a powerful pathfinding and graph traversal algorithm that efficiently finds the shortest path from a starting node to a target node. By combining the strengths of Dijkstra's algorithm and greedy best-first search, A* uses heuristics to prioritize paths, making it widely used in applications such as game development and robotics." -tags: [dsa, algorithms, pathfinding] ---- - -# A* Algorithm - -## Introduction - -The A* (A-star) algorithm is a popular and powerful pathfinding and graph traversal algorithm used in computer science, robotics, and artificial intelligence. It efficiently finds the shortest path from a starting node to a target node by combining the strengths of Dijkstra's algorithm and greedy best-first search. A* uses a heuristic to estimate the cost from the current node to the goal, allowing it to prioritize paths that are likely to lead to the shortest route. - -## Implementation - -The A* algorithm operates by maintaining a priority queue of nodes to be explored. The key steps involved in the implementation are: - -1. **Initialize**: - - Create an open list (priority queue) to store nodes that need to be evaluated. - - Create a closed list to store nodes that have already been evaluated. - - Initialize the starting node with a cost of zero. - -2. **Evaluate Nodes**: - - While there are nodes in the open list: - - Retrieve the node with the lowest total cost (f = g + h), where: - - \( g \) is the cost from the start node to the current node. - - \( h \) is the heuristic estimated cost from the current node to the goal. - - If this node is the goal, reconstruct and return the path. - - Otherwise, evaluate its neighbors and update their costs. - -3. **Update Costs**: - - For each neighbor: - - Calculate tentative \( g \) cost. - - If this path is better than any previously recorded path, update costs and add the neighbor to the open list. - -## Code in Java - -Here’s a sample implementation of the A* algorithm in Java: - -```java -import java.util.*; - -class Node implements Comparable { - int x, y; - double gCost; // Cost from start node - double hCost; // Heuristic cost - double fCost; // Total cost - Node parent; - - public Node(int x, int y) { - this.x = x; - this.y = y; - } - - @Override - public int compareTo(Node other) { - return Double.compare(this.fCost, other.fCost); - } -} - -public class AStarAlgorithm { - private static final int[][] DIRECTIONS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // Up, Right, Down, Left - - public static List aStar(Node start, Node goal, int[][] grid) { - PriorityQueue openList = new PriorityQueue<>(); - boolean[][] closedList = new boolean[grid.length][grid.length]; - - start.gCost = 0; - start.hCost = heuristic(start, goal); - start.fCost = start.gCost + start.hCost; - openList.add(start); - - while (!openList.isEmpty()) { - Node current = openList.poll(); - if (current.equals(goal)) { - return reconstructPath(current); - } - - closedList[current.x][current.y] = true; - - for (int[] direction : DIRECTIONS) { - int newX = current.x + direction; - int newY = current.y + direction; - - if (isValid(newX, newY, grid) && !closedList[newX][newY]) { - Node neighbor = new Node(newX, newY); - double tentativeGCost = current.gCost + 1; // Assuming uniform cost for simplicity - - if (tentativeGCost < neighbor.gCost || !openList.contains(neighbor)) { - neighbor.gCost = tentativeGCost; - neighbor.hCost = heuristic(neighbor, goal); - neighbor.fCost = neighbor.gCost + neighbor.hCost; - neighbor.parent = current; - - if (!openList.contains(neighbor)) { - openList.add(neighbor); - } - } - } - } - } - return Collections.emptyList(); // No path found - } - - private static double heuristic(Node a, Node b) { - return Math.abs(a.x - b.x) + Math.abs(a.y - b.y); // Manhattan distance - } - - private static boolean isValid(int x, int y, int[][] grid) { - return x >= 0 && x < grid.length && y >= 0 && y < grid.length && grid[x][y] == 0; // 0 represents walkable cell - } - - private static List reconstructPath(Node goal) { - List path = new ArrayList<>(); - for (Node at = goal; at != null; at = at.parent) { - path.add(at); - } - Collections.reverse(path); - return path; - } - - public static void main(String[] args) { - int[][] grid = { - {0, 0, 0, 0}, - {1, 1, 0, 1}, - {0, 0, 0, 0}, - {0, 1, 1, 0} - }; - - Node start = new Node(0, 0); - Node goal = new Node(2, 3); - - List path = aStar(start, goal, grid); - - System.out.println("Path from start to goal:"); - for (Node node : path) { - System.out.println("(" + node.x + ", " + node.y + ")"); - } - } -} -``` - -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of the A* algorithm is \( O(E) \), where \( E \) is the number of edges. In practice, this can vary based on the heuristic used and how many nodes are explored. - -### Space Complexity - -The space complexity is also \( O(E) \), as it stores all nodes in memory during execution. - -## Points to Remember - -1. **Heuristic Function**: The choice of heuristic significantly affects performance; common heuristics include Manhattan distance and Euclidean distance. -2. **Optimality**: A* is guaranteed to find an optimal solution if the heuristic is admissible (never overestimates). -3. **Applications**: Widely used in AI for game development and robotics for navigation tasks. -4. **Trade-offs**: Balances between Dijkstra's exhaustive search and greedy best-first search efficiency. -5. **Grid-Based Pathfinding**: Particularly effective in grid-based systems where movement costs are uniform. \ No newline at end of file diff --git a/docs/graphs/Topological Sorting Algorithms/Depth-First Search Based Topological Sort.md b/docs/graphs/Topological Sorting Algorithms/Depth-First Search Based Topological Sort.md deleted file mode 100644 index f8100b59f..000000000 --- a/docs/graphs/Topological Sorting Algorithms/Depth-First Search Based Topological Sort.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -id: dfs-topological-sort -title: Depth-First Search Based Topological Sort -sidebar_label: DFS Based Topological Sort -description: "In this blog post, we'll explore the Depth-First Search (DFS) Based Topological Sort, an effective algorithm for ordering the vertices of a directed acyclic graph (DAG). We'll discuss its implementation, time complexity, space complexity, and key points to remember." -tags: [dsa, algorithms, topological sorting] ---- - -# Depth-First Search Based Topological Sort - -## Introduction - -Depth-First Search (DFS) Based Topological Sort is a powerful algorithm used to order the vertices of a directed acyclic graph (DAG). In topological sorting, the vertices are arranged in such a way that for every directed edge \( u -> v \), vertex \( u \) comes before vertex \( v \) in the ordering. This algorithm is particularly useful in scenarios like task scheduling, where certain tasks must be completed before others can start. - -DFS-Based Topological Sort leverages the depth-first search strategy to explore the graph and build a topological order by utilizing a stack to keep track of the vertices as they are processed. - -## Implementation - -The implementation of DFS-Based Topological Sort involves the following steps: - -1. **Initialize**: - - Create a visited array to keep track of visited vertices. - - Create a stack to store the topological order. - -2. **Perform DFS**: - - For each unvisited vertex, perform a DFS. - - Mark the vertex as visited and recursively visit all its unvisited neighbors. - - After visiting all neighbors, push the vertex onto the stack. - -3. **Construct Topological Order**: - - Once all vertices are processed, pop elements from the stack to get the topological order. - -## Code in Java - -Here’s a sample implementation of Depth-First Search Based Topological Sort in Java: - -```java -import java.util.*; - -public class DFSTopologicalSort { - - private static void dfs(int vertex, boolean[] visited, Stack stack, List> adj) { - visited[vertex] = true; - - // Visit all the neighbors - for (int neighbor : adj.get(vertex)) { - if (!visited[neighbor]) { - dfs(neighbor, visited, stack, adj); - } - } - - // Push current vertex to stack after visiting all its neighbors - stack.push(vertex); - } - - public static List topologicalSort(int vertices, List> adj) { - boolean[] visited = new boolean[vertices]; - Stack stack = new Stack<>(); - - // Perform DFS for each vertex - for (int i = 0; i < vertices; i++) { - if (!visited[i]) { - dfs(i, visited, stack, adj); - } - } - - // Prepare the topological order from the stack - List topoOrder = new ArrayList<>(); - while (!stack.isEmpty()) { - topoOrder.add(stack.pop()); - } - - return topoOrder; - } - - public static void main(String[] args) { - int vertices = 6; - List> adj = new ArrayList<>(vertices); - - for (int i = 0; i < vertices; i++) { - adj.add(new ArrayList<>()); - } - - // Example graph edges - adj.get(5).add(2); - adj.get(5).add(0); - adj.get(4).add(0); - adj.get(4).add(1); - adj.get(2).add(3); - adj.get(3).add(1); - - List result = topologicalSort(vertices, adj); - System.out.println("Topological Order: " + result); - } -} -``` -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of DFS-Based Topological Sort is \( O(V + E) \), where \( V \) is the number of vertices and \( E \) is the number of edges. This is because: -- Each vertex is processed once. -- Each edge is traversed once during the DFS. - -### Space Complexity - -The space complexity is \( O(V) \), which accounts for: -- The storage of the visited array. -- The stack used to store the topological order. - -## Points to Remember - -1. **Directed Acyclic Graphs**: This algorithm only works on directed acyclic graphs. If there are cycles, a valid topological sort cannot be produced. - -2. **Multiple Valid Orders**: There may be multiple valid topological sorts for a given DAG. - -3. **Cycle Detection**: If you detect that not all vertices are included in the final topological order, it indicates that there is at least one cycle present in the graph. - -4. **Use Cases**: Common applications include task scheduling, course prerequisite management, and dependency resolution. - -5. **Alternative Algorithms**: While DFS-Based Topological Sort is efficient and intuitive, Kahn's Algorithm can also be used for topological sorting. \ No newline at end of file diff --git a/docs/graphs/Topological Sorting Algorithms/Kahn's Algorithm.md b/docs/graphs/Topological Sorting Algorithms/Kahn's Algorithm.md deleted file mode 100644 index d489a0ffc..000000000 --- a/docs/graphs/Topological Sorting Algorithms/Kahn's Algorithm.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -id: kahns-algorithm -title: Kahn's Algorithm -sidebar_label: Kahn's Algorithm -description: "In this blog post, we'll explore Kahn's Algorithm, a key graph algorithm used for topological sorting of directed acyclic graphs (DAGs). We'll discuss its implementation, time complexity, and practical applications in various fields." -tags: [dsa, algorithms, topological sorting] ---- - -# Kahn's Algorithm - -## Introduction - -Kahn's Algorithm is a fundamental method used to perform topological sorting on directed acyclic graphs (DAGs). Topological sorting is a linear ordering of vertices such that for every directed edge \( u \rightarrow v \), vertex \( u \) comes before vertex \( v \) in the ordering. This algorithm is particularly useful in scenarios such as task scheduling, where certain tasks must be completed before others can begin. - -Kahn’s Algorithm operates using the concept of in-degrees, which counts the number of incoming edges to each vertex. By iteratively removing vertices with zero in-degrees and updating the in-degrees of their neighbors, Kahn's Algorithm efficiently constructs a valid topological order. - -## Implementation - -Kahn’s Algorithm can be implemented using a queue to keep track of vertices with zero in-degrees. The basic steps are as follows: - -1. Calculate the in-degree for each vertex. -2. Initialize a queue and enqueue all vertices with an in-degree of zero. -3. While the queue is not empty: - - Dequeue a vertex and add it to the topological order. - - For each outgoing edge from this vertex, decrease the in-degree of the destination vertex by one. - - If any destination vertex's in-degree becomes zero, enqueue it. -4. If all vertices are processed, return the topological order; otherwise, report that a cycle exists. - -## Code in Java - -Here’s a sample implementation of Kahn's Algorithm in Java: - -```java -import java.util.*; - -public class KahnsAlgorithm { - - public static List topologicalSort(int vertices, List> adj) { - int[] inDegree = new int[vertices]; - List topoOrder = new ArrayList<>(); - - // Step 1: Calculate in-degrees - for (int i = 0; i < vertices; i++) { - for (int neighbor : adj.get(i)) { - inDegree[neighbor]++; - } - } - - // Step 2: Initialize the queue with all vertices having in-degree 0 - Queue queue = new LinkedList<>(); - for (int i = 0; i < vertices; i++) { - if (inDegree[i] == 0) { - queue.offer(i); - } - } - - // Step 3: Process vertices - while (!queue.isEmpty()) { - int current = queue.poll(); - topoOrder.add(current); - - // Decrease the in-degree of neighboring vertices - for (int neighbor : adj.get(current)) { - inDegree[neighbor]--; - // If the in-degree becomes zero, add it to the queue - if (inDegree[neighbor] == 0) { - queue.offer(neighbor); - } - } - } - - // Check for cycles - if (topoOrder.size() != vertices) { - throw new IllegalArgumentException("Graph has at least one cycle."); - } - - return topoOrder; - } - - public static void main(String[] args) { - int vertices = 6; - List> adj = new ArrayList<>(vertices); - - for (int i = 0; i < vertices; i++) { - adj.add(new ArrayList<>()); - } - - // Example graph edges - adj.get(5).add(2); - adj.get(5).add(0); - adj.get(4).add(0); - adj.get(4).add(1); - adj.get(2).add(3); - adj.get(3).add(1); - - try { - List result = topologicalSort(vertices, adj); - System.out.println("Topological Order: " + result); - } catch (IllegalArgumentException e) { - System.out.println(e.getMessage()); - } - } -} -``` -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of Kahn's Algorithm is \( O(V + E) \), where \( V \) is the number of vertices and \( E \) is the number of edges. This is because: -- Calculating the in-degrees takes \( O(E) \). -- Processing each vertex and its neighbors also takes \( O(V + E) \). - -### Space Complexity - -The space complexity is \( O(V) \), which accounts for: -- The storage of the in-degree array. -- The queue used to manage vertices with zero in-degrees. -- The list storing the topological order. - -## Points to Remember -- Directed Acyclic Graphs: Kahn’s Algorithm only works on directed acyclic graphs. If there are cycles, a valid topological sort cannot be produced. -- Multiple Valid Orders: There may be multiple valid topological sorts for a given DAG. -- Cycle Detection: If the size of the resulting topological order does not equal the number of vertices, it indicates that there is at least one cycle present in the graph. -- Use Cases: Common applications include task scheduling, course prerequisite management, and dependency resolution. -Alternative Algorithms: While Kahn's Algorithm is efficient and intuitive, other methods like Depth-First Search can also be used for topological sorting. \ No newline at end of file diff --git a/docs/graphs/Traversals/Breadth First Search.md b/docs/graphs/Traversals/Breadth First Search.md deleted file mode 100644 index b19bb72ae..000000000 --- a/docs/graphs/Traversals/Breadth First Search.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -id: breadth-first-search -title: Breadth-First Search (BFS) -sidebar_label: Breadth-First Search -description: "In this blog post, we'll delve into Breadth-First Search (BFS), another essential graph traversal algorithm. We'll cover its implementation, time complexity, and use cases in applications like shortest path finding and network broadcasting." -tags: [dsa, algorithms, graph traversal] ---- - - -### Breadth-First Search (BFS) - -```markdown -# Breadth-First Search (BFS) - -## Introduction - -Breadth-First Search (BFS) is another fundamental graph traversal algorithm that explores nodes level by level. Starting from a selected node (or vertex), BFS visits all neighboring nodes at the present depth before moving on to nodes at the next depth level. This algorithm is widely used in applications such as finding the shortest path in unweighted graphs and network broadcasting. - -## Implementation - -The implementation of BFS involves using a queue to keep track of vertices that need to be explored. The basic steps are: - -1. **Initialize**: - - Create a visited array to track visited vertices. - - Use a queue to manage the vertices for exploration. - -2. **Process Vertices**: - - Enqueue the starting vertex and mark it as visited. - - While the queue is not empty, dequeue a vertex and visit all its unvisited neighbors, enqueuing them for future exploration. - -## Code in Java - -Here’s a sample implementation of Breadth-First Search in Java: - -```java -import java.util.*; - -public class BreadthFirstSearch { - - public static void bfs(int startVertex, boolean[] visited, List> adj) { - Queue queue = new LinkedList<>(); - queue.offer(startVertex); - visited[startVertex] = true; - - while (!queue.isEmpty()) { - int vertex = queue.poll(); - System.out.print(vertex + " "); // Process the current vertex - - // Visit all neighbors - for (int neighbor : adj.get(vertex)) { - if (!visited[neighbor]) { - visited[neighbor] = true; - queue.offer(neighbor); - } - } - } - } - - public static void main(String[] args) { - int vertices = 5; - List> adj = new ArrayList<>(vertices); - - for (int i = 0; i < vertices; i++) { - adj.add(new ArrayList<>()); - } - - // Example graph edges - adj.get(0).add(1); - adj.get(0).add(2); - adj.get(1).add(3); - adj.get(1).add(4); - - boolean[] visited = new boolean[vertices]; - System.out.println("Breadth-First Search starting from vertex 0:"); - bfs(0, visited, adj); - } -} -``` - -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of BFS is \( O(V + E) \), where \( V \) is the number of vertices and \( E \) is the number of edges. Each vertex and edge is processed once during traversal. - -### Space Complexity - -The space complexity is \( O(V) \), which accounts for: -- The storage of the visited array. -- The queue used for managing vertices during traversal. - -## Points to Remember - -1. **Graph Type**: BFS works on both directed and undirected graphs. -2. **Shortest Path**: BFS can find the shortest path in unweighted graphs. -3. **Level Order Traversal**: BFS explores nodes level by level, making it suitable for problems involving layers or levels. -4. **Memory Usage**: BFS requires more memory than DFS due to its use of a queue. -5. **Cycle Detection**: BFS can also be used to detect cycles in undirected graphs by checking back edges. \ No newline at end of file diff --git a/docs/graphs/Traversals/Depth First Search.md b/docs/graphs/Traversals/Depth First Search.md deleted file mode 100644 index f07344a18..000000000 --- a/docs/graphs/Traversals/Depth First Search.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -id: depth-first-search -title: Depth-First Search (DFS) -sidebar_label: Depth-First Search -description: "In this blog post, we'll explore Depth-First Search (DFS), a fundamental graph traversal algorithm. We'll discuss its implementation, time complexity, and practical applications in various fields such as pathfinding and tree traversals." -tags: [dsa, algorithms, graph traversal] ---- - -# Depth-First Search (DFS) - -## Introduction - -Depth-First Search (DFS) is a fundamental graph traversal algorithm used to explore nodes and edges of a graph. Starting from a selected node (or vertex), DFS explores as far as possible along each branch before backtracking. This algorithm is particularly useful in scenarios such as pathfinding, topological sorting, and solving puzzles with a single solution. - -## Implementation - -The implementation of DFS can be done using either recursion or an explicit stack. The basic steps are: - -1. **Initialize**: - - Create a visited array to track visited vertices. - - Use a stack or recursion to explore vertices. - -2. **Process Vertices**: - - For each unvisited vertex, mark it as visited and recursively visit all its unvisited neighbors. - -## Code in Java - -Here’s a sample implementation of Depth-First Search in Java: - -```java -import java.util.*; - -public class DepthFirstSearch { - - public static void dfs(int vertex, boolean[] visited, List> adj) { - visited[vertex] = true; - System.out.print(vertex + " "); // Process the current vertex - - // Visit all the neighbors - for (int neighbor : adj.get(vertex)) { - if (!visited[neighbor]) { - dfs(neighbor, visited, adj); - } - } - } - - public static void main(String[] args) { - int vertices = 5; - List> adj = new ArrayList<>(vertices); - - for (int i = 0; i < vertices; i++) { - adj.add(new ArrayList<>()); - } - - // Example graph edges - adj.get(0).add(1); - adj.get(0).add(2); - adj.get(1).add(3); - adj.get(1).add(4); - - boolean[] visited = new boolean[vertices]; - System.out.println("Depth-First Search starting from vertex 0:"); - dfs(0, visited, adj); - } -} -``` - -## Time Complexity and Space Complexity - -### Time Complexity - -The time complexity of DFS is \( O(V + E) \), where \( V \) is the number of vertices and \( E \) is the number of edges. This is because each vertex and edge is processed once. - -### Space Complexity - -The space complexity is \( O(V) \), which accounts for: -- The storage of the visited array. -- The recursion stack (or explicit stack) used for traversal. - -## Points to Remember - -1. **Graph Type**: DFS works on both directed and undirected graphs. -2. **Cycle Detection**: DFS can be used to detect cycles in graphs. -3. **Pathfinding**: DFS may not find the shortest path in weighted graphs. -4. **Traversal Order**: The order of traversal may vary based on the implementation and graph structure. -5. **Memory Usage**: Recursive implementations may lead to stack overflow for large graphs due to deep recursion. \ No newline at end of file