Skip to content

Commit fc17c4d

Browse files
committed
LIS added
1 parent f0b4e46 commit fc17c4d

File tree

4 files changed

+264
-3
lines changed

4 files changed

+264
-3
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
export function longestIncreasingSubsequenceSteps(arr = []) {
2+
const n = arr.length;
3+
const dp = Array(n).fill(1);
4+
const parent = Array(n).fill(-1);
5+
const steps = [];
6+
7+
const snapshot = () => [...dp];
8+
9+
// Fill dp array
10+
for (let i = 0; i < n; i++) {
11+
for (let j = 0; j < i; j++) {
12+
if (arr[j] < arr[i] && dp[j] + 1 > dp[i]) {
13+
parent[i] = j;
14+
dp[i] = dp[j] + 1;
15+
steps.push({
16+
dp: snapshot(),
17+
active: { i, j },
18+
match: { i, j },
19+
message: `arr[${j}] (${arr[j]}) < arr[${i}] (${arr[i]}) → dp[${i}] = dp[${j}] + 1 = ${dp[i]}`
20+
});
21+
} else {
22+
steps.push({
23+
dp: snapshot(),
24+
active: { i, j },
25+
message: `arr[${j}] (${arr[j]}) >= arr[${i}] (${arr[i]}) → no update, dp[${i}] remains ${dp[i]}`
26+
});
27+
}
28+
}
29+
}
30+
31+
// Find LIS length and reconstruct sequence
32+
let lisLen = 0, lisEnd = 0;
33+
for (let i = 0; i < n; i++) {
34+
if (dp[i] > lisLen) {
35+
lisLen = dp[i];
36+
lisEnd = i;
37+
}
38+
}
39+
40+
const sequence = [];
41+
let t = lisEnd;
42+
while (t !== -1) {
43+
sequence.push(arr[t]);
44+
t = parent[t];
45+
}
46+
sequence.reverse();
47+
48+
steps.push({
49+
dp: snapshot(),
50+
message: `Final LIS sequence = [${sequence.join(', ')}], length = ${lisLen}`,
51+
sequence,
52+
});
53+
54+
return steps;
55+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import React, { useState, useEffect, useRef } from "react";
2+
import { longestIncreasingSubsequenceSteps } from "../../algorithms/dynamic-programming/longestIncreasingSubsequence";
3+
4+
const DPArray = ({ dp, active }) => {
5+
if (!dp || dp.length === 0) return null;
6+
7+
return (
8+
<div className="mt-4 overflow-auto">
9+
<h3 className="text-xl font-semibold mb-3 text-blue-400">
10+
DP Array (LIS Lengths)
11+
</h3>
12+
<div className="inline-flex gap-2 border rounded-lg bg-gray-800 p-3">
13+
{dp.map((val, i) => {
14+
const isActive = active && active.i === i;
15+
const color = isActive ? "bg-blue-600" : "bg-gray-700";
16+
return (
17+
<div
18+
key={i}
19+
className={`w-14 h-12 flex items-center justify-center text-white font-semibold border ${color} rounded-md`}
20+
>
21+
{val}
22+
</div>
23+
);
24+
})}
25+
</div>
26+
</div>
27+
);
28+
};
29+
30+
const SPEEDS = { Slow: 1200, Medium: 600, Fast: 250 };
31+
32+
export default function LISVisualizer() {
33+
const [input, setInput] = useState("10,22,9,33,21,50,41,60");
34+
const [steps, setSteps] = useState([]);
35+
const [stepIndex, setStepIndex] = useState(0);
36+
const [isPlaying, setIsPlaying] = useState(false);
37+
const [speed, setSpeed] = useState(SPEEDS.Medium);
38+
const timer = useRef(null);
39+
40+
const handleStart = () => {
41+
const arr = input
42+
.split(",")
43+
.map(x => parseInt(x.trim()))
44+
.filter(x => !isNaN(x));
45+
const s = longestIncreasingSubsequenceSteps(arr);
46+
setSteps(s);
47+
setStepIndex(0);
48+
setIsPlaying(false);
49+
};
50+
51+
useEffect(() => {
52+
if (isPlaying && stepIndex < steps.length - 1) {
53+
timer.current = setInterval(() => {
54+
setStepIndex(i => i + 1);
55+
}, speed);
56+
} else clearInterval(timer.current);
57+
58+
return () => clearInterval(timer.current);
59+
}, [isPlaying, stepIndex, steps.length, speed]);
60+
61+
const togglePlay = () => {
62+
if (stepIndex === steps.length - 1) setStepIndex(0);
63+
setIsPlaying(!isPlaying);
64+
};
65+
66+
const current = steps[stepIndex] || {};
67+
const finalLIS =
68+
stepIndex === steps.length - 1 ? current.sequence?.join(", ") : "";
69+
70+
return (
71+
<div className="p-6 min-h-screen bg-gray-900 text-gray-100 font-sans">
72+
<div className="max-w-5xl mx-auto">
73+
<h1 className="text-4xl font-extrabold mb-8 text-indigo-400 text-center">
74+
Longest Increasing Subsequence (LIS)
75+
</h1>
76+
77+
<div className="mb-8 p-4 rounded-xl bg-gray-800 border border-gray-700">
78+
<p className="text-gray-300">
79+
This visualizer demonstrates how the LIS DP array is updated
80+
step-by-step and how the final increasing subsequence is derived.
81+
</p>
82+
</div>
83+
84+
<div className="flex flex-wrap justify-center items-center gap-5 mb-8 p-6 rounded-xl bg-gray-800 border border-gray-700">
85+
<div className="w-full md:w-2/3">
86+
<label className="text-gray-300">Input Array (comma separated):</label>
87+
<input
88+
value={input}
89+
onChange={e => setInput(e.target.value)}
90+
className="w-full mt-2 p-2 rounded-lg bg-gray-700 border border-gray-600 text-white"
91+
/>
92+
</div>
93+
94+
<button
95+
onClick={handleStart}
96+
className="bg-indigo-600 hover:bg-indigo-700 text-white font-bold px-6 py-3 rounded-xl"
97+
>
98+
Start Visualization
99+
</button>
100+
</div>
101+
102+
{steps.length > 0 ? (
103+
<>
104+
<div className="flex flex-wrap justify-between items-center mb-6 p-4 bg-gray-800 border border-gray-700 rounded-xl">
105+
<button
106+
onClick={togglePlay}
107+
className={`px-5 py-2 rounded-lg font-semibold ${isPlaying ? "bg-red-600" : "bg-green-600"
108+
} text-white`}
109+
>
110+
{isPlaying ? "Pause ⏸️" : "Play ▶️"}
111+
</button>
112+
113+
<div className="flex gap-2">
114+
<button
115+
onClick={() => setStepIndex(i => Math.max(0, i - 1))}
116+
className={`px-3 py-2 rounded-lg font-semibold ${stepIndex > 0
117+
? "bg-purple-600 text-white"
118+
: "bg-gray-600 text-gray-400"
119+
}`}
120+
>
121+
&lt; Prev
122+
</button>
123+
<button
124+
onClick={() =>
125+
setStepIndex(i =>
126+
i < steps.length - 1 ? i + 1 : steps.length - 1
127+
)
128+
}
129+
className={`px-3 py-2 rounded-lg font-semibold ${stepIndex < steps.length - 1
130+
? "bg-purple-600 text-white"
131+
: "bg-gray-600 text-gray-400"
132+
}`}
133+
>
134+
Next &gt;
135+
</button>
136+
</div>
137+
138+
<div className="flex items-center gap-2">
139+
<label className="text-gray-300">Speed:</label>
140+
<select
141+
value={speed}
142+
onChange={e => setSpeed(Number(e.target.value))}
143+
className="p-2 rounded-lg bg-gray-700 text-white border border-gray-600"
144+
>
145+
{Object.entries(SPEEDS).map(([label, ms]) => (
146+
<option key={label} value={ms}>
147+
{label}
148+
</option>
149+
))}
150+
</select>
151+
</div>
152+
</div>
153+
154+
<div className="text-center mb-4">
155+
<p className="text-2xl font-bold text-yellow-400">
156+
Step {stepIndex + 1} / {steps.length}
157+
</p>
158+
</div>
159+
160+
<div className="border border-gray-700 p-6 rounded-xl bg-gray-800 shadow-2xl">
161+
<div className="mb-6 p-4 rounded-lg bg-gray-700 border-l-4 border-teal-400">
162+
<p className="text-teal-400 font-semibold uppercase">
163+
Current Action
164+
</p>
165+
<p className="text-lg mt-2 text-gray-200 leading-relaxed">
166+
{current.message || "Processing..."}
167+
</p>
168+
</div>
169+
170+
{current.dp && <DPArray dp={current.dp} active={current.active} />}
171+
172+
{finalLIS && (
173+
<div className="mt-8 p-5 rounded-xl bg-green-900 border border-green-700 text-center shadow-lg">
174+
<p className="text-green-400 text-2xl font-extrabold">
175+
Final LIS ={" "}
176+
<span className="text-green-200 text-3xl">{finalLIS}</span>
177+
</p>
178+
</div>
179+
)}
180+
</div>
181+
</>
182+
) : (
183+
<div className="text-center p-12 bg-gray-800 rounded-xl text-gray-400 text-xl border border-gray-700">
184+
<p className="mb-4">Welcome to the LIS Visualizer!</p>
185+
<p>Enter an array and click “Start Visualization”.</p>
186+
</div>
187+
)}
188+
</div>
189+
</div>
190+
);
191+
}

src/pages/dynamic-programming/DyanmicProgrammingPage.jsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import FibonacciSequence from "./FibonacciSequence";
66
import Knapsack from "./Knapsack";
77
import PascalTriangle from "./PascalTriangle";
88
import LCSPage from "./LCS";
9-
import CoinChange from "./CoinChange"; // ✅ Added import
9+
import LISPage from "./LIS";
10+
import CoinChange from "./CoinChange";
11+
1012

1113
export default function DynamicProgrammingPage() {
1214
const [selectedAlgo, setSelectedAlgo] = useState("");
@@ -50,7 +52,13 @@ export default function DynamicProgrammingPage() {
5052
<LCSPage />
5153
</div>
5254
);
53-
case "CoinChange": // ✅ Added new algorithm case
55+
case "LongestIncreasingSubsequence":
56+
return (
57+
<div className="md:w-full w-screen overflow-clip p-2">
58+
<LISPage />
59+
</div>
60+
);
61+
case "CoinChange":
5462
return (
5563
<div className="md:w-full w-screen overflow-clip p-2">
5664
<CoinChange />
@@ -120,7 +128,8 @@ export default function DynamicProgrammingPage() {
120128
<option value="Knapsack">Knapsack</option>
121129
<option value="PascalTriangle">Pascal Triangle</option>
122130
<option value="LongestCommonSubsequence">Longest Common Subsequence</option>
123-
<option value="CoinChange">Coin Change</option> {/* ✅ Added */}
131+
<option value="LongestIncreasingSubsequence">Longest Increasing Subsequence</option>
132+
<option value="CoinChange">Coin Change</option>
124133
</select>
125134

126135
<button
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from "react";
2+
import LISVisualizer from "@/components/dynamic-programming/LISVisualizer";
3+
4+
export default function LISPage() {
5+
return <LISVisualizer />;
6+
}

0 commit comments

Comments
 (0)