diff --git a/app/visualizer/graph/representation/adjacency-list/animation.jsx b/app/visualizer/graph/representation/adjacency-list/animation.jsx new file mode 100755 index 0000000..1660574 --- /dev/null +++ b/app/visualizer/graph/representation/adjacency-list/animation.jsx @@ -0,0 +1,354 @@ +"use client"; +import React, { useState } from "react"; +import { gsap } from "gsap"; +import { Plus, Shuffle, RotateCcw, ArrowRight } from "lucide-react"; + +const LETTERS = "ABCDEFGHIJ".split(""); +const MAX_VERTICES = 10; + +const layoutCircle = (vertices) => { + const n = vertices.length; + const radius = n <= 1 ? 0 : 130; + const cx = 220; + const cy = 170; + return vertices.map((v, i) => { + const angle = (2 * Math.PI * i) / n - Math.PI / 2; + return { label: v, x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) }; + }); +}; + +// Unlike a matrix, a list only stores entries for edges that actually exist — +// each vertex's row is exactly as long as its number of neighbors. +const buildAdjList = (vertices, edges) => { + const list = {}; + vertices.forEach((v) => (list[v] = [])); + edges.forEach((e) => { + if (list[e.from]) list[e.from].push({ to: e.to, weight: e.weight }); + if (!e.directed && list[e.to]) list[e.to].push({ to: e.from, weight: e.weight }); + }); + return list; +}; + +const EXAMPLE_VERTICES = ["A", "B", "C", "D", "E"]; +const EXAMPLE_EDGES = [ + { from: "A", to: "B", weight: 1, directed: false }, + { from: "A", to: "C", weight: 1, directed: false }, + { from: "B", to: "D", weight: 1, directed: false }, + { from: "C", to: "D", weight: 1, directed: false }, + { from: "D", to: "E", weight: 1, directed: false }, +]; + +const AdjacencyListVisualizer = () => { + const [vertices, setVertices] = useState([]); + const [edges, setEdges] = useState([]); + const [directedMode, setDirectedMode] = useState(false); + const [fromVertex, setFromVertex] = useState(""); + const [toVertex, setToVertex] = useState(""); + const [weightInput, setWeightInput] = useState("1"); + const [recentEdge, setRecentEdge] = useState(null); + const [selectedEdge, setSelectedEdge] = useState(null); + const [message, setMessage] = useState("Add vertices, then connect them with edges"); + + const addVertex = () => { + if (vertices.length >= MAX_VERTICES) { + setMessage(`Limit of ${MAX_VERTICES} vertices reached`); + return; + } + const next = LETTERS[vertices.length]; + setVertices((prev) => [...prev, next]); + setMessage(`Added vertex ${next}`); + }; + + const addEdge = () => { + if (!fromVertex || !toVertex) { + setMessage("Choose both a From and a To vertex"); + return; + } + const weight = parseFloat(weightInput); + if (Number.isNaN(weight)) { + setMessage("Enter a valid weight"); + return; + } + + setEdges((prev) => { + const existingIndex = prev.findIndex( + (e) => (e.from === fromVertex && e.to === toVertex) || (!e.directed && e.from === toVertex && e.to === fromVertex) + ); + const newEdge = { from: fromVertex, to: toVertex, weight, directed: directedMode }; + if (existingIndex >= 0) { + const copy = [...prev]; + copy[existingIndex] = newEdge; + return copy; + } + return [...prev, newEdge]; + }); + + setRecentEdge({ from: fromVertex, to: toVertex, directed: directedMode }); + setSelectedEdge(null); + setMessage(`Appended ${toVertex} to ${fromVertex}'s list${directedMode ? "" : `, and ${fromVertex} to ${toVertex}'s list — undirected edges are mirrored`}`); + + const chips = document.querySelectorAll(".list-chip-active"); + if (chips.length > 0) { + gsap.fromTo(chips, { scale: 1 }, { scale: 1.15, duration: 0.2, yoyo: true, repeat: 1 }); + } + }; + + const loadExample = () => { + setVertices(EXAMPLE_VERTICES); + setEdges(EXAMPLE_EDGES); + setRecentEdge(null); + setSelectedEdge(null); + setMessage("Loaded an example graph"); + }; + + const reset = () => { + setVertices([]); + setEdges([]); + setFromVertex(""); + setToVertex(""); + setWeightInput("1"); + setRecentEdge(null); + setSelectedEdge(null); + setMessage("Add vertices, then connect them with edges"); + }; + + const handleChipClick = (from, to) => { + if (selectedEdge && selectedEdge.from === from && selectedEdge.to === to) { + setSelectedEdge(null); + setMessage("Deselected"); + return; + } + setSelectedEdge({ from, to }); + setRecentEdge(null); + setMessage(`Highlighting edge ${from} → ${to}`); + }; + + const positions = layoutCircle(vertices); + const posByLabel = Object.fromEntries(positions.map((p) => [p.label, p])); + const adjList = buildAdjList(vertices, edges); + + const isEdgeHighlighted = (from, to) => { + if (recentEdge && ((recentEdge.from === from && recentEdge.to === to) || (!recentEdge.directed && recentEdge.from === to && recentEdge.to === from))) return "recent"; + if (selectedEdge && ((selectedEdge.from === from && selectedEdge.to === to) || (selectedEdge.from === to && selectedEdge.to === from))) return "selected"; + return null; + }; + + const animateDropIn = (el) => { + if (!el || el.dataset.animated) return; + el.dataset.animated = "true"; + gsap.fromTo(el, { scale: 0, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.4, ease: "back.out(1.7)" }); + }; + + return ( +
+

+ See how a graph's edges become per-vertex neighbor lists +

+ +
+ {/* Controls */} +
+
+ + + +
+ +
+ + + setWeightInput(e.target.value)} + placeholder="Weight" + className="w-20 px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-neutral-900 text-gray-800 dark:text-gray-100 outline-none focus:ring-2 focus:ring-blue-500/40" + /> + +
+ +
+ + +
+
+ + {/* Message */} +
+ {message} +
+ + {/* Graph */} +
+

Graph

+
+ {vertices.length > 0 ? ( + + + + + + + + + + + + {edges.map((e, i) => { + const a = posByLabel[e.from]; + const b = posByLabel[e.to]; + if (!a || !b) return null; + const status = isEdgeHighlighted(e.from, e.to); + const midX = (a.x + b.x) / 2; + const midY = (a.y + b.y) / 2; + return ( + + + {e.weight !== 1 && ( + <> + + + {e.weight} + + + )} + + ); + })} + + {positions.map((p, i) => ( + + + + {p.label} + + + ))} + + ) : ( +
+ Add a vertex to begin +
+ )} +
+
+ + {/* Adjacency List */} +
+

Adjacency List

+ {vertices.length > 0 ? ( +
+ {vertices.map((v) => ( +
+
+ {v} +
+ + {adjList[v].length > 0 ? ( +
+ {adjList[v].map((entry, i) => { + const status = isEdgeHighlighted(v, entry.to); + return ( + + ); + })} +
+ ) : ( + no neighbors + )} +
+ ))} +
+ ) : ( +
Add vertices to see the list
+ )} + +
+ + + Neighbor entry + + + + Most recently added edge + + + + Selected (click any entry) + +
+
+
+
+ ); +}; + +export default AdjacencyListVisualizer; diff --git a/app/visualizer/graph/representation/adjacency-list/code.js b/app/visualizer/graph/representation/adjacency-list/code.js new file mode 100755 index 0000000..bd49f19 --- /dev/null +++ b/app/visualizer/graph/representation/adjacency-list/code.js @@ -0,0 +1,141 @@ +const codeExamples = { + javascript: `class GraphList { + constructor() { + this.list = new Map(); // vertex -> [{ to, weight }, ...] + } + + addVertex(v) { + if (!this.list.has(v)) this.list.set(v, []); + } + + addEdge(from, to, weight = 1, directed = false) { + this.list.get(from).push({ to, weight }); + if (!directed) this.list.get(to).push({ to: from, weight }); // mirror the edge both ways + } + + hasEdge(from, to) { + return this.list.get(from).some((entry) => entry.to === to); // O(degree(from)) + } + + neighbors(v) { + return this.list.get(v).map((entry) => entry.to); // exactly the relevant entries + } +} + +// Usage example +const g = new GraphList(); +["A", "B", "C", "D"].forEach((v) => g.addVertex(v)); +g.addEdge("A", "B"); +g.addEdge("A", "C"); +g.hasEdge("A", "B"); // true +g.neighbors("A"); // ["B", "C"]`, + + python: `class GraphList: + def __init__(self): + self.list = {} # vertex -> [(to, weight), ...] + + def add_vertex(self, v): + self.list.setdefault(v, []) + + def add_edge(self, frm, to, weight=1, directed=False): + self.list[frm].append((to, weight)) + if not directed: + self.list[to].append((frm, weight)) # mirror the edge both ways + + def has_edge(self, frm, to): + return any(t == to for t, _ in self.list[frm]) # O(degree(frm)) + + def neighbors(self, v): + return [t for t, _ in self.list[v]] # exactly the relevant entries + +# Usage example +g = GraphList() +for v in ["A", "B", "C", "D"]: + g.add_vertex(v) +g.add_edge("A", "B") +g.add_edge("A", "C") +g.has_edge("A", "B") # True +g.neighbors("A") # ["B", "C"]`, + + c: `#include +#include + +typedef struct EdgeNode { + char to; + int weight; + struct EdgeNode* next; +} EdgeNode; + +#define MAX_V 26 +EdgeNode* list[MAX_V] = { NULL }; + +void addEdge(char from, char to, int weight, int directed) { + EdgeNode* node = malloc(sizeof(EdgeNode)); + node->to = to; + node->weight = weight; + node->next = list[from - 'A']; + list[from - 'A'] = node; // prepend to from's list + + if (!directed) { + EdgeNode* back = malloc(sizeof(EdgeNode)); + back->to = from; + back->weight = weight; + back->next = list[to - 'A']; + list[to - 'A'] = back; // mirror the edge both ways + } +} + +int hasEdge(char from, char to) { + for (EdgeNode* n = list[from - 'A']; n != NULL; n = n->next) { // O(degree(from)) + if (n->to == to) return 1; + } + return 0; +} + +int main() { + addEdge('A', 'B', 1, 0); + addEdge('A', 'C', 1, 0); + printf("A-B edge: %d\\n", hasEdge('A', 'B')); + return 0; +}`, + + java: `import java.util.*; + +public class GraphList { + Map> list = new HashMap<>(); // vertex -> [(toAsChar, weight), ...] + // Using a simple pair representation: [to, weight] where 'to' is stored as its char code. + + void addVertex(char v) { + list.putIfAbsent(v, new ArrayList<>()); + } + + void addEdge(char from, char to, int weight, boolean directed) { + list.get(from).add(new int[]{ to, weight }); + if (!directed) list.get(to).add(new int[]{ from, weight }); // mirror the edge both ways + } + + boolean hasEdge(char from, char to) { + for (int[] entry : list.get(from)) { // O(degree(from)) + if (entry[0] == to) return true; + } + return false; + } + + List neighbors(char v) { + List result = new ArrayList<>(); + for (int[] entry : list.get(v)) result.add((char) entry[0]); + return result; + } + + public static void main(String[] args) { + GraphList g = new GraphList(); + for (char v : new char[]{'A', 'B', 'C', 'D'}) g.addVertex(v); + g.addEdge('A', 'B', 1, false); + g.addEdge('A', 'C', 1, false); + System.out.println("A-B edge: " + g.hasEdge('A', 'B')); + System.out.println("A's neighbors: " + g.neighbors('A')); + } +}`, +}; + +export default codeExamples; diff --git a/app/visualizer/graph/representation/adjacency-list/content.jsx b/app/visualizer/graph/representation/adjacency-list/content.jsx new file mode 100755 index 0000000..7ad2f52 --- /dev/null +++ b/app/visualizer/graph/representation/adjacency-list/content.jsx @@ -0,0 +1,207 @@ +"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: 110, y: 30 }, + { id: "B", x: 40, y: 90 }, + { id: "C", x: 180, y: 90 }, + ]; + const edges = [ + ["A", "B"], + ["A", "C"], + ["B", "C"], + ]; + const byId = Object.fromEntries(vertices.map((v) => [v.id, v])); + const list = { A: ["B", "C"], B: ["A", "C"], C: ["A", "B"] }; + + return ( +
+ + {edges.map(([from, to], i) => ( + + ))} + {vertices.map((v, i) => ( + + + + {v.id} + + + ))} + + +
+ {Object.entries(list).map(([v, neighbors]) => ( +
+ {v} + +
+ {neighbors.map((n) => ( + + {n} + + ))} +
+
+ ))} +
+
+ ); +}; + +const Content = () => { + const { theme } = useTheme(); + + const paragraphs = [ + `An adjacency list represents a graph as a collection of per-vertex neighbor lists: one entry per vertex, holding only the vertices it's actually connected to. Instead of a full V×V grid of mostly zeros, each vertex stores exactly as many entries as it has edges — nothing more.`, + `Adding an edge (u, v) appends v to u's list. For an undirected graph, u is also appended to v's list, since the edge goes both ways; for a directed graph, only u's list gets the new entry. Checking whether an edge exists means scanning through one vertex's list looking for the target — proportional to that vertex's degree (its number of neighbors), not the whole graph.`, + `This is the mirror image of an adjacency matrix's tradeoffs. A matrix spends O(V²) space no matter what, in exchange for O(1) edge-existence checks. A list spends space proportional to the actual number of edges — O(V + E) — but checking a specific edge now costs O(degree) instead of O(1). For the vast majority of real-world graphs, which are sparse (E is much smaller than V²), that tradeoff strongly favors the list.`, + `Adjacency lists are the default choice for most graph algorithms — BFS, DFS, Dijkstra's algorithm, and topological sort all need to repeatedly ask "what are this vertex's neighbors?", which a list answers by directly returning exactly the relevant entries, without wasting time scanning past vertices that aren't connected at all.`, + ]; + + const algorithm = [ + { points: "Create an empty list (or map) for every vertex" }, + { points: "For every edge (u, v) with weight w: append (v, w) to u's list" }, + { points: "If the graph is undirected, also append (u, w) to v's list — the same edge is recorded from both directions" }, + { points: "To check if an edge exists between two vertices, scan the source vertex's list for the target" }, + ]; + + const complexity = [ + { points: "Space Complexity: O(V + E) — proportional to the actual number of vertices and edges, not V²." }, + { points: "Check if edge (u, v) exists: O(degree(u)) — scan u's list, which is only as long as u's actual neighbor count." }, + { points: "Iterate over a vertex's neighbors: O(degree(u)) — the list already holds exactly the relevant entries." }, + { points: "Add an edge: O(1) — appending to a list." }, + ]; + + return ( +
+
+ + +
+
+ {/* What is it */} +
+

+ + What is an Adjacency List? +

+
+

+ {paragraphs[0]} +

+
+
+ + {/* How it works */} +
+

+ + How Does It Work? +

+
+

+ {paragraphs[1]} +

+
+
+

+ {paragraphs[2]} +

+
+ +
+ The same triangle graph — every vertex's list holds only its actual neighbors +
+ +
+ + {/* Algorithm Steps */} +
+

+ + Building the List +

+
+
    + {algorithm.map((item, index) => ( +
  1. + {item.points} +
  2. + ))} +
+
+
+ + {/* Time Complexity */} +
+

+ + Complexity +

+
+
    + {complexity.map((item, index) => ( +
  • + + {item.points.split(":")[0]}: + + {item.points.split(":")[1]} +
  • + ))} +
+
+ +
+ n} + averageCase={(n) => n + n} + worstCase={(n) => n * n} + maxN={20} + /> +
+ + +
+ + {/* Additional Info */} +
+
+
+

+ {paragraphs[3]} +

+
+
+
+
+ + +
+ ); +}; + +export default Content; diff --git a/app/visualizer/graph/representation/adjacency-list/page.jsx b/app/visualizer/graph/representation/adjacency-list/page.jsx new file mode 100755 index 0000000..f4bbdec --- /dev/null +++ b/app/visualizer/graph/representation/adjacency-list/page.jsx @@ -0,0 +1,109 @@ +import Animation from "@/app/visualizer/graph/representation/adjacency-list/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/representation/adjacency-list/quiz"; +import Content from "@/app/visualizer/graph/representation/adjacency-list/content"; +import ModuleCard from "@/app/components/ui/ModuleCard"; +import { MODULE_MAPS } from "@/lib/modulesMap"; + +export const metadata = { + title: "Adjacency List | Animation and Explanation", + description: + "Learn how an adjacency list represents a graph as per-vertex neighbor lists, with an interactive visualizer showing both the graph and its list side by side, code examples in JavaScript, C, Python, and Java, and a quiz.", + keywords: [ + "Adjacency List", + "Adjacency List Graph", + "Adjacency List Algorithm", + "Adjacency List Visualization", + "Graph Representation", + "Graph Data Structure", + "Weighted Graph List", + "Directed Graph List", + "Adjacency List in JavaScript", + "Adjacency List in C", + "Adjacency List in Python", + "Adjacency List in Java", + "Graph Algorithms", + "DSA Graphs", + "Learn Graphs", + "Graph Quiz", + ], + robots: "index, follow", + openGraph: { + images: [ + { + url: "/og.png", + width: 1200, + height: 630, + alt: "Adjacency List Visualization", + }, + ], + }, +}; + +export default function Page() { + const paths = [ + { name: "Home", href: "/" }, + { name: "Visualizer", href: "/visualizer" }, + { name: "Graph : Adjacency List", href: "" }, + ]; + + return ( + <> +
+ +
+ +
+
+ + +
+ +
+ +
+ +
+

+ Test Your Knowledge before moving forward! +

+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +