diff --git a/src/algorithms/Recursion/permutation.js b/src/algorithms/Recursion/permutation.js new file mode 100644 index 0000000..d5aa643 --- /dev/null +++ b/src/algorithms/Recursion/permutation.js @@ -0,0 +1,57 @@ +export function generatePermutations(arr) { + const steps = []; + const result = []; + + function swap(a, i, j) { + const temp = a[i]; + a[i] = a[j]; + a[j] = temp; + } + + function backtrack(a, index, depth) { + steps.push({ + type: "recurse", + array: [...a], + index, + depth + }); + + if (index === a.length) { + result.push([...a]); + steps.push({ + type: "output", + array: [...a], + depth + }); + return; + } + + for (let i = index; i < a.length; i++) { + steps.push({ + type: "swap", + array: [...a], + i: index, + j: i, + depth + }); + swap(a, index, i); + backtrack(a, index + 1, depth + 1); + steps.push({ + type: "backtrack", + array: [...a], + i: index, + j: i, + depth + }); + + swap(a, index, i); + } + } + steps.push({ + type: "start", + array: [...arr], + depth: 0 + }); +backtrack([...arr], 0, 0); + return { steps, permutations: result }; +} diff --git a/src/components/Recursion/PermutationsVisualizer.jsx b/src/components/Recursion/PermutationsVisualizer.jsx new file mode 100644 index 0000000..bbac8ed --- /dev/null +++ b/src/components/Recursion/PermutationsVisualizer.jsx @@ -0,0 +1,209 @@ +import React, { useState, useEffect, useRef } from "react"; +import { generatePermutations } from "@/algorithms/Recursion/permutation"; + +export default function PermutationsVisualizer() { + const [inputText, setInputText] = useState("A,B,C"); + const [array, setArray] = useState(["A", "B", "C"]); + const [steps, setSteps] = useState([]); + const [permutations, setPermutations] = useState([]); + const [index, setIndex] = useState(0); + const [playing, setPlaying] = useState(false); + const [speed, setSpeed] = useState(600); + + const timerRef = useRef(null); + + useEffect(() => { + const arr = inputText.split(",").map(x => x.trim()).filter(Boolean); + setArray(arr.length ? arr : []); + }, [inputText]); + + useEffect(() => { + if (!playing) return; + if (index >= steps.length) { + setPlaying(false); + return; + } + timerRef.current = setTimeout(() => { + setIndex(i => Math.min(i + 1, steps.length)); + }, speed); + + return () => clearTimeout(timerRef.current); + }, [playing, index, steps.length, speed]); + + function build() { + const arr = inputText.split(",").map(x => x.trim()).filter(Boolean); + const { steps: s, permutations: p } = generatePermutations(arr); + + setSteps(s); + setPermutations(p); + setIndex(0); + setPlaying(false); + } + + function stepForward() { + setIndex(i => Math.min(i + 1, steps.length)); + } + + function stepBackward() { + setIndex(i => Math.max(i - 1, 0)); + } + + function reset() { + setIndex(0); + setPlaying(false); + } + + const currentStep = index > 0 ? steps[index - 1] : null; + const displayedArray = currentStep ? currentStep.array : array; + + let highlight = { i: null, j: null }; + let status = "Idle"; + let depth = currentStep ? currentStep.depth : "-"; + + if (currentStep) { + if (["swap", "backtrack"].includes(currentStep.type)) { + highlight.i = currentStep.i; + highlight.j = currentStep.j; + status = + currentStep.type === "swap" + ? `Swap ${currentStep.i} ↔ ${currentStep.j}` + : `Backtrack ${currentStep.i} ↔ ${currentStep.j}`; + } else if (currentStep.type === "recurse") { + status = `Recurse → index ${currentStep.index}`; + } else if (currentStep.type === "output") { + status = "Output permutation"; + } else if (currentStep.type === "start") { + status = "Start"; + } + } + + return ( +