Skip to content

Commit 07abc76

Browse files
Merge branch 'main' into feature/LIS
2 parents fc17c4d + 27fb8ff commit 07abc76

File tree

13 files changed

+1123
-3
lines changed

13 files changed

+1123
-3
lines changed

package-lock.json

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export function floodFill(mat, x, y, targetColor, newColor, steps = []) {
2+
const rows = mat.length;
3+
const cols = mat[0].length;
4+
5+
if (x < 0 || y < 0 || x >= rows || y >= cols) return;
6+
if (mat[x][y] !== targetColor) return;
7+
8+
steps.push({ x, y });
9+
10+
mat[x][y] = newColor;
11+
12+
floodFill(mat, x + 1, y, targetColor, newColor, steps);
13+
floodFill(mat, x - 1, y, targetColor, newColor, steps);
14+
floodFill(mat, x, y + 1, targetColor, newColor, steps);
15+
floodFill(mat, x, y - 1, targetColor, newColor, steps);
16+
17+
return steps;
18+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
export function rodCuttingTopDown(prices) {
2+
const n = prices.length;
3+
const memo = new Array(n + 1).fill(null);
4+
const steps = [];
5+
6+
function solve(k) {
7+
steps.push({
8+
array: [...memo],
9+
currentIndex: k,
10+
readingIndices: [],
11+
priceIndex: null,
12+
message: `Calling solve(k = ${k}).`
13+
});
14+
15+
if (k === 0) {
16+
steps.push({
17+
array: [...memo],
18+
currentIndex: k,
19+
readingIndices: [],
20+
priceIndex: null,
21+
message: `Base case: solve(0) = 0.`
22+
});
23+
return 0;
24+
}
25+
26+
if (memo[k] !== null) {
27+
steps.push({
28+
array: [...memo],
29+
currentIndex: k,
30+
readingIndices: [],
31+
priceIndex: null,
32+
message: `Memo hit: solve(${k}) is already computed as ${memo[k]}.`
33+
});
34+
return memo[k];
35+
}
36+
37+
let currentMaxProfit = -1;
38+
39+
for (let j = 1; j <= k; j++) {
40+
steps.push({
41+
array: [...memo],
42+
currentIndex: k,
43+
readingIndices: [],
44+
priceIndex: j - 1,
45+
message: `... for k = ${k}, trying cut j = ${j}. Need to find solve(${k - j}).`
46+
});
47+
48+
const remainingProfit = solve(k - j);
49+
let profit = prices[j - 1] + remainingProfit;
50+
51+
steps.push({
52+
array: [...memo],
53+
currentIndex: k,
54+
readingIndices: [k - j],
55+
priceIndex: j - 1,
56+
message: `... cut j = ${j} gives profit = prices[${j - 1}] (${prices[j-1]}) + solve(${k - j}) (${remainingProfit}) = ${profit}.`
57+
});
58+
59+
if (profit > currentMaxProfit) {
60+
currentMaxProfit = profit;
61+
}
62+
}
63+
64+
memo[k] = currentMaxProfit;
65+
66+
steps.push({
67+
array: [...memo],
68+
currentIndex: k,
69+
readingIndices: [],
70+
priceIndex: null,
71+
message: `Computed solve(${k}) = ${currentMaxProfit}. Storing in memo[${k}].`
72+
});
73+
74+
return currentMaxProfit;
75+
}
76+
77+
const result = solve(n);
78+
79+
steps.push({
80+
array: [...memo],
81+
currentIndex: n,
82+
readingIndices: [],
83+
priceIndex: null,
84+
message: `Computation complete. Max profit for a rod of length ${n} is ${result}.`
85+
});
86+
87+
return { steps, result };
88+
}
89+
90+
91+
export function rodCuttingBottomUp(prices) {
92+
const n = prices.length;
93+
const dp = new Array(n + 1).fill(0);
94+
const steps = [];
95+
96+
steps.push({
97+
array: [...dp],
98+
currentIndex: 0,
99+
readingIndices: [],
100+
priceIndex: null,
101+
message: `Base case: Max profit for length 0 is 0. dp[0] = 0.`
102+
});
103+
104+
for (let i = 1; i <= n; i++) {
105+
let currentMaxProfit = -1;
106+
107+
steps.push({
108+
array: [...dp],
109+
currentIndex: i,
110+
readingIndices: [],
111+
priceIndex: null,
112+
message: `Calculating max profit for length i = ${i}.`
113+
});
114+
115+
for (let j = 1; j <= i; j++) {
116+
let profit = prices[j - 1] + dp[i - j];
117+
118+
steps.push({
119+
array: [...dp],
120+
currentIndex: i,
121+
readingIndices: [i - j],
122+
priceIndex: j - 1,
123+
message: `... trying cut j = ${j}. Profit = prices[${j - 1}] (${prices[j-1]}) + dp[${i - j}] (${dp[i-j]}) = ${profit}.`
124+
});
125+
126+
if (profit > currentMaxProfit) {
127+
currentMaxProfit = profit;
128+
}
129+
}
130+
131+
dp[i] = currentMaxProfit;
132+
133+
steps.push({
134+
array: [...dp],
135+
currentIndex: i,
136+
readingIndices: [],
137+
priceIndex: null,
138+
message: `Max profit for length ${i} is ${currentMaxProfit}. Storing in dp[${i}].`
139+
});
140+
}
141+
142+
steps.push({
143+
array: [...dp],
144+
currentIndex: n,
145+
readingIndices: [],
146+
priceIndex: null,
147+
message: `Computation complete. Max profit for a rod of length ${n} is ${dp[n]}.`
148+
});
149+
150+
return { steps, result: dp[n] };
151+
}

src/algorithms/graph/prim.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// src/algorithms/graph/prim.js
2+
3+
// Build adjacency list from list of edges
4+
function buildAdjacency(edges, n) {
5+
const adj = Array.from({ length: n }, () => []);
6+
for (const e of edges) {
7+
const u = e.from - 1;
8+
const v = e.to - 1;
9+
const w = e.weight;
10+
11+
adj[u].push({ u, v, w });
12+
adj[v].push({ u: v, v: u, w }); // undirected graph
13+
}
14+
return adj;
15+
}
16+
17+
/**
18+
* Prim's Algorithm – Step Generator
19+
* Yields one step at a time:
20+
* type: "consider" | "add" | "skip" | "done"
21+
* edge: {from, to, weight}
22+
* visited: boolean[]
23+
* frontier: edge[]
24+
* mst: collected MST edges
25+
*/
26+
export function* primSteps(edges, nodeCount, startNode = 1) {
27+
if (nodeCount === 0) {
28+
yield { type: "done", mst: [], visited: [], frontier: [] };
29+
return;
30+
}
31+
32+
const adj = buildAdjacency(edges, nodeCount);
33+
const visited = Array(nodeCount).fill(false);
34+
const mst = [];
35+
const frontier = [];
36+
37+
const pushEdges = (u) => {
38+
for (const { v, w } of adj[u]) {
39+
if (!visited[v]) {
40+
frontier.push({ from: u + 1, to: v + 1, weight: w });
41+
}
42+
}
43+
};
44+
45+
const startIdx = Math.max(1, Math.min(startNode, nodeCount)) - 1;
46+
visited[startIdx] = true;
47+
pushEdges(startIdx);
48+
49+
while (mst.length < nodeCount - 1 && frontier.length > 0) {
50+
frontier.sort((a, b) => a.weight - b.weight);
51+
const edge = frontier.shift();
52+
53+
yield {
54+
type: "consider",
55+
edge,
56+
visited: [...visited],
57+
frontier: [...frontier],
58+
mst: [...mst],
59+
};
60+
61+
const u = edge.from - 1;
62+
const v = edge.to - 1;
63+
64+
if (visited[u] && visited[v]) {
65+
yield {
66+
type: "skip",
67+
edge,
68+
visited: [...visited],
69+
frontier: [...frontier],
70+
mst: [...mst],
71+
};
72+
continue;
73+
}
74+
75+
const nextNode = visited[u] ? v : u;
76+
visited[nextNode] = true;
77+
78+
mst.push(edge);
79+
pushEdges(nextNode);
80+
81+
yield {
82+
type: "add",
83+
edge,
84+
visited: [...visited],
85+
frontier: [...frontier],
86+
mst: [...mst],
87+
};
88+
}
89+
90+
yield {
91+
type: "done",
92+
mst: [...mst],
93+
visited: [...visited],
94+
frontier: [...frontier],
95+
};
96+
}

0 commit comments

Comments
 (0)