+
+ );
+};
+
+export default DijkstraVisualizer;
diff --git a/app/visualizer/graph/algorithms/dijkstra/code.js b/app/visualizer/graph/algorithms/dijkstra/code.js
new file mode 100755
index 0000000..b0b4c14
--- /dev/null
+++ b/app/visualizer/graph/algorithms/dijkstra/code.js
@@ -0,0 +1,215 @@
+const codeExamples = {
+ javascript: `// Greedy relaxation: since all weights are non-negative, always finalizing
+// the closest unvisited vertex guarantees its distance can never improve.
+function dijkstra(vertices, adjList, start) {
+ const dist = {};
+ const prev = {};
+ const visited = new Set();
+ vertices.forEach((v) => (dist[v] = Infinity));
+ dist[start] = 0;
+
+ while (visited.size < vertices.length) {
+ // Pick the unvisited vertex with the smallest tentative distance
+ let u = null;
+ let best = Infinity;
+ for (const v of vertices) {
+ if (!visited.has(v) && dist[v] < best) {
+ best = dist[v];
+ u = v;
+ }
+ }
+ if (u === null) break; // remaining vertices are unreachable
+
+ visited.add(u);
+
+ for (const { to, weight } of adjList[u] || []) {
+ if (visited.has(to)) continue;
+ const candidate = dist[u] + weight;
+ if (candidate < dist[to]) {
+ dist[to] = candidate; // relax the edge
+ prev[to] = u;
+ }
+ }
+ }
+
+ return { dist, prev };
+}
+
+function reconstructPath(prev, start, target) {
+ const path = [];
+ let current = target;
+ while (current !== undefined && current !== start) {
+ path.unshift(current);
+ current = prev[current];
+ }
+ if (current !== start) return null; // unreachable
+ path.unshift(start);
+ return path;
+}
+
+// Usage example
+const adjList = {
+ A: [{ to: "B", weight: 4 }, { to: "C", weight: 1 }],
+ B: [{ to: "A", weight: 4 }, { to: "C", weight: 2 }, { to: "D", weight: 5 }],
+ C: [{ to: "A", weight: 1 }, { to: "B", weight: 2 }, { to: "D", weight: 8 }],
+ D: [{ to: "B", weight: 5 }, { to: "C", weight: 8 }],
+};
+const { dist, prev } = dijkstra(["A", "B", "C", "D"], adjList, "A");
+reconstructPath(prev, "A", "D"); // ["A", "C", "B", "D"]`,
+
+ python: `import math
+
+# Greedy relaxation: since all weights are non-negative, always finalizing
+# the closest unvisited vertex guarantees its distance can never improve.
+def dijkstra(vertices, adj_list, start):
+ dist = {v: math.inf for v in vertices}
+ prev = {}
+ visited = set()
+ dist[start] = 0
+
+ while len(visited) < len(vertices):
+ # Pick the unvisited vertex with the smallest tentative distance
+ u, best = None, math.inf
+ for v in vertices:
+ if v not in visited and dist[v] < best:
+ best, u = dist[v], v
+ if u is None:
+ break # remaining vertices are unreachable
+
+ visited.add(u)
+
+ for to, weight in adj_list.get(u, []):
+ if to in visited:
+ continue
+ candidate = dist[u] + weight
+ if candidate < dist[to]:
+ dist[to] = candidate # relax the edge
+ prev[to] = u
+
+ return dist, prev
+
+def reconstruct_path(prev, start, target):
+ path = []
+ current = target
+ while current is not None and current != start:
+ path.insert(0, current)
+ current = prev.get(current)
+ if current != start:
+ return None # unreachable
+ path.insert(0, start)
+ return path
+
+# Usage example
+adj_list = {
+ "A": [("B", 4), ("C", 1)],
+ "B": [("A", 4), ("C", 2), ("D", 5)],
+ "C": [("A", 1), ("B", 2), ("D", 8)],
+ "D": [("B", 5), ("C", 8)],
+}
+dist, prev = dijkstra(["A", "B", "C", "D"], adj_list, "A")
+reconstruct_path(prev, "A", "D") # ["A", "C", "B", "D"]`,
+
+ c: `#include
+#include
+#include
+
+#define MAX_V 26
+#define INF INT_MAX
+
+int graph[MAX_V][MAX_V]; // graph[i][j] = weight, or 0 for no edge
+int dist[MAX_V];
+bool visited[MAX_V];
+
+// Greedy relaxation: since all weights are non-negative, always finalizing
+// the closest unvisited vertex guarantees its distance can never improve.
+void dijkstra(int n, int start) {
+ for (int i = 0; i < n; i++) { dist[i] = INF; visited[i] = false; }
+ dist[start] = 0;
+
+ for (int count = 0; count < n; count++) {
+ int u = -1, best = INF;
+ for (int v = 0; v < n; v++) {
+ if (!visited[v] && dist[v] < best) { best = dist[v]; u = v; }
+ }
+ if (u == -1) break; // remaining vertices are unreachable
+
+ visited[u] = true;
+
+ for (int v = 0; v < n; v++) {
+ if (graph[u][v] != 0 && !visited[v] && dist[u] != INF) {
+ int candidate = dist[u] + graph[u][v];
+ if (candidate < dist[v]) dist[v] = candidate; // relax the edge
+ }
+ }
+ }
+}
+
+int main() {
+ int n = 4; // A=0, B=1, C=2, D=3
+ graph[0][1] = graph[1][0] = 4;
+ graph[0][2] = graph[2][0] = 1;
+ graph[1][2] = graph[2][1] = 2;
+ graph[1][3] = graph[3][1] = 5;
+ graph[2][3] = graph[3][2] = 8;
+
+ dijkstra(n, 0);
+ for (int i = 0; i < n; i++) printf("dist[%d] = %d\\n", i, dist[i]);
+ return 0;
+}`,
+
+ java: `import java.util.*;
+
+public class Dijkstra {
+ static class Edge {
+ char to;
+ int weight;
+ Edge(char to, int weight) { this.to = to; this.weight = weight; }
+ }
+
+ // Greedy relaxation: since all weights are non-negative, always finalizing
+ // the closest unvisited vertex guarantees its distance can never improve.
+ static Map dijkstra(List vertices, Map> adjList, char start) {
+ Map dist = new HashMap<>();
+ Set visited = new HashSet<>();
+ for (char v : vertices) dist.put(v, Integer.MAX_VALUE);
+ dist.put(start, 0);
+
+ while (visited.size() < vertices.size()) {
+ char u = 0;
+ int best = Integer.MAX_VALUE;
+ boolean found = false;
+ for (char v : vertices) {
+ if (!visited.contains(v) && dist.get(v) < best) {
+ best = dist.get(v);
+ u = v;
+ found = true;
+ }
+ }
+ if (!found) break; // remaining vertices are unreachable
+
+ visited.add(u);
+
+ for (Edge edge : adjList.getOrDefault(u, List.of())) {
+ if (visited.contains(edge.to)) continue;
+ int candidate = dist.get(u) + edge.weight;
+ if (candidate < dist.get(edge.to)) {
+ dist.put(edge.to, candidate); // relax the edge
+ }
+ }
+ }
+ return dist;
+ }
+
+ public static void main(String[] args) {
+ Map> adjList = new HashMap<>();
+ adjList.put('A', List.of(new Edge('B', 4), new Edge('C', 1)));
+ adjList.put('B', List.of(new Edge('A', 4), new Edge('C', 2), new Edge('D', 5)));
+ adjList.put('C', List.of(new Edge('A', 1), new Edge('B', 2), new Edge('D', 8)));
+ adjList.put('D', List.of(new Edge('B', 5), new Edge('C', 8)));
+
+ System.out.println(dijkstra(List.of('A', 'B', 'C', 'D'), adjList, 'A'));
+ }
+}`,
+};
+
+export default codeExamples;
diff --git a/app/visualizer/graph/algorithms/dijkstra/content.jsx b/app/visualizer/graph/algorithms/dijkstra/content.jsx
new file mode 100755
index 0000000..d40ed67
--- /dev/null
+++ b/app/visualizer/graph/algorithms/dijkstra/content.jsx
@@ -0,0 +1,218 @@
+"use client";
+import ComplexityGraph from "@/app/components/ui/graph";
+import { useTheme } from "@/app/contexts/ThemeContext";
+import DailyDSAEmbed from "@/app/components/ui/DailyDSAEmbed";
+import NewsletterEmbed from "@/app/components/ui/NewsletterEmbed";
+import InContentAd from "@/app/components/ads/InContentAd";
+import { motion } from "framer-motion";
+
+const WalkthroughDiagram = () => {
+ const vertices = [
+ { id: "A", x: 20, y: 75, dist: 0 },
+ { id: "B", x: 100, y: 20, dist: 3 },
+ { id: "C", x: 100, y: 130, dist: 1 },
+ { id: "D", x: 180, y: 75, dist: 6 },
+ ];
+ const edges = [
+ ["A", "B", 3],
+ ["A", "C", 1],
+ ["C", "B", 1],
+ ["B", "D", 3],
+ ["C", "D", 8],
+ ];
+ const byId = Object.fromEntries(vertices.map((v) => [v.id, v]));
+ const shortestEdges = new Set(["A-C", "C-B", "B-D"]);
+
+ return (
+
+ );
+};
+
+const Content = () => {
+ const { theme } = useTheme();
+
+ const paragraphs = [
+ `Dijkstra's algorithm finds the shortest-distance path from a single start vertex to every other vertex in a weighted graph — "shortest" meaning the smallest total edge weight along the path, not the fewest edges (which is what plain BFS finds on an unweighted graph). It requires every edge weight to be non-negative; a single negative weight can break its core assumption and produce wrong answers.`,
+ `The algorithm keeps a tentative distance for every vertex, starting at 0 for the source and infinity for everything else. At each step, it finalizes whichever unvisited vertex currently has the smallest tentative distance — once a vertex is finalized, its distance is guaranteed correct and will never be revised again. Then it "relaxes" every edge out of that vertex: for each neighbor, if going through the just-finalized vertex would produce a shorter distance than what's currently recorded, the neighbor's distance is updated.`,
+ `The key insight behind why picking the smallest unvisited distance is always safe: since every edge weight is non-negative, any path to that vertex through a still-unvisited (and therefore farther-or-equal) vertex could only be equal or longer. There's no way a shortcut could still be waiting to be discovered. That guarantee is exactly what breaks down if a negative edge weight is allowed — a path through a vertex that currently looks farther away could later turn out shorter, and algorithms like Bellman-Ford exist specifically to handle that case.`,
+ `Dijkstra's algorithm (typically implemented with a min-priority-queue for efficiency) is the standard tool behind GPS and mapping route-finding, network routing protocols that pick the cheapest path between routers, and any scenario where "cheapest route through a weighted network" needs an exact answer rather than an approximation.`,
+ ];
+
+ const algorithm = [
+ { points: "Set the start vertex's distance to 0, and every other vertex's distance to infinity" },
+ {
+ points: "While unvisited vertices remain, repeat:",
+ subpoints: [
+ "Pick the unvisited vertex with the smallest tentative distance and mark it finalized",
+ "For each of its neighbors, if the path through this vertex is shorter than the neighbor's current recorded distance, update it (this is a \"relaxation\")",
+ ],
+ },
+ { points: "Once every reachable vertex is finalized, each vertex's recorded distance is its true shortest distance from the start" },
+ { points: "To reconstruct the actual shortest path to any vertex, follow the chain of \"came from\" pointers recorded during relaxation, back to the start" },
+ ];
+
+ const complexity = [
+ { points: "Time Complexity: O((V + E) log V) with a binary heap priority queue — each vertex is extracted once and each edge triggers at most one relaxation, both at logarithmic cost." },
+ { points: "Space Complexity: O(V) — for the distance array, the previous-vertex pointers, and the priority queue." },
+ ];
+
+ return (
+
+
+
+
+
+
+ {/* What is it */}
+
+
+
+ What is Dijkstra's Algorithm?
+
+
+
+ {paragraphs[0]}
+
+
+
+
+ {/* How it works */}
+
+
+
+ How Does It Work?
+
+
+
+ {paragraphs[1]}
+
+
+
+
+ {paragraphs[2]}
+
+
+
+
+ Shortest distances from A — the path A→C→B→D (1+1+3=5) beats A→B→D directly (3+3=6)
+
+
+ );
+};
+
+export default KruskalVisualizer;
diff --git a/app/visualizer/graph/algorithms/kruskal/code.js b/app/visualizer/graph/algorithms/kruskal/code.js
new file mode 100755
index 0000000..f0d4d02
--- /dev/null
+++ b/app/visualizer/graph/algorithms/kruskal/code.js
@@ -0,0 +1,183 @@
+const codeExamples = {
+ javascript: `// Union-Find (disjoint set): tracks which component each vertex belongs to.
+function find(parent, v) {
+ while (parent[v] !== v) v = parent[v];
+ return v;
+}
+
+function union(parent, a, b) {
+ parent[find(parent, a)] = find(parent, b);
+}
+
+// Process edges cheapest-first; only accept an edge if its endpoints are
+// in different components -- accepting it otherwise would create a cycle.
+function kruskal(vertices, edges) {
+ const parent = {};
+ vertices.forEach((v) => (parent[v] = v));
+
+ const sorted = [...edges].sort((a, b) => a.weight - b.weight);
+ const mst = [];
+ let totalWeight = 0;
+
+ for (const edge of sorted) {
+ const rootA = find(parent, edge.from);
+ const rootB = find(parent, edge.to);
+ if (rootA !== rootB) {
+ union(parent, rootA, rootB);
+ mst.push(edge);
+ totalWeight += edge.weight;
+ }
+ }
+
+ return { mst, totalWeight };
+}
+
+// Usage example
+const vertices = ["A", "B", "C", "D"];
+const edges = [
+ { from: "A", to: "B", weight: 4 },
+ { from: "A", to: "C", weight: 1 },
+ { from: "C", to: "B", weight: 2 },
+ { from: "B", to: "D", weight: 5 },
+ { from: "C", to: "D", weight: 8 },
+];
+kruskal(vertices, edges); // mst: A-C, C-B, B-D — totalWeight: 8`,
+
+ python: `# Union-Find (disjoint set): tracks which component each vertex belongs to.
+def find(parent, v):
+ while parent[v] != v:
+ v = parent[v]
+ return v
+
+def union(parent, a, b):
+ parent[find(parent, a)] = find(parent, b)
+
+# Process edges cheapest-first; only accept an edge if its endpoints are
+# in different components -- accepting it otherwise would create a cycle.
+def kruskal(vertices, edges):
+ parent = {v: v for v in vertices}
+ sorted_edges = sorted(edges, key=lambda e: e["weight"])
+ mst = []
+ total_weight = 0
+
+ for edge in sorted_edges:
+ root_a = find(parent, edge["from"])
+ root_b = find(parent, edge["to"])
+ if root_a != root_b:
+ union(parent, root_a, root_b)
+ mst.append(edge)
+ total_weight += edge["weight"]
+
+ return mst, total_weight
+
+# Usage example
+vertices = ["A", "B", "C", "D"]
+edges = [
+ {"from": "A", "to": "B", "weight": 4},
+ {"from": "A", "to": "C", "weight": 1},
+ {"from": "C", "to": "B", "weight": 2},
+ {"from": "B", "to": "D", "weight": 5},
+ {"from": "C", "to": "D", "weight": 8},
+]
+kruskal(vertices, edges) # mst: A-C, C-B, B-D -- total_weight: 8`,
+
+ c: `#include
+#include
+
+typedef struct { int from, to, weight; } Edge;
+
+int parent[26];
+
+// Union-Find (disjoint set): tracks which component each vertex belongs to.
+int find(int v) {
+ while (parent[v] != v) v = parent[v];
+ return v;
+}
+
+void unite(int a, int b) {
+ parent[find(a)] = find(b);
+}
+
+int compareEdges(const void* a, const void* b) {
+ return ((Edge*)a)->weight - ((Edge*)b)->weight;
+}
+
+// Process edges cheapest-first; only accept an edge if its endpoints are
+// in different components -- accepting it otherwise would create a cycle.
+int kruskal(Edge edges[], int edgeCount, int vertexCount) {
+ for (int i = 0; i < vertexCount; i++) parent[i] = i;
+ qsort(edges, edgeCount, sizeof(Edge), compareEdges);
+
+ int totalWeight = 0;
+ for (int i = 0; i < edgeCount; i++) {
+ int rootA = find(edges[i].from);
+ int rootB = find(edges[i].to);
+ if (rootA != rootB) {
+ unite(rootA, rootB);
+ totalWeight += edges[i].weight;
+ printf("Accepted: %d-%d (%d)\\n", edges[i].from, edges[i].to, edges[i].weight);
+ }
+ }
+ return totalWeight;
+}
+
+int main() {
+ Edge edges[] = { {0,1,4}, {0,2,1}, {2,1,2}, {1,3,5}, {2,3,8} }; // A=0,B=1,C=2,D=3
+ printf("Total weight: %d\\n", kruskal(edges, 5, 4));
+ return 0;
+}`,
+
+ java: `import java.util.*;
+
+public class Kruskal {
+ static class Edge {
+ char from, to;
+ int weight;
+ Edge(char from, char to, int weight) { this.from = from; this.to = to; this.weight = weight; }
+ }
+
+ static Map parent = new HashMap<>();
+
+ // Union-Find (disjoint set): tracks which component each vertex belongs to.
+ static char find(char v) {
+ while (parent.get(v) != v) v = parent.get(v);
+ return v;
+ }
+
+ static void union(char a, char b) {
+ parent.put(find(a), find(b));
+ }
+
+ // Process edges cheapest-first; only accept an edge if its endpoints are
+ // in different components -- accepting it otherwise would create a cycle.
+ static List kruskal(List vertices, List edges) {
+ for (char v : vertices) parent.put(v, v);
+ edges.sort(Comparator.comparingInt(e -> e.weight));
+
+ List mst = new ArrayList<>();
+ for (Edge edge : edges) {
+ char rootA = find(edge.from);
+ char rootB = find(edge.to);
+ if (rootA != rootB) {
+ union(rootA, rootB);
+ mst.add(edge);
+ }
+ }
+ return mst;
+ }
+
+ public static void main(String[] args) {
+ List vertices = List.of('A', 'B', 'C', 'D');
+ List edges = new ArrayList<>(List.of(
+ new Edge('A', 'B', 4), new Edge('A', 'C', 1), new Edge('C', 'B', 2),
+ new Edge('B', 'D', 5), new Edge('C', 'D', 8)
+ ));
+
+ for (Edge e : kruskal(vertices, edges)) {
+ System.out.println(e.from + "-" + e.to + " (" + e.weight + ")");
+ }
+ }
+}`,
+};
+
+export default codeExamples;
diff --git a/app/visualizer/graph/algorithms/kruskal/content.jsx b/app/visualizer/graph/algorithms/kruskal/content.jsx
new file mode 100755
index 0000000..686b433
--- /dev/null
+++ b/app/visualizer/graph/algorithms/kruskal/content.jsx
@@ -0,0 +1,216 @@
+"use client";
+import ComplexityGraph from "@/app/components/ui/graph";
+import { useTheme } from "@/app/contexts/ThemeContext";
+import DailyDSAEmbed from "@/app/components/ui/DailyDSAEmbed";
+import NewsletterEmbed from "@/app/components/ui/NewsletterEmbed";
+import InContentAd from "@/app/components/ads/InContentAd";
+import { motion } from "framer-motion";
+
+const WalkthroughDiagram = () => {
+ const vertices = [
+ { id: "A", x: 20, y: 75 },
+ { id: "B", x: 100, y: 20 },
+ { id: "C", x: 100, y: 130 },
+ { id: "D", x: 180, y: 75 },
+ ];
+ const edges = [
+ ["A", "C", 1, true],
+ ["C", "B", 2, true],
+ ["A", "B", 4, false],
+ ["B", "D", 3, true],
+ ["C", "D", 8, false],
+ ];
+ const byId = Object.fromEntries(vertices.map((v) => [v.id, v]));
+
+ return (
+
+ );
+};
+
+const Content = () => {
+ const { theme } = useTheme();
+
+ const paragraphs = [
+ `A minimum spanning tree (MST) of a connected, undirected, weighted graph is a subset of its edges that connects every vertex together, contains no cycles, and has the smallest possible total edge weight among all such subsets. "Spanning" means every vertex is included; "tree" means there's exactly one path between any two vertices in it (n vertices, n-1 edges, no cycles); "minimum" means no other spanning tree costs less.`,
+ `Kruskal's algorithm builds one with a simple greedy rule: sort every edge in the graph by weight, from cheapest to most expensive, then walk through them in that order, adding each edge to the tree unless doing so would create a cycle. An edge creates a cycle exactly when its two endpoints are already connected to each other through edges already accepted — so the only real question at each step is "are these two vertices already in the same connected piece?"`,
+ `That question is answered efficiently with a Union-Find (disjoint-set) structure, which tracks which connected component each vertex currently belongs to. Checking whether two vertices are in the same component is a "find" operation; accepting an edge and merging two components is a "union" operation. Both run in close to constant time with the right implementation, which is what keeps the whole algorithm fast even though it needs one check per edge.`,
+ `Kruskal's algorithm is the standard choice when a graph is sparse (relatively few edges compared to vertices) since sorting the edge list dominates its cost. It's used to design minimum-cost networks — laying cable or pipe to connect a set of locations as cheaply as possible, building efficient road or utility networks, and as a subroutine in clustering algorithms that group data points by cutting the most expensive edges out of a spanning tree.`,
+ ];
+
+ const algorithm = [
+ { points: "Sort all edges in the graph by weight, ascending" },
+ {
+ points: "Process edges in that order, and for each one:",
+ subpoints: [
+ "If its two endpoints are in different components, accept the edge — add it to the tree and merge the two components",
+ "If its two endpoints are already in the same component, reject the edge — accepting it would create a cycle",
+ ],
+ },
+ { points: "Stop once the tree has (number of vertices − 1) edges — every vertex is now connected" },
+ ];
+
+ const complexity = [
+ { points: "Time Complexity: O(E log E) — dominated by sorting the edge list; the Union-Find operations that follow are nearly O(1) each." },
+ { points: "Space Complexity: O(V + E) — for the edge list and the Union-Find structure." },
+ ];
+
+ return (
+
+
+
+
+
+
+ {/* What is it */}
+
+
+
+ What is a Minimum Spanning Tree?
+
+
+
+ {paragraphs[0]}
+
+
+
+
+ {/* How it works */}
+
+
+
+ How Does Kruskal's Algorithm Work?
+
+
+
+ {paragraphs[1]}
+
+
+
+
+ {paragraphs[2]}
+
+
+
+
+ Cheapest edges are taken first; A-B and C-D are rejected since they'd close a cycle
+
+
+
+ Outside the tree
+
+
+
+ In the tree
+
+
+
+ Just pulled in / key updated
+
+
+
+
+
+ );
+};
+
+export default PrimVisualizer;
diff --git a/app/visualizer/graph/algorithms/prim/code.js b/app/visualizer/graph/algorithms/prim/code.js
new file mode 100755
index 0000000..f20c10d
--- /dev/null
+++ b/app/visualizer/graph/algorithms/prim/code.js
@@ -0,0 +1,202 @@
+const codeExamples = {
+ javascript: `// "key" is the weight of the cheapest edge discovered so far connecting a
+// vertex directly to the tree -- not a cumulative distance like Dijkstra's.
+function prim(vertices, adjList, start) {
+ const key = {};
+ const parent = {};
+ const inTree = new Set();
+ vertices.forEach((v) => (key[v] = Infinity));
+ key[start] = 0;
+
+ while (inTree.size < vertices.length) {
+ // Pick the outside vertex with the smallest key
+ let u = null;
+ let best = Infinity;
+ for (const v of vertices) {
+ if (!inTree.has(v) && key[v] < best) {
+ best = key[v];
+ u = v;
+ }
+ }
+ if (u === null) break; // remaining vertices are unreachable
+
+ inTree.add(u);
+
+ for (const { to, weight } of adjList[u] || []) {
+ if (!inTree.has(to) && weight < key[to]) {
+ key[to] = weight; // a cheaper direct connection was just found
+ parent[to] = u;
+ }
+ }
+ }
+
+ const mstEdges = [];
+ vertices.forEach((v) => {
+ if (parent[v] !== undefined) mstEdges.push({ from: parent[v], to: v, weight: key[v] });
+ });
+ return mstEdges;
+}
+
+// Usage example
+const adjList = {
+ A: [{ to: "B", weight: 4 }, { to: "C", weight: 1 }],
+ B: [{ to: "A", weight: 4 }, { to: "C", weight: 2 }, { to: "D", weight: 5 }],
+ C: [{ to: "A", weight: 1 }, { to: "B", weight: 2 }, { to: "D", weight: 8 }],
+ D: [{ to: "B", weight: 5 }, { to: "C", weight: 8 }],
+};
+prim(["A", "B", "C", "D"], adjList, "A"); // A-C, C-B, B-D`,
+
+ python: `import math
+
+# "key" is the weight of the cheapest edge discovered so far connecting a
+# vertex directly to the tree -- not a cumulative distance like Dijkstra's.
+def prim(vertices, adj_list, start):
+ key = {v: math.inf for v in vertices}
+ parent = {}
+ in_tree = set()
+ key[start] = 0
+
+ while len(in_tree) < len(vertices):
+ # Pick the outside vertex with the smallest key
+ u, best = None, math.inf
+ for v in vertices:
+ if v not in in_tree and key[v] < best:
+ best, u = key[v], v
+ if u is None:
+ break # remaining vertices are unreachable
+
+ in_tree.add(u)
+
+ for to, weight in adj_list.get(u, []):
+ if to not in in_tree and weight < key[to]:
+ key[to] = weight # a cheaper direct connection was just found
+ parent[to] = u
+
+ mst_edges = []
+ for v in vertices:
+ if v in parent:
+ mst_edges.append((parent[v], v, key[v]))
+ return mst_edges
+
+# Usage example
+adj_list = {
+ "A": [("B", 4), ("C", 1)],
+ "B": [("A", 4), ("C", 2), ("D", 5)],
+ "C": [("A", 1), ("B", 2), ("D", 8)],
+ "D": [("B", 5), ("C", 8)],
+}
+prim(["A", "B", "C", "D"], adj_list, "A") # A-C, C-B, B-D`,
+
+ c: `#include
+#include
+#include
+
+#define MAX_V 26
+#define INF INT_MAX
+
+int graph[MAX_V][MAX_V]; // graph[i][j] = weight, or 0 for no edge
+int key_[MAX_V];
+int parent_[MAX_V];
+bool inTree[MAX_V];
+
+// "key" is the weight of the cheapest edge discovered so far connecting a
+// vertex directly to the tree -- not a cumulative distance like Dijkstra's.
+void prim(int n, int start) {
+ for (int i = 0; i < n; i++) { key_[i] = INF; inTree[i] = false; }
+ key_[start] = 0;
+ parent_[start] = -1;
+
+ for (int count = 0; count < n; count++) {
+ int u = -1, best = INF;
+ for (int v = 0; v < n; v++) {
+ if (!inTree[v] && key_[v] < best) { best = key_[v]; u = v; }
+ }
+ if (u == -1) break; // remaining vertices are unreachable
+
+ inTree[u] = true;
+
+ for (int v = 0; v < n; v++) {
+ if (graph[u][v] != 0 && !inTree[v] && graph[u][v] < key_[v]) {
+ key_[v] = graph[u][v]; // a cheaper direct connection was just found
+ parent_[v] = u;
+ }
+ }
+ }
+}
+
+int main() {
+ int n = 4; // A=0, B=1, C=2, D=3
+ graph[0][1] = graph[1][0] = 4;
+ graph[0][2] = graph[2][0] = 1;
+ graph[1][2] = graph[2][1] = 2;
+ graph[1][3] = graph[3][1] = 5;
+ graph[2][3] = graph[3][2] = 8;
+
+ prim(n, 0);
+ for (int v = 1; v < n; v++) printf("%d-%d (%d)\\n", parent_[v], v, key_[v]);
+ return 0;
+}`,
+
+ java: `import java.util.*;
+
+public class Prim {
+ static class Edge {
+ char to;
+ int weight;
+ Edge(char to, int weight) { this.to = to; this.weight = weight; }
+ }
+
+ // "key" is the weight of the cheapest edge discovered so far connecting a
+ // vertex directly to the tree -- not a cumulative distance like Dijkstra's.
+ static List prim(List vertices, Map> adjList, char start) {
+ Map key = new HashMap<>();
+ Map parent = new HashMap<>();
+ Set inTree = new HashSet<>();
+ for (char v : vertices) key.put(v, Integer.MAX_VALUE);
+ key.put(start, 0);
+
+ while (inTree.size() < vertices.size()) {
+ char u = 0;
+ int best = Integer.MAX_VALUE;
+ boolean found = false;
+ for (char v : vertices) {
+ if (!inTree.contains(v) && key.get(v) < best) {
+ best = key.get(v);
+ u = v;
+ found = true;
+ }
+ }
+ if (!found) break; // remaining vertices are unreachable
+
+ inTree.add(u);
+
+ for (Edge edge : adjList.getOrDefault(u, List.of())) {
+ if (!inTree.contains(edge.to) && edge.weight < key.get(edge.to)) {
+ key.put(edge.to, edge.weight); // a cheaper direct connection was just found
+ parent.put(edge.to, u);
+ }
+ }
+ }
+
+ List mstEdges = new ArrayList<>();
+ for (char v : vertices) {
+ if (parent.containsKey(v)) {
+ mstEdges.add(new int[]{ parent.get(v), v, key.get(v) });
+ }
+ }
+ return mstEdges;
+ }
+
+ public static void main(String[] args) {
+ Map> adjList = new HashMap<>();
+ adjList.put('A', List.of(new Edge('B', 4), new Edge('C', 1)));
+ adjList.put('B', List.of(new Edge('A', 4), new Edge('C', 2), new Edge('D', 5)));
+ adjList.put('C', List.of(new Edge('A', 1), new Edge('B', 2), new Edge('D', 8)));
+ adjList.put('D', List.of(new Edge('B', 5), new Edge('C', 8)));
+
+ System.out.println(prim(List.of('A', 'B', 'C', 'D'), adjList, 'A').size() + " edges in MST");
+ }
+}`,
+};
+
+export default codeExamples;
diff --git a/app/visualizer/graph/algorithms/prim/content.jsx b/app/visualizer/graph/algorithms/prim/content.jsx
new file mode 100755
index 0000000..b944ad1
--- /dev/null
+++ b/app/visualizer/graph/algorithms/prim/content.jsx
@@ -0,0 +1,219 @@
+"use client";
+import ComplexityGraph from "@/app/components/ui/graph";
+import { useTheme } from "@/app/contexts/ThemeContext";
+import DailyDSAEmbed from "@/app/components/ui/DailyDSAEmbed";
+import NewsletterEmbed from "@/app/components/ui/NewsletterEmbed";
+import InContentAd from "@/app/components/ads/InContentAd";
+import { motion } from "framer-motion";
+
+const WalkthroughDiagram = () => {
+ const vertices = [
+ { id: "A", x: 20, y: 75, order: 1 },
+ { id: "C", x: 100, y: 130, order: 2 },
+ { id: "B", x: 100, y: 20, order: 3 },
+ { id: "D", x: 180, y: 75, order: 4 },
+ ];
+ const edges = [
+ ["A", "C", 1, true],
+ ["C", "B", 2, true],
+ ["A", "B", 4, false],
+ ["B", "D", 5, true],
+ ["C", "D", 8, false],
+ ];
+ const byId = Object.fromEntries(vertices.map((v) => [v.id, v]));
+
+ return (
+
+ );
+};
+
+const Content = () => {
+ const { theme } = useTheme();
+
+ const paragraphs = [
+ `Prim's algorithm builds a minimum spanning tree the same way Kruskal's does — greedily, ending up with the cheapest possible set of edges connecting every vertex with no cycles — but it grows outward from a single starting vertex instead of considering edges from the whole graph in sorted order. At every step, the tree adds whichever edge is cheapest among all the edges connecting the current tree to a vertex not yet in it.`,
+ `Rather than sorting the whole edge list up front, Prim's algorithm tracks a "key" value for every vertex outside the tree: the weight of the cheapest edge discovered so far connecting it directly to the tree (infinity if none is known yet). At each step, the algorithm pulls in whichever outside vertex has the smallest key, adds the edge that earned it that key, and then checks whether any of its edges give some other outside vertex an even cheaper way into the (now larger) tree.`,
+ `This "key" is deliberately different from Dijkstra's "distance": Dijkstra's distance is the cumulative weight of the entire path from the start, while Prim's key is just the weight of one direct edge into the tree, regardless of how far the tree has traveled to get there. That's exactly why Prim's algorithm finds a minimum spanning tree — cheapest total connections — while Dijkstra finds shortest paths — cheapest cumulative routes. They look almost identical in code, but they're solving genuinely different problems.`,
+ `Prim's algorithm tends to be the better choice on dense graphs (many edges relative to vertices), since it never needs to sort the full edge list the way Kruskal's does — with a good priority queue it can outperform Kruskal's as edge count grows. Like Kruskal's, it's used for minimum-cost network design: wiring, piping, or cabling a set of locations together as cheaply as possible.`,
+ ];
+
+ const algorithm = [
+ { points: "Set the start vertex's key to 0, and every other vertex's key to infinity" },
+ {
+ points: "While vertices remain outside the tree, repeat:",
+ subpoints: [
+ "Pull in whichever outside vertex currently has the smallest key, and add the edge that produced that key to the tree",
+ "For each of its edges to a still-outside vertex, if that edge is cheaper than the outside vertex's current key, update the key",
+ ],
+ },
+ { points: "Once every reachable vertex is in the tree, the accepted edges form the minimum spanning tree" },
+ ];
+
+ const complexity = [
+ { points: "Time Complexity: O((V + E) log V) with a binary heap priority queue — comparable to Dijkstra's, and often faster than Kruskal's on dense graphs." },
+ { points: "Space Complexity: O(V) — for the key array, the parent pointers, and the priority queue." },
+ ];
+
+ return (
+
+
+
+
+
+
+ {/* What is it */}
+
+
+
+ What is Prim's Algorithm?
+
+
+
+ {paragraphs[0]}
+
+
+
+
+ {/* How it works */}
+
+
+
+ How Does It Work?
+
+
+
+ {paragraphs[1]}
+
+
+
+
+ {paragraphs[2]}
+
+
+
+
+ Starting from A, the tree grows one cheapest-edge step at a time
+
+
+
+
+
+
+ );
+};
+
+export default Content;
diff --git a/app/visualizer/graph/algorithms/prim/page.jsx b/app/visualizer/graph/algorithms/prim/page.jsx
new file mode 100755
index 0000000..a6fc737
--- /dev/null
+++ b/app/visualizer/graph/algorithms/prim/page.jsx
@@ -0,0 +1,113 @@
+import Animation from "@/app/visualizer/graph/algorithms/prim/animation";
+import Navbar from "@/app/components/navbarinner";
+import ModuleHeader from "@/app/components/modules/Header";
+import Footer from "@/app/components/footer";
+import BackToTop from "@/app/components/ui/backtotop";
+import ExploreOther from "@/app/components/ui/exploreOther";
+import CodeBlock from "@/app/components/modules/CodeBlock";
+import codeExamples from "./code";
+import Quiz from "@/app/visualizer/graph/algorithms/prim/quiz";
+import Content from "@/app/visualizer/graph/algorithms/prim/content";
+import ModuleCard from "@/app/components/ui/ModuleCard";
+import { MODULE_MAPS } from "@/lib/modulesMap";
+
+export const metadata = {
+ title: "Prim's Algorithm | Animation and Explanation",
+ description:
+ "Learn how Prim's algorithm builds a minimum spanning tree by growing outward from a start vertex, always pulling in the cheapest edge to a new vertex, with an interactive visualizer, code examples in JavaScript, C, Python, and Java, and a quiz.",
+ keywords: [
+ "Prim's Algorithm",
+ "Prim Algorithm",
+ "Prim's Algorithm Visualization",
+ "Minimum Spanning Tree",
+ "MST Algorithm",
+ "Prim vs Kruskal",
+ "Greedy Graph Algorithms",
+ "Prim's Algorithm in JavaScript",
+ "Prim Algorithm in JavaScript",
+ "Prim's Algorithm in C",
+ "Prim Algorithm in C",
+ "Prim's Algorithm in Python",
+ "Prim Algorithm in Python",
+ "Prim's Algorithm in Java",
+ "Prim Algorithm in Java",
+ "Graph Algorithms",
+ "DSA Graphs",
+ "Learn Graphs",
+ "Graph Quiz",
+ ],
+ robots: "index, follow",
+ openGraph: {
+ images: [
+ {
+ url: "/og.png",
+ width: 1200,
+ height: 630,
+ alt: "Prim's Algorithm Visualization",
+ },
+ ],
+ },
+};
+
+export default function Page() {
+ const paths = [
+ { name: "Home", href: "/" },
+ { name: "Visualizer", href: "/visualizer" },
+ { name: "Graph : Prim's Algorithm", href: "" },
+ ];
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Test Your Knowledge before moving forward!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/app/visualizer/graph/algorithms/prim/quiz.jsx b/app/visualizer/graph/algorithms/prim/quiz.jsx
new file mode 100755
index 0000000..33c2a2b
--- /dev/null
+++ b/app/visualizer/graph/algorithms/prim/quiz.jsx
@@ -0,0 +1,384 @@
+"use client";
+import React, { useState } from 'react';
+import { FaCheck, FaTimes, FaArrowRight, FaArrowLeft, FaInfoCircle, FaRedo, FaTrophy, FaStar, FaAward } from 'react-icons/fa';
+import { motion, AnimatePresence } from 'framer-motion';
+
+const PrimQuiz = () => {
+ const questions = [
+ {
+ question: "How does Prim's algorithm grow its minimum spanning tree?",
+ options: [
+ "By sorting every edge in the graph up front and processing them in order",
+ "Outward from a single starting vertex, always adding the cheapest edge connecting the current tree to a new vertex",
+ "By visiting vertices in alphabetical order",
+ "By removing the most expensive edges one at a time"
+ ],
+ correctAnswer: 1,
+ explanation: "Unlike Kruskal's global edge-sorting approach, Prim's algorithm expands one tree outward, one cheapest-connection step at a time."
+ },
+ {
+ question: "What does a vertex's \"key\" value represent in Prim's algorithm?",
+ options: [
+ "The cumulative distance from the start vertex, like in Dijkstra's algorithm",
+ "The weight of the cheapest edge discovered so far connecting it directly to the current tree",
+ "Its alphabetical position among the vertices",
+ "The total number of edges it has"
+ ],
+ correctAnswer: 1,
+ explanation: "The key is just one edge's weight — the best direct connection into the tree found so far — not a running total like Dijkstra's distance."
+ },
+ {
+ question: "What is the key difference between Prim's \"key\" and Dijkstra's \"distance\", even though the algorithms look very similar?",
+ options: [
+ "There is no real difference — they compute exactly the same thing",
+ "Prim's key is the weight of one direct edge into the tree; Dijkstra's distance is the cumulative weight of the whole path from the start",
+ "Dijkstra's algorithm doesn't use a key or distance value at all",
+ "Prim's algorithm only works on unweighted graphs"
+ ],
+ correctAnswer: 1,
+ explanation: "That distinction is exactly why Prim's finds a minimum spanning tree (cheapest total connections) while Dijkstra's finds shortest paths (cheapest cumulative routes)."
+ },
+ {
+ question: "When is a vertex's key value updated during the algorithm?",
+ options: [
+ "Never — keys are fixed once set",
+ "Whenever an edge from a vertex just added to the tree offers a cheaper direct connection than the vertex's current key",
+ "Only at the very start of the algorithm",
+ "Every time any edge in the graph is examined, regardless of relevance"
+ ],
+ correctAnswer: 1,
+ explanation: "After pulling a vertex into the tree, its edges are checked — if one offers some other outside vertex a cheaper way in than it currently has, that vertex's key improves."
+ },
+ {
+ question: "On which kind of graph does Prim's algorithm tend to outperform Kruskal's?",
+ options: [
+ "Sparse graphs with very few edges",
+ "Dense graphs, since Prim's avoids the cost of sorting the entire edge list up front",
+ "Graphs with no edges at all",
+ "Graphs with negative edge weights"
+ ],
+ correctAnswer: 1,
+ explanation: "Kruskal's pays an O(E log E) sorting cost regardless of structure; on dense graphs, Prim's priority-queue approach can edge ahead."
+ }
+ ];
+
+ const [currentQuestion, setCurrentQuestion] = useState(0);
+ const [selectedAnswer, setSelectedAnswer] = useState(null);
+ const [score, setScore] = useState(0);
+ const [showResult, setShowResult] = useState(false);
+ const [quizCompleted, setQuizCompleted] = useState(false);
+ const [answers, setAnswers] = useState(Array(questions.length).fill(null));
+ const [showIntro, setShowIntro] = useState(true);
+ const [showSuccessAnimation, setShowSuccessAnimation] = useState(false);
+
+ const handleAnswerSelect = (optionIndex) => {
+ setSelectedAnswer(optionIndex);
+ const newAnswers = [...answers];
+ newAnswers[currentQuestion] = optionIndex;
+ setAnswers(newAnswers);
+ };
+
+ const handleNextQuestion = () => {
+ if (selectedAnswer === null) return;
+
+ if (selectedAnswer === questions[currentQuestion].correctAnswer) {
+ setScore(score + 1);
+ }
+ if (currentQuestion < questions.length - 1) {
+ setCurrentQuestion(currentQuestion + 1);
+ setSelectedAnswer(null);
+ } else {
+ setShowSuccessAnimation(true);
+ setTimeout(() => {
+ setShowSuccessAnimation(false);
+ setQuizCompleted(true);
+ setShowResult(true);
+ }, 2000);
+ }
+ };
+
+ const handlePreviousQuestion = () => {
+ setCurrentQuestion(currentQuestion - 1);
+ setSelectedAnswer(answers[currentQuestion - 1]);
+ };
+
+ const resetQuiz = () => {
+ setCurrentQuestion(0);
+ setSelectedAnswer(null);
+ setScore(0);
+ setShowResult(false);
+ setQuizCompleted(false);
+ setAnswers(Array(questions.length).fill(null));
+ setShowIntro(true);
+ };
+
+ const calculateWeakAreas = () => {
+ const weakAreas = [];
+ if (answers[0] !== questions[0].correctAnswer) {
+ weakAreas.push("how Prim's algorithm grows the tree");
+ }
+ if (answers[1] !== questions[1].correctAnswer) {
+ weakAreas.push("what the key value represents");
+ }
+ if (answers[2] !== questions[2].correctAnswer) {
+ weakAreas.push("the key-vs-distance distinction from Dijkstra's");
+ }
+ if (answers[3] !== questions[3].correctAnswer) {
+ weakAreas.push("when key values get updated");
+ }
+ if (answers[4] !== questions[4].correctAnswer) {
+ weakAreas.push("when Prim's outperforms Kruskal's");
+ }
+
+ return weakAreas.length > 0
+ ? `Focus on improving: ${weakAreas.join(', ')}. Review the corresponding sections above.`
+ : "Perfect! You've mastered Prim's Algorithm!";
+ };
+
+ const startQuiz = () => {
+ setShowIntro(false);
+ };
+
+ const getStarRating = () => {
+ const percentage = (score / questions.length) * 100;
+ if (percentage >= 90) return 5;
+ if (percentage >= 70) return 4;
+ if (percentage >= 50) return 3;
+ if (percentage >= 30) return 2;
+ return 1;
+ };
+
+ return (
+
+ {showIntro ? (
+
+
+
+
+
+
+
+ Prim's Algorithm Quiz
+
+
+
+ How it works:
+
+
+
+
+ +1 point for each correct answer
+
+
+
+ 0 points for wrong answers
+
+
+
+ -0.5 point penalty for viewing explanations
+
+
+
+ Earn stars based on your final score (max 5 stars)
+
+
+
+ Unprocessed
+
+
+
+ Placed in order
+
+
+
+ Currently processed
+
+
+
+ Stuck in a cycle
+
+
+
+
+ {/* Order */}
+ {order.length > 0 && (
+
+
Topological Order
+
+ {order.map((v, i) => (
+
+
{v}
+ {i < order.length - 1 && →}
+
+ ))}
+
+
+ )}
+
+
+ );
+};
+
+export default TopologicalSortVisualizer;
diff --git a/app/visualizer/graph/algorithms/topological-sort/code.js b/app/visualizer/graph/algorithms/topological-sort/code.js
new file mode 100755
index 0000000..e818c63
--- /dev/null
+++ b/app/visualizer/graph/algorithms/topological-sort/code.js
@@ -0,0 +1,156 @@
+const codeExamples = {
+ javascript: `// Kahn's algorithm: repeatedly process a vertex with no remaining
+// prerequisites (in-degree 0), then decrement its neighbors' in-degrees --
+// which may free them up to be processed too.
+function topologicalSort(vertices, adjList) {
+ const inDegree = {};
+ vertices.forEach((v) => (inDegree[v] = 0));
+ vertices.forEach((v) => (adjList[v] || []).forEach((to) => (inDegree[to] += 1)));
+
+ const queue = vertices.filter((v) => inDegree[v] === 0);
+ const order = [];
+
+ while (queue.length > 0) {
+ const u = queue.shift();
+ order.push(u);
+
+ for (const v of adjList[u] || []) {
+ inDegree[v] -= 1;
+ if (inDegree[v] === 0) queue.push(v);
+ }
+ }
+
+ if (order.length < vertices.length) {
+ throw new Error("Graph contains a cycle -- no valid topological order exists");
+ }
+ return order;
+}
+
+// Usage example
+const adjList = { A: ["B", "C"], B: ["D"], C: ["D"], D: ["E"], E: [] };
+topologicalSort(["A", "B", "C", "D", "E"], adjList); // ["A", "B", "C", "D", "E"]`,
+
+ python: `from collections import deque
+
+# Kahn's algorithm: repeatedly process a vertex with no remaining
+# prerequisites (in-degree 0), then decrement its neighbors' in-degrees --
+# which may free them up to be processed too.
+def topological_sort(vertices, adj_list):
+ in_degree = {v: 0 for v in vertices}
+ for v in vertices:
+ for to in adj_list.get(v, []):
+ in_degree[to] += 1
+
+ queue = deque(v for v in vertices if in_degree[v] == 0)
+ order = []
+
+ while queue:
+ u = queue.popleft()
+ order.append(u)
+
+ for v in adj_list.get(u, []):
+ in_degree[v] -= 1
+ if in_degree[v] == 0:
+ queue.append(v)
+
+ if len(order) < len(vertices):
+ raise ValueError("Graph contains a cycle -- no valid topological order exists")
+ return order
+
+# Usage example
+adj_list = {"A": ["B", "C"], "B": ["D"], "C": ["D"], "D": ["E"], "E": []}
+topological_sort(["A", "B", "C", "D", "E"], adj_list) # ["A", "B", "C", "D", "E"]`,
+
+ c: `#include
+
+#define MAX_V 26
+
+char adjList[MAX_V][MAX_V];
+int adjCount[MAX_V] = {0};
+int inDegree[MAX_V] = {0};
+
+// Kahn's algorithm: repeatedly process a vertex with no remaining
+// prerequisites (in-degree 0), then decrement its neighbors' in-degrees --
+// which may free them up to be processed too.
+int topologicalSort(int n, char* order) {
+ char queue[MAX_V];
+ int front = 0, back = 0, count = 0;
+
+ for (int i = 0; i < n; i++) {
+ if (inDegree[i] == 0) queue[back++] = 'A' + i;
+ }
+
+ while (front < back) {
+ char u = queue[front++];
+ order[count++] = u;
+
+ for (int i = 0; i < adjCount[u - 'A']; i++) {
+ char v = adjList[u - 'A'][i];
+ if (--inDegree[v - 'A'] == 0) queue[back++] = v;
+ }
+ }
+
+ return count; // if count < n, the graph contains a cycle
+}
+
+int main() {
+ // Example: A->B, A->C, B->D, C->D, D->E
+ adjList[0][0]='B'; adjList[0][1]='C'; adjCount[0]=2; inDegree[1]++; inDegree[2]++;
+ adjList[1][0]='D'; adjCount[1]=1; inDegree[3]++;
+ adjList[2][0]='D'; adjCount[2]=1; inDegree[3]++;
+ adjList[3][0]='E'; adjCount[3]=1; inDegree[4]++;
+
+ char order[MAX_V];
+ int count = topologicalSort(5, order);
+ for (int i = 0; i < count; i++) printf("%c ", order[i]); // A B C D E (order may vary)
+ return 0;
+}`,
+
+ java: `import java.util.*;
+
+public class TopologicalSort {
+ // Kahn's algorithm: repeatedly process a vertex with no remaining
+ // prerequisites (in-degree 0), then decrement its neighbors' in-degrees --
+ // which may free them up to be processed too.
+ static List topologicalSort(List vertices, Map> adjList) {
+ Map inDegree = new HashMap<>();
+ for (char v : vertices) inDegree.put(v, 0);
+ for (char v : vertices) {
+ for (char to : adjList.getOrDefault(v, List.of())) {
+ inDegree.merge(to, 1, Integer::sum);
+ }
+ }
+
+ Queue queue = new LinkedList<>();
+ for (char v : vertices) if (inDegree.get(v) == 0) queue.add(v);
+
+ List order = new ArrayList<>();
+ while (!queue.isEmpty()) {
+ char u = queue.poll();
+ order.add(u);
+
+ for (char v : adjList.getOrDefault(u, List.of())) {
+ inDegree.put(v, inDegree.get(v) - 1);
+ if (inDegree.get(v) == 0) queue.add(v);
+ }
+ }
+
+ if (order.size() < vertices.size()) {
+ throw new IllegalStateException("Graph contains a cycle -- no valid topological order exists");
+ }
+ return order;
+ }
+
+ public static void main(String[] args) {
+ Map> adjList = new HashMap<>();
+ adjList.put('A', List.of('B', 'C'));
+ adjList.put('B', List.of('D'));
+ adjList.put('C', List.of('D'));
+ adjList.put('D', List.of('E'));
+
+ System.out.println(topologicalSort(List.of('A', 'B', 'C', 'D', 'E'), adjList));
+ }
+}`,
+};
+
+export default codeExamples;
diff --git a/app/visualizer/graph/algorithms/topological-sort/content.jsx b/app/visualizer/graph/algorithms/topological-sort/content.jsx
new file mode 100755
index 0000000..13c12f4
--- /dev/null
+++ b/app/visualizer/graph/algorithms/topological-sort/content.jsx
@@ -0,0 +1,212 @@
+"use client";
+import ComplexityGraph from "@/app/components/ui/graph";
+import { useTheme } from "@/app/contexts/ThemeContext";
+import DailyDSAEmbed from "@/app/components/ui/DailyDSAEmbed";
+import NewsletterEmbed from "@/app/components/ui/NewsletterEmbed";
+import InContentAd from "@/app/components/ads/InContentAd";
+import { motion } from "framer-motion";
+
+const WalkthroughDiagram = () => {
+ const vertices = [
+ { id: "Shirt", x: 30, y: 30, order: 1 },
+ { id: "Belt", x: 170, y: 30, order: 3 },
+ { id: "Pants", x: 30, y: 90, order: 2 },
+ { id: "Shoes", x: 170, y: 90, order: 4 },
+ ];
+ const edges = [
+ ["Shirt", "Belt"],
+ ["Pants", "Belt"],
+ ["Pants", "Shoes"],
+ ];
+ const byId = Object.fromEntries(vertices.map((v) => [v.id, v]));
+
+ return (
+
+ );
+};
+
+const Content = () => {
+ const { theme } = useTheme();
+
+ const paragraphs = [
+ `A topological sort takes a directed acyclic graph (a DAG — directed edges, no cycles) and arranges every vertex into a linear order such that, for every edge u → v, u appears before v in that order. Think of each edge as a "must come before" constraint: topological sort finds an order that satisfies every constraint at once. Multiple valid orders can exist for the same graph — the algorithm just needs to find one of them.`,
+ `Kahn's algorithm builds the order using each vertex's in-degree — the number of edges pointing into it, which represents "how many prerequisites are left." Any vertex with in-degree 0 has no unmet prerequisites, so it's safe to place next in the order right away. Whenever a vertex is placed, its outgoing edges are "removed" by decrementing the in-degree of everything it points to — which may free up new vertices to become in-degree 0 and get queued themselves.`,
+ `That queue-driven process is exactly why the algorithm doubles as a cycle detector: if the graph really is acyclic, every vertex eventually reaches in-degree 0 and gets processed. But if a cycle exists, every vertex in that cycle keeps at least one unmet prerequisite forever — none of them can ever reach in-degree 0, so they're never queued. If the final order doesn't include every vertex, the graph must contain a cycle, and no valid topological order exists at all.`,
+ `Topological sort is the standard tool for scheduling problems with dependencies: build systems compiling files in the right order, package managers installing dependencies before the packages that need them, course prerequisite planning, and task schedulers in project management tools that need to respect "this must finish before that starts" constraints.`,
+ ];
+
+ const algorithm = [
+ { points: "Compute the in-degree (number of incoming edges) for every vertex" },
+ { points: "Initialize a queue with every vertex that already has in-degree 0" },
+ {
+ points: "While the queue isn't empty, repeat:",
+ subpoints: [
+ "Dequeue a vertex and place it next in the output order",
+ "For each of its outgoing edges, decrement the target vertex's in-degree",
+ "If a target vertex's in-degree just reached 0, enqueue it",
+ ],
+ },
+ { points: "If the final order includes every vertex, it's a valid topological order; if not, the graph contains a cycle" },
+ ];
+
+ const complexity = [
+ { points: "Time Complexity: O(V + E) — every vertex is enqueued once and every edge is examined once during in-degree updates." },
+ { points: "Space Complexity: O(V) — for the in-degree array and the queue." },
+ ];
+
+ return (
+
+
+
+
+
+
+ {/* What is it */}
+
+
+
+ What is Topological Sort?
+
+
+
+ {paragraphs[0]}
+
+
+
+
+ {/* How it works */}
+
+
+
+ How Does Kahn's Algorithm Work?
+
+
+
+ {paragraphs[1]}
+
+
+
+
+ {paragraphs[2]}
+
+
+
+
+ Getting dressed: Shirt and Pants have no prerequisites, so either can go first
+
+ n}
+ averageCase={(n) => n + n}
+ worstCase={(n) => n + n}
+ maxN={25}
+ />
+
+
+
+
+
+ {/* Additional Info */}
+
+
+
+
+ {paragraphs[3]}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Content;
diff --git a/app/visualizer/graph/algorithms/topological-sort/page.jsx b/app/visualizer/graph/algorithms/topological-sort/page.jsx
new file mode 100755
index 0000000..869ccea
--- /dev/null
+++ b/app/visualizer/graph/algorithms/topological-sort/page.jsx
@@ -0,0 +1,113 @@
+import Animation from "@/app/visualizer/graph/algorithms/topological-sort/animation";
+import Navbar from "@/app/components/navbarinner";
+import ModuleHeader from "@/app/components/modules/Header";
+import Footer from "@/app/components/footer";
+import BackToTop from "@/app/components/ui/backtotop";
+import ExploreOther from "@/app/components/ui/exploreOther";
+import CodeBlock from "@/app/components/modules/CodeBlock";
+import codeExamples from "./code";
+import Quiz from "@/app/visualizer/graph/algorithms/topological-sort/quiz";
+import Content from "@/app/visualizer/graph/algorithms/topological-sort/content";
+import ModuleCard from "@/app/components/ui/ModuleCard";
+import { MODULE_MAPS } from "@/lib/modulesMap";
+
+export const metadata = {
+ title: "Topological Sort | Animation and Explanation",
+ description:
+ "Learn how Topological Sort orders a directed acyclic graph's vertices so every dependency comes before what depends on it, using Kahn's in-degree algorithm, with an interactive visualizer, code examples in JavaScript, C, Python, and Java, and a quiz.",
+ keywords: [
+ "Topological Sort",
+ "Topological Sort Algorithm",
+ "Topological Sort Visualization",
+ "Kahn's Algorithm",
+ "Directed Acyclic Graph",
+ "DAG",
+ "Dependency Resolution Algorithm",
+ "Cycle Detection Graph",
+ "Topological Sort in JavaScript",
+ "Kahn's Algorithm in JavaScript",
+ "Topological Sort in C",
+ "Kahn's Algorithm in C",
+ "Topological Sort in Python",
+ "Kahn's Algorithm in Python",
+ "Topological Sort in Java",
+ "Kahn's Algorithm in Java",
+ "Graph Algorithms",
+ "DSA Graphs",
+ "Learn Graphs",
+ "Graph Quiz",
+ ],
+ robots: "index, follow",
+ openGraph: {
+ images: [
+ {
+ url: "/og.png",
+ width: 1200,
+ height: 630,
+ alt: "Topological Sort Visualization",
+ },
+ ],
+ },
+};
+
+export default function Page() {
+ const paths = [
+ { name: "Home", href: "/" },
+ { name: "Visualizer", href: "/visualizer" },
+ { name: "Graph : Topological Sort", href: "" },
+ ];
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Test Your Knowledge before moving forward!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/app/visualizer/graph/algorithms/topological-sort/quiz.jsx b/app/visualizer/graph/algorithms/topological-sort/quiz.jsx
new file mode 100755
index 0000000..682c2e4
--- /dev/null
+++ b/app/visualizer/graph/algorithms/topological-sort/quiz.jsx
@@ -0,0 +1,384 @@
+"use client";
+import React, { useState } from 'react';
+import { FaCheck, FaTimes, FaArrowRight, FaArrowLeft, FaInfoCircle, FaRedo, FaTrophy, FaStar, FaAward } from 'react-icons/fa';
+import { motion, AnimatePresence } from 'framer-motion';
+
+const TopologicalSortQuiz = () => {
+ const questions = [
+ {
+ question: "What does a topological sort produce?",
+ options: [
+ "A linear order of a graph's vertices where every edge u → v has u appearing before v",
+ "The shortest path between two vertices",
+ "A sorted list of edge weights",
+ "The number of connected components"
+ ],
+ correctAnswer: 0,
+ explanation: "Each directed edge is a \"must come before\" constraint, and a topological sort finds a vertex order satisfying every such constraint at once."
+ },
+ {
+ question: "What kind of graph can be topologically sorted?",
+ options: [
+ "Any undirected graph",
+ "A directed acyclic graph (a DAG) — directed edges with no cycles",
+ "Only graphs with exactly one vertex",
+ "Any weighted graph"
+ ],
+ correctAnswer: 1,
+ explanation: "A cycle would require some vertex to come both before and after another, which is impossible to satisfy in a linear order — so cycles rule out a valid topological sort entirely."
+ },
+ {
+ question: "In Kahn's algorithm, what does a vertex's in-degree represent, and why does in-degree 0 matter?",
+ options: [
+ "The number of outgoing edges; 0 means it has no effect on other vertices",
+ "The number of incoming edges (unmet prerequisites); 0 means it has none left and is safe to place next",
+ "Its position in the alphabet",
+ "The total number of edges in the whole graph"
+ ],
+ correctAnswer: 1,
+ explanation: "In-degree tracks how many prerequisite edges still point into a vertex; once that count hits 0, nothing is blocking it from being placed."
+ },
+ {
+ question: "What happens to a vertex's neighbors' in-degrees when that vertex is processed?",
+ options: [
+ "Nothing changes",
+ "Each neighbor's in-degree is decremented, since one of its prerequisites (this vertex) is now satisfied",
+ "Each neighbor's in-degree is incremented",
+ "All neighbors are immediately removed from the graph"
+ ],
+ correctAnswer: 1,
+ explanation: "Processing a vertex effectively \"removes\" its outgoing edges, which decreases the in-degree of everything it pointed to — possibly freeing those vertices up to be queued."
+ },
+ {
+ question: "How does Kahn's algorithm detect that a graph contains a cycle?",
+ options: [
+ "It doesn't — cycles cause an infinite loop",
+ "If the final output order has fewer vertices than the graph, the remaining vertices never reached in-degree 0, which only happens because of a cycle",
+ "It checks whether any edge has a negative weight",
+ "It counts the total number of edges before starting"
+ ],
+ correctAnswer: 1,
+ explanation: "Every vertex in a cycle keeps at least one unmet prerequisite forever, so it never gets queued — an incomplete final order is the tell-tale sign of a cycle."
+ }
+ ];
+
+ const [currentQuestion, setCurrentQuestion] = useState(0);
+ const [selectedAnswer, setSelectedAnswer] = useState(null);
+ const [score, setScore] = useState(0);
+ const [showResult, setShowResult] = useState(false);
+ const [quizCompleted, setQuizCompleted] = useState(false);
+ const [answers, setAnswers] = useState(Array(questions.length).fill(null));
+ const [showIntro, setShowIntro] = useState(true);
+ const [showSuccessAnimation, setShowSuccessAnimation] = useState(false);
+
+ const handleAnswerSelect = (optionIndex) => {
+ setSelectedAnswer(optionIndex);
+ const newAnswers = [...answers];
+ newAnswers[currentQuestion] = optionIndex;
+ setAnswers(newAnswers);
+ };
+
+ const handleNextQuestion = () => {
+ if (selectedAnswer === null) return;
+
+ if (selectedAnswer === questions[currentQuestion].correctAnswer) {
+ setScore(score + 1);
+ }
+ if (currentQuestion < questions.length - 1) {
+ setCurrentQuestion(currentQuestion + 1);
+ setSelectedAnswer(null);
+ } else {
+ setShowSuccessAnimation(true);
+ setTimeout(() => {
+ setShowSuccessAnimation(false);
+ setQuizCompleted(true);
+ setShowResult(true);
+ }, 2000);
+ }
+ };
+
+ const handlePreviousQuestion = () => {
+ setCurrentQuestion(currentQuestion - 1);
+ setSelectedAnswer(answers[currentQuestion - 1]);
+ };
+
+ const resetQuiz = () => {
+ setCurrentQuestion(0);
+ setSelectedAnswer(null);
+ setScore(0);
+ setShowResult(false);
+ setQuizCompleted(false);
+ setAnswers(Array(questions.length).fill(null));
+ setShowIntro(true);
+ };
+
+ const calculateWeakAreas = () => {
+ const weakAreas = [];
+ if (answers[0] !== questions[0].correctAnswer) {
+ weakAreas.push("what topological sort produces");
+ }
+ if (answers[1] !== questions[1].correctAnswer) {
+ weakAreas.push("why cycles rule out a valid order");
+ }
+ if (answers[2] !== questions[2].correctAnswer) {
+ weakAreas.push("what in-degree represents");
+ }
+ if (answers[3] !== questions[3].correctAnswer) {
+ weakAreas.push("how processing a vertex affects its neighbors");
+ }
+ if (answers[4] !== questions[4].correctAnswer) {
+ weakAreas.push("how the algorithm detects a cycle");
+ }
+
+ return weakAreas.length > 0
+ ? `Focus on improving: ${weakAreas.join(', ')}. Review the corresponding sections above.`
+ : "Perfect! You've mastered Topological Sort!";
+ };
+
+ const startQuiz = () => {
+ setShowIntro(false);
+ };
+
+ const getStarRating = () => {
+ const percentage = (score / questions.length) * 100;
+ if (percentage >= 90) return 5;
+ if (percentage >= 70) return 4;
+ if (percentage >= 50) return 3;
+ if (percentage >= 30) return 2;
+ return 1;
+ };
+
+ return (
+
+ {showIntro ? (
+
+
+
+
+
+
+
+ Topological Sort Quiz
+
+
+
+ How it works:
+
+
+
+
+ +1 point for each correct answer
+
+
+
+ 0 points for wrong answers
+
+
+
+ -0.5 point penalty for viewing explanations
+
+
+
+ Earn stars based on your final score (max 5 stars)
+