@@ -112,8 +179,8 @@ const LinkedListVisualizer = ({ darkMode }) => {
{node.value}
@@ -122,7 +189,7 @@ const LinkedListVisualizer = ({ darkMode }) => {
)}
{idx === displayList.length - 1 && (
-
NULL
+
NULL
)}
))}
diff --git a/src/lib/dsaAdvancedUtils.js b/src/lib/dsaAdvancedUtils.js
index 83f91a3..b2444df 100644
--- a/src/lib/dsaAdvancedUtils.js
+++ b/src/lib/dsaAdvancedUtils.js
@@ -5,9 +5,9 @@ export const generateLinkedListSteps = (currentList, operation, params) => {
const steps = [];
// list is array of { id, value }
// operations: 'insert', 'delete', 'search'
-
+
const newList = [...currentList];
-
+
if (operation === 'insert') {
const { value, index } = params;
const validIndex = Math.max(0, Math.min(index, newList.length));
@@ -15,33 +15,170 @@ export const generateLinkedListSteps = (currentList, operation, params) => {
// Step 1: Visualize finding position
for (let i = 0; i < validIndex; i++) {
- steps.push({ type: 'highlight', index: i, message: `Traversing to index ${i}...` });
+ steps.push({ type: 'highlight', index: i, message: `Traversing to Pointer ${i}...` });
}
// Step 2: Insert
newList.splice(validIndex, 0, newNode);
- steps.push({ type: 'update', list: [...newList], highlightedIndex: validIndex, message: `Inserted ${value} at index ${validIndex}` });
- }
+ steps.push({ type: 'update', list: [...newList], highlightedIndex: validIndex, message: `Inserted node with value ${value} at Pointer ${validIndex}` });
+ }
else if (operation === 'delete') {
const { index } = params;
if (index < 0 || index >= newList.length) return [];
for (let i = 0; i < index; i++) {
- steps.push({ type: 'highlight', index: i, message: `Traversing to index ${i}...` });
+ steps.push({ type: 'highlight', index: i, message: `Traversing to Pointer ${i}...` });
+ }
+
+ const removedVal = newList[index].value;
+ newList.splice(index, 1);
+ steps.push({ type: 'update', list: [...newList], message: `Deleted node with value ${removedVal}` });
+ }
+ else if (operation === 'search') {
+ const { value } = params;
+ let found = false;
+ for (let i = 0; i < newList.length; i++) {
+ steps.push({ type: 'highlight', index: i, message: `Checking if node value ${newList[i].value} matches ${value}...` });
+ if (newList[i].value === value) {
+ steps.push({ type: 'found', index: i, highlightedIndex: i, message: `Found value ${value} at Pointer ${i}!` });
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ steps.push({ type: 'not-found', highlightedIndex: -1, message: `Value ${value} not found in the list.` });
+ }
+ }
+
+ return steps;
+};
+
+export const generateDoublyLinkedListSteps = (currentList, operation, params) => {
+ const steps = [];
+ const newList = [...currentList];
+
+ if (operation === 'insert') {
+ const { value, index } = params;
+ const validIndex = Math.max(0, Math.min(index, newList.length));
+ const newNode = { id: Date.now(), value };
+
+ for (let i = 0; i < validIndex; i++) {
+ steps.push({ type: 'highlight', index: i, direction: 'forward', message: `Traversing forward to Pointer ${i}...` });
+ }
+
+ newList.splice(validIndex, 0, newNode);
+ steps.push({ type: 'update', list: [...newList], highlightedIndex: validIndex, message: `Inserted ${value} and connected prev and next arrows` });
+ }
+ else if (operation === 'delete') {
+ const { index } = params;
+ if (index < 0 || index >= newList.length) return [];
+
+ for (let i = 0; i < index; i++) {
+ steps.push({ type: 'highlight', index: i, direction: 'forward', message: `Traversing forward to Pointer ${i}...` });
+ }
+
+ const removedVal = newList[index].value;
+
+ steps.push({ type: 'highlight', index, message: `Disconnecting prev and next arrows for node ${removedVal}` });
+
+ newList.splice(index, 1);
+ steps.push({ type: 'update', list: [...newList], message: `Deleted node with value ${removedVal}` });
+ }
+ else if (operation === 'search') {
+ const { value } = params;
+ let found = false;
+ for (let i = 0; i < newList.length; i++) {
+ steps.push({ type: 'highlight', index: i, direction: 'forward', message: `Checking if node value ${newList[i].value} matches ${value}...` });
+ if (newList[i].value === value) {
+ steps.push({ type: 'found', index: i, highlightedIndex: i, message: `Found value ${value} at Pointer ${i}!` });
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ steps.push({ type: 'not-found', highlightedIndex: -1, message: `Value ${value} not found in the list.` });
+ }
+ }
+ else if (operation === 'traverse') {
+ if (newList.length === 0) return steps;
+ for (let i = 0; i < newList.length; i++) {
+ steps.push({ type: 'highlight', index: i, direction: 'forward', message: `Traversing forward at Pointer ${i}` });
+ }
+ for (let i = newList.length - 1; i >= 0; i--) {
+ steps.push({ type: 'highlight', index: i, direction: 'backward', message: `Traversing backward at Pointer ${i}` });
}
+ steps.push({ type: 'done', message: 'Traversal complete' });
+ }
+
+ return steps;
+};
+
+export const generateCircularLinkedListSteps = (currentList, operation, params) => {
+ const steps = [];
+ const listLength = currentList.length;
+ let newList = [...currentList];
+
+ if (operation === 'insert') {
+ const { value, index } = params;
+ const newNode = { id: Date.now(), value };
+
+ if (index === 0) {
+ newList.unshift(newNode);
+ } else if (index >= listLength) {
+ newList.push(newNode);
+ } else {
+ newList.splice(index, 0, newNode);
+ }
+ steps.push({ type: 'update', list: [...newList], message: `Inserted node with value ${value}` });
+ }
+ else if (operation === 'delete') {
+ const { index } = params;
+ if (index < 0 || index >= listLength || listLength === 0) return steps;
const removedVal = newList[index].value;
newList.splice(index, 1);
steps.push({ type: 'update', list: [...newList], message: `Deleted node with value ${removedVal}` });
}
-
+ else if (operation === 'search') {
+ const { value } = params;
+ let found = false;
+ for (let i = 0; i < newList.length; i++) {
+ steps.push({ type: 'highlight', index: i, message: `Checking if node value ${newList[i].value} matches ${value}...` });
+ if (newList[i].value === value) {
+ steps.push({ type: 'found', index: i, highlightedIndex: i, message: `Found value ${value} at Pointer ${i}!` });
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ steps.push({ type: 'not-found', highlightedIndex: -1, message: `Value ${value} not found in the list.` });
+ }
+ }
+ else if (operation === 'traverse') {
+ if (newList.length === 0) return steps;
+ const loops = 5;
+ const limit = newList.length * loops;
+ let currentIdx = 0;
+ for (let i = 0; i < limit; i++) {
+ steps.push({ type: 'highlight', index: currentIdx, message: `Traversing node at Pointer ${currentIdx}` });
+
+ let nextIdx = (currentIdx + 1) % newList.length;
+ if (nextIdx === 0 && i !== limit - 1) {
+ steps.push({ type: 'highlight-curve', index: currentIdx, message: `Following circular link back to head` });
+ }
+ currentIdx = nextIdx;
+ }
+ steps.push({ type: 'done', message: 'Circular traversal complete' });
+ }
+
return steps;
};
+
// BST Logic
export const generateBSTSteps = (root, operation, value) => {
const steps = [];
-
+
// Deep clone helper
const clone = (node) => node ? { ...node, left: clone(node.left), right: clone(node.right) } : null;
let newRoot = clone(root);
@@ -56,7 +193,7 @@ export const generateBSTSteps = (root, operation, value) => {
let curr = newRoot;
while (true) {
steps.push({ type: 'visit', activeId: curr.id, root: clone(newRoot), message: `Comparing ${value} with ${curr.value}` });
-
+
if (value < curr.value) {
if (!curr.left) {
curr.left = { id: Date.now(), value };
@@ -86,13 +223,13 @@ export const generateHeapSteps = (heap, operation, value) => {
if (operation === 'insert') {
newHeap.push(value);
steps.push({ type: 'update', heap: [...newHeap], activeIdx: newHeap.length - 1, message: `Added ${value} at the end` });
-
+
// Heapify Up
let idx = newHeap.length - 1;
while (idx > 0) {
let parentIdx = Math.floor((idx - 1) / 2);
steps.push({ type: 'compare', heap: [...newHeap], indices: [idx, parentIdx], message: `Comparing ${newHeap[idx]} with parent ${newHeap[parentIdx]}` });
-
+
if (newHeap[idx] > newHeap[parentIdx]) {
[newHeap[idx], newHeap[parentIdx]] = [newHeap[parentIdx], newHeap[idx]];
steps.push({ type: 'swap', heap: [...newHeap], indices: [idx, parentIdx], message: 'Child is larger, swapping' });
@@ -110,7 +247,7 @@ export const generateHeapSteps = (heap, operation, value) => {
// DP: Steps Generator
export const generateDPSteps = (type, params) => {
const steps = [];
-
+
if (type === 'LCS') {
const { s1, s2 } = params;
// Two Pointers Approach for "Is Subsequence" / Greedy LCS check
@@ -118,10 +255,10 @@ export const generateDPSteps = (type, params) => {
let i = 0;
let j = 0;
const matchedIndicesS1 = []; // Indices in s1 that matched
-
+
steps.push({
type: 'init',
- i: 0, j: 0,
+ i: 0, j: 0,
matchedIndicesS1: [],
s1, s2,
message: 'Initialized pointers. Looking for characters of Pattern in String.'
@@ -131,7 +268,7 @@ export const generateDPSteps = (type, params) => {
// Visualize comparison
steps.push({
type: 'compare',
- i, j,
+ i, j,
matchedIndicesS1: [...matchedIndicesS1],
s1, s2,
message: `Comparing String[${i}] ('${s1[i]}') with Pattern[${j}] ('${s2[j]}').`
@@ -142,7 +279,7 @@ export const generateDPSteps = (type, params) => {
matchedIndicesS1.push(i);
steps.push({
type: 'match',
- i, j,
+ i, j,
matchedIndicesS1: [...matchedIndicesS1],
s1, s2,
message: `Match! Found '${s2[j]}' at index ${i}. Moving both pointers.`
@@ -153,7 +290,7 @@ export const generateDPSteps = (type, params) => {
// No match
steps.push({
type: 'mismatch',
- i, j,
+ i, j,
matchedIndicesS1: [...matchedIndicesS1],
s1, s2,
message: `Mismatch. '${s1[i]}' is not '${s2[j]}'. Moving String pointer.`
@@ -161,15 +298,15 @@ export const generateDPSteps = (type, params) => {
i++;
}
}
-
+
// Final state
steps.push({
type: 'finish',
- i, j,
+ i, j,
matchedIndicesS1: [...matchedIndicesS1],
s1, s2,
- message: j === s2.length
- ? `Success! Found full subsequence "${s2}".`
+ message: j === s2.length
+ ? `Success! Found full subsequence "${s2}".`
: `Finished. Found partial subsequence: "${s2.substring(0, j)}".`
});
@@ -180,11 +317,11 @@ export const generateDPSteps = (type, params) => {
memo[0] = 0;
memo[1] = 1;
steps.push({ type: 'init', table: [...memo], message: 'Base cases: Fib(0)=0, Fib(1)=1' });
-
- for(let i=2; i<=n; i++) {
- steps.push({ type: 'calc', i, table: [...memo], message: `Calculating Fib(${i}) = Fib(${i-1}) + Fib(${i-2})` });
- memo[i] = memo[i-1] + memo[i-2];
- steps.push({ type: 'update', i, val: memo[i], table: [...memo], message: `Fib(${i}) = ${memo[i]}` });
+
+ for (let i = 2; i <= n; i++) {
+ steps.push({ type: 'calc', i, table: [...memo], message: `Calculating Fib(${i}) = Fib(${i - 1}) + Fib(${i - 2})` });
+ memo[i] = memo[i - 1] + memo[i - 2];
+ steps.push({ type: 'update', i, val: memo[i], table: [...memo], message: `Fib(${i}) = ${memo[i]}` });
}
}
diff --git a/src/pages/DsaVisualization.jsx b/src/pages/DsaVisualization.jsx
index 5caefa3..18a0745 100644
--- a/src/pages/DsaVisualization.jsx
+++ b/src/pages/DsaVisualization.jsx
@@ -3,10 +3,8 @@ import React, { useState, useEffect, useRef, useMemo } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Helmet } from 'react-helmet';
import {
- Play, Pause, RotateCcw, Settings, ChevronRight, ChevronLeft,
- LayoutGrid, List, BarChart2, Activity, Moon, Sun,
- GitCompare, SplitSquareHorizontal, ArrowRight, Network,
- PlusCircle, Move, MousePointer2, Trash2, StepForward,
+ Play, Pause, RotateCcw, List, Activity, Moon, Sun,
+ GitCompare, Network, PlusCircle, Move, Trash2, StepForward,
StepBack, Binary, BoxSelect, Layers, Search, Github
} from 'lucide-react';
import { Button } from '@/components/ui/button';
@@ -28,6 +26,8 @@ import { useToast } from '@/components/ui/use-toast';
// Import new visualizers
import LinkedListVisualizer from '@/components/dsa/LinkedListVisualizer';
+import DoublyLinkedListVisualizer from '@/components/dsa/DoublyLinkedListVisualizer';
+import CircularLinkedListVisualizer from '@/components/dsa/CircularLinkedListVisualizer';
import TreeVisualizer from '@/components/dsa/TreeVisualizer';
import HeapVisualizer from '@/components/dsa/HeapVisualizer';
import DPVisualizer from '@/components/dsa/DPVisualizer';
@@ -408,6 +408,7 @@ const DsaVisualization = () => {
const [darkMode, setDarkMode] = useState(true);
const [customInput, setCustomInput] = useState('');
const [searchTarget, setSearchTarget] = useState(42); // Default target
+ const [llType, setLlType] = useState('singly');
const [array, setArray] = useState([]);
@@ -675,9 +676,28 @@ const DsaVisualization = () => {
-
Linked List Visualizer
+
+
Linked List Visualizer ({llType.charAt(0).toUpperCase() + llType.slice(1)})
+
+
+ Type
+
+
-
+
+ {llType === 'singly' && }
+ {llType === 'doubly' && }
+ {llType === 'circular' && }
+
) : activeTab === 'trees' ? (