From fc17c4dcdf42ffc05a3b9e423ed2380e4e22d6b7 Mon Sep 17 00:00:00 2001 From: LONECODER1 Date: Fri, 7 Nov 2025 13:27:27 +0530 Subject: [PATCH] LIS added --- .../longestIncreasingSubsequence.js | 55 +++++ .../dynamic-programming/LISVisualizer.jsx | 191 ++++++++++++++++++ .../DyanmicProgrammingPage.jsx | 15 +- src/pages/dynamic-programming/LIS.jsx | 6 + 4 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 src/algorithms/dynamic-programming/longestIncreasingSubsequence.js create mode 100644 src/components/dynamic-programming/LISVisualizer.jsx create mode 100644 src/pages/dynamic-programming/LIS.jsx diff --git a/src/algorithms/dynamic-programming/longestIncreasingSubsequence.js b/src/algorithms/dynamic-programming/longestIncreasingSubsequence.js new file mode 100644 index 0000000..4a9cc1d --- /dev/null +++ b/src/algorithms/dynamic-programming/longestIncreasingSubsequence.js @@ -0,0 +1,55 @@ +export function longestIncreasingSubsequenceSteps(arr = []) { + const n = arr.length; + const dp = Array(n).fill(1); + const parent = Array(n).fill(-1); + const steps = []; + + const snapshot = () => [...dp]; + + // Fill dp array + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (arr[j] < arr[i] && dp[j] + 1 > dp[i]) { + parent[i] = j; + dp[i] = dp[j] + 1; + steps.push({ + dp: snapshot(), + active: { i, j }, + match: { i, j }, + message: `arr[${j}] (${arr[j]}) < arr[${i}] (${arr[i]}) → dp[${i}] = dp[${j}] + 1 = ${dp[i]}` + }); + } else { + steps.push({ + dp: snapshot(), + active: { i, j }, + message: `arr[${j}] (${arr[j]}) >= arr[${i}] (${arr[i]}) → no update, dp[${i}] remains ${dp[i]}` + }); + } + } + } + + // Find LIS length and reconstruct sequence + let lisLen = 0, lisEnd = 0; + for (let i = 0; i < n; i++) { + if (dp[i] > lisLen) { + lisLen = dp[i]; + lisEnd = i; + } + } + + const sequence = []; + let t = lisEnd; + while (t !== -1) { + sequence.push(arr[t]); + t = parent[t]; + } + sequence.reverse(); + + steps.push({ + dp: snapshot(), + message: `Final LIS sequence = [${sequence.join(', ')}], length = ${lisLen}`, + sequence, + }); + + return steps; +} diff --git a/src/components/dynamic-programming/LISVisualizer.jsx b/src/components/dynamic-programming/LISVisualizer.jsx new file mode 100644 index 0000000..d0bc478 --- /dev/null +++ b/src/components/dynamic-programming/LISVisualizer.jsx @@ -0,0 +1,191 @@ +import React, { useState, useEffect, useRef } from "react"; +import { longestIncreasingSubsequenceSteps } from "../../algorithms/dynamic-programming/longestIncreasingSubsequence"; + +const DPArray = ({ dp, active }) => { + if (!dp || dp.length === 0) return null; + + return ( +
+

+ DP Array (LIS Lengths) +

+
+ {dp.map((val, i) => { + const isActive = active && active.i === i; + const color = isActive ? "bg-blue-600" : "bg-gray-700"; + return ( +
+ {val} +
+ ); + })} +
+
+ ); +}; + +const SPEEDS = { Slow: 1200, Medium: 600, Fast: 250 }; + +export default function LISVisualizer() { + const [input, setInput] = useState("10,22,9,33,21,50,41,60"); + const [steps, setSteps] = useState([]); + const [stepIndex, setStepIndex] = useState(0); + const [isPlaying, setIsPlaying] = useState(false); + const [speed, setSpeed] = useState(SPEEDS.Medium); + const timer = useRef(null); + + const handleStart = () => { + const arr = input + .split(",") + .map(x => parseInt(x.trim())) + .filter(x => !isNaN(x)); + const s = longestIncreasingSubsequenceSteps(arr); + setSteps(s); + setStepIndex(0); + setIsPlaying(false); + }; + + useEffect(() => { + if (isPlaying && stepIndex < steps.length - 1) { + timer.current = setInterval(() => { + setStepIndex(i => i + 1); + }, speed); + } else clearInterval(timer.current); + + return () => clearInterval(timer.current); + }, [isPlaying, stepIndex, steps.length, speed]); + + const togglePlay = () => { + if (stepIndex === steps.length - 1) setStepIndex(0); + setIsPlaying(!isPlaying); + }; + + const current = steps[stepIndex] || {}; + const finalLIS = + stepIndex === steps.length - 1 ? current.sequence?.join(", ") : ""; + + return ( +
+
+

+ Longest Increasing Subsequence (LIS) +

+ +
+

+ This visualizer demonstrates how the LIS DP array is updated + step-by-step and how the final increasing subsequence is derived. +

+
+ +
+
+ + setInput(e.target.value)} + className="w-full mt-2 p-2 rounded-lg bg-gray-700 border border-gray-600 text-white" + /> +
+ + +
+ + {steps.length > 0 ? ( + <> +
+ + +
+ + +
+ +
+ + +
+
+ +
+

+ Step {stepIndex + 1} / {steps.length} +

+
+ +
+
+

+ Current Action +

+

+ {current.message || "Processing..."} +

+
+ + {current.dp && } + + {finalLIS && ( +
+

+ Final LIS ={" "} + {finalLIS} +

+
+ )} +
+ + ) : ( +
+

Welcome to the LIS Visualizer!

+

Enter an array and click “Start Visualization”.

+
+ )} +
+
+ ); +} diff --git a/src/pages/dynamic-programming/DyanmicProgrammingPage.jsx b/src/pages/dynamic-programming/DyanmicProgrammingPage.jsx index 13744ed..4e9139f 100644 --- a/src/pages/dynamic-programming/DyanmicProgrammingPage.jsx +++ b/src/pages/dynamic-programming/DyanmicProgrammingPage.jsx @@ -6,7 +6,9 @@ import FibonacciSequence from "./FibonacciSequence"; import Knapsack from "./Knapsack"; import PascalTriangle from "./PascalTriangle"; import LCSPage from "./LCS"; -import CoinChange from "./CoinChange"; // ✅ Added import +import LISPage from "./LIS"; +import CoinChange from "./CoinChange"; + export default function DynamicProgrammingPage() { const [selectedAlgo, setSelectedAlgo] = useState(""); @@ -50,7 +52,13 @@ export default function DynamicProgrammingPage() { ); - case "CoinChange": // ✅ Added new algorithm case + case "LongestIncreasingSubsequence": + return ( +
+ +
+ ); + case "CoinChange": return (
@@ -120,7 +128,8 @@ export default function DynamicProgrammingPage() { - {/* ✅ Added */} + +