Skip to content

Commit 471941b

Browse files
authored
Merge pull request #50 from NSS-Workshops/develop
Push live
2 parents cb03c69 + a8ac4a1 commit 471941b

3 files changed

Lines changed: 309 additions & 1 deletion

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { TestResult } from "../../utils/test_utils";
2+
3+
export const codeExerciseFixedWindow = {
4+
id: 'a8d1c7e2',
5+
title: 'Module 8 - In Class Code Exercise One',
6+
sectionId: 'sliding-window',
7+
previousChapterId: null,
8+
content: `
9+
Hi 👋,
10+
11+
### Group Exercise Rules
12+
- Be kind 💜
13+
- Make sure everyone gets a chance to contribute
14+
- Ask questions out loud — don’t suffer in silence
15+
- It’s okay to be wrong, mistakes are part of the process
16+
- Focus on learning together, not just getting the answer
17+
18+
## Problem (Fixed Window): Maximum Sum Subarray of Size K
19+
20+
Given an array of integers and a number \`k\`, find the maximum sum of any contiguous subarray of size \`k\`.
21+
22+
### Examples
23+
- Input: \`arr = [2, 1, 5, 1, 3, 2], k = 3\` → Output: \`9\` (subarray \`[5, 1, 3]\`)
24+
- Input: \`arr = [2, 3, 4, 1, 5], k = 2\` → Output: \`7\` (subarray \`[3, 4]\`)
25+
- Input: \`arr = [1, 4, 2, 9, 5], k = 4\` → Output: \`20\` (subarray \`[4, 2, 9, 5]\`)
26+
27+
### Follow-up Questions
28+
- What is the time complexity of your initial (brute-force) solution?
29+
- What is the time complexity after refactoring to sliding window?
30+
- What is the space complexity in both cases?`,
31+
exercise: {
32+
starterCode:`/*
33+
Problem: Maximum Sum Subarray of Size K (Fixed Window)
34+
35+
Given an array of integers and a number k, find the maximum sum of any contiguous subarray of size k.
36+
37+
Brute-force approach (inefficient but correct): try all windows and compute their sums from scratch.
38+
Time: O(n * k), Space: O(1)
39+
*/
40+
41+
function maxSumSubarray(arr, k) {
42+
if (!Array.isArray(arr) || k <= 0 || k > arr.length) return 0;
43+
let max = -Infinity;
44+
for (let start = 0; start + k <= arr.length; start++) {
45+
let sum = 0;
46+
for (let i = start; i < start + k; i++) {
47+
sum += arr[i];
48+
}
49+
if (sum > max) max = sum;
50+
}
51+
return max === -Infinity ? 0 : max;
52+
}`,
53+
solution:`/*
54+
Refactor to Sliding Window:
55+
- Compute sum of first k elements once
56+
- Slide by removing leftmost and adding next rightmost
57+
Time: O(n), Space: O(1)
58+
*/
59+
60+
function maxSumSubarray(arr, k) {
61+
if (!Array.isArray(arr) || k <= 0 || k > arr.length) return 0;
62+
63+
// sum of first window
64+
let windowSum = 0;
65+
for (let i = 0; i < k; i++) windowSum += arr[i];
66+
67+
let maxSum = windowSum;
68+
69+
// slide the window
70+
for (let right = k; right < arr.length; right++) {
71+
windowSum += arr[right] - arr[right - k];
72+
if (windowSum > maxSum) maxSum = windowSum;
73+
}
74+
75+
return maxSum;
76+
}`,
77+
tests:[
78+
{
79+
name: "Basic fixed-window cases",
80+
test: (code) => {
81+
try {
82+
const maxSumSubarray = new Function(`${code}; return maxSumSubarray;`)();
83+
const t1 = maxSumSubarray([2, 1, 5, 1, 3, 2], 3) === 9;
84+
const t2 = maxSumSubarray([2, 3, 4, 1, 5], 2) === 7;
85+
const t3 = maxSumSubarray([1, 4, 2, 9, 5], 4) === 20;
86+
return (t1 && t2 && t3)
87+
? new TestResult({ passed: true })
88+
: new TestResult({ passed: false, message: "Basic cases failed." });
89+
} catch (e) {
90+
return new TestResult({ passed: false, message: `Error: ${e.message}` });
91+
}
92+
},
93+
message: "Function should find maximum sum subarray of size k."
94+
},
95+
{
96+
name: "Negative numbers allowed",
97+
test: (code) => {
98+
try {
99+
const maxSumSubarray = new Function(`${code}; return maxSumSubarray;`)();
100+
const t1 = maxSumSubarray([-1, -2, -3, -4], 2) === -3; // [-1,-2]
101+
const t2 = maxSumSubarray([1, -2, 3, -4, 5], 2) === 1; // [-4,5]
102+
const t3 = maxSumSubarray([-5, -1, -3, -2], 3) === -6; // [-1,-3,-2]
103+
return (t1 && t2 && t3)
104+
? new TestResult({ passed: true })
105+
: new TestResult({ passed: false, message: "Negative number cases failed." });
106+
} catch (e) {
107+
return new TestResult({ passed: false, message: `Error: ${e.message}` });
108+
}
109+
},
110+
message: "Function should handle arrays with negative numbers."
111+
},
112+
{
113+
name: "Edge cases",
114+
test: (code) => {
115+
try {
116+
const maxSumSubarray = new Function(`${code}; return maxSumSubarray;`)();
117+
const t1 = maxSumSubarray([5], 1) === 5;
118+
const t2 = maxSumSubarray([1, 2, 3], 3) === 6;
119+
const t3 = maxSumSubarray([], 1) === 0;
120+
const t4 = maxSumSubarray([1, 2, 3], 0) === 0;
121+
const t5 = maxSumSubarray([1, 2], 3) === 0;
122+
return (t1 && t2 && t3 && t4 && t5)
123+
? new TestResult({ passed: true })
124+
: new TestResult({ passed: false, message: "Edge cases failed." });
125+
} catch (e) {
126+
return new TestResult({ passed: false, message: `Error: ${e.message}` });
127+
}
128+
},
129+
message: "Function should handle edge cases correctly."
130+
},
131+
{
132+
name: "Larger array sanity",
133+
test: (code) => {
134+
try {
135+
const maxSumSubarray = new Function(`${code}; return maxSumSubarray;`)();
136+
const largeArr = [1,2,3,4,5,6,7,8,9,10];
137+
const got = maxSumSubarray(largeArr, 3);
138+
return (got === 27)
139+
? new TestResult({ passed: true })
140+
: new TestResult({ passed: false, message: `Expected 27, got ${got}` });
141+
} catch (e) {
142+
return new TestResult({ passed: false, message: `Error: ${e.message}` });
143+
}
144+
},
145+
message: "Function should work efficiently with larger arrays."
146+
}
147+
]
148+
}
149+
};
150+
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { TestResult } from "../../utils/test_utils";
2+
3+
export const codeExerciseVariableWindow = {
4+
id: 'c3f94b51',
5+
title: 'Module 8 - In Class Code Exercise Two',
6+
sectionId: 'sliding-window',
7+
previousChapterId: null,
8+
content: `
9+
Hi 👋,
10+
11+
### Group Exercise Rules
12+
- Be kind 💜
13+
- Everyone should get hands on the keyboard at some point
14+
- Take turns suggesting ideas before coding
15+
- Celebrate small wins and progress
16+
- Remember: efficiency comes after correctness
17+
18+
## Problem (Variable Window): Smallest Subarray With Sum ≥ Target
19+
20+
Given an array of **positive integers** \`arr\` and a \`target\`, find the **length** of the smallest contiguous subarray whose sum is **greater than or equal** to \`target\`. If no such subarray exists, return \`0\`.
21+
22+
### Examples
23+
- Input: \`arr = [2, 1, 5, 2, 3, 2], target = 7\` → Output: \`2\` (e.g., \`[5,2]\`)
24+
- Input: \`arr = [2, 1, 5, 2, 8], target = 7\` → Output: \`1\` (\`[8]\`)
25+
- Input: \`arr = [1, 1, 1, 1], target = 5\` → Output: \`0\`
26+
27+
### Follow-up Questions
28+
- Why does sliding window work particularly well when all numbers are non-negative?
29+
- What would change if negatives were allowed?`,
30+
exercise: {
31+
starterCode:`/*
32+
Problem: Smallest Subarray With Sum >= Target (Variable Window)
33+
34+
Brute-force (inefficient but correct for positives):
35+
- Try every start index, extend to every end index, compute sum each time.
36+
Time: O(n^2), Space: O(1)
37+
*/
38+
39+
function minSubarrayLenAtLeastTarget(target, arr) {
40+
if (!Array.isArray(arr) || target <= 0) return 0;
41+
42+
let best = Infinity;
43+
44+
for (let start = 0; start < arr.length; start++) {
45+
let sum = 0;
46+
for (let end = start; end < arr.length; end++) {
47+
sum += arr[end];
48+
if (sum >= target) {
49+
const len = end - start + 1;
50+
if (len < best) best = len;
51+
break; // no need to extend further from this start
52+
}
53+
}
54+
}
55+
56+
return best === Infinity ? 0 : best;
57+
}`,
58+
solution:`/*
59+
Refactor to Sliding Window:
60+
Expand right until sum >= target, then shrink left while valid.
61+
Time: O(n), Space: O(1)
62+
*/
63+
64+
function minSubarrayLenAtLeastTarget(target, arr) {
65+
if (!Array.isArray(arr) || target <= 0) return 0;
66+
67+
let left = 0;
68+
let sum = 0;
69+
let best = Infinity;
70+
71+
for (let right = 0; right < arr.length; right++) {
72+
sum += arr[right];
73+
while (sum >= target) {
74+
best = Math.min(best, right - left + 1);
75+
sum -= arr[left];
76+
left++;
77+
}
78+
}
79+
80+
return best === Infinity ? 0 : best;
81+
}`,
82+
tests:[
83+
{
84+
name: "Basic variable-window cases",
85+
test: (code) => {
86+
try {
87+
const minSubarrayLenAtLeastTarget = new Function(`${code}; return minSubarrayLenAtLeastTarget;`)();
88+
const t1 = minSubarrayLenAtLeastTarget(7, [2,1,5,2,3,2]) === 2; // [5,2]
89+
const t2 = minSubarrayLenAtLeastTarget(7, [2,1,5,2,8]) === 1; // [8]
90+
// For [3,4,1,1,6] with target=11: [4,1,1,6] has sum=12 and length=4
91+
const t3 = minSubarrayLenAtLeastTarget(11, [3,4,1,1,6]) === 4; // [4,1,1,6] length 4
92+
return (t1 && t2 && t3)
93+
? new TestResult({ passed: true })
94+
: new TestResult({ passed: false, message: "Basic cases failed." });
95+
} catch (e) {
96+
return new TestResult({ passed: false, message: `Error: ${e.message}` });
97+
}
98+
},
99+
message: "Function should find the smallest subarray length with sum >= target."
100+
},
101+
{
102+
name: "No possible subarray",
103+
test: (code) => {
104+
try {
105+
const fn = new Function(`${code}; return minSubarrayLenAtLeastTarget;`)();
106+
const t1 = fn(5, [1,1,1,1]) === 0;
107+
const t2 = fn(100, [10,20,30]) === 0;
108+
return (t1 && t2)
109+
? new TestResult({ passed: true })
110+
: new TestResult({ passed: false, message: "No-solution cases failed." });
111+
} catch (e) {
112+
return new TestResult({ passed: false, message: `Error: ${e.message}` });
113+
}
114+
},
115+
message: "Should return 0 when no subarray reaches the target."
116+
},
117+
{
118+
name: "Edge & single-element hits",
119+
test: (code) => {
120+
try {
121+
const fn = new Function(`${code}; return minSubarrayLenAtLeastTarget;`)();
122+
const t1 = fn(3, [3]) === 1;
123+
const t2 = fn(3, []) === 0;
124+
const t3 = fn(1, [0,0,0,1]) === 1;
125+
const t4 = fn(7, [7,1,2]) === 1; // single element equals target
126+
return (t1 && t2 && t3 && t4)
127+
? new TestResult({ passed: true })
128+
: new TestResult({ passed: false, message: "Edge cases failed." });
129+
} catch (e) {
130+
return new TestResult({ passed: false, message: `Error: ${e.message}` });
131+
}
132+
},
133+
message: "Handles empty arrays, single hits, and zeros."
134+
},
135+
{
136+
name: "Larger array sanity",
137+
test: (code) => {
138+
try {
139+
const fn = new Function(`${code}; return minSubarrayLenAtLeastTarget;`)();
140+
// Let's construct a deterministic case:
141+
const arr = [1,2,3,4,0,0,0,5,5,1]; // minimal for target=10 is 2 ([5,5])
142+
const got = fn(10, arr);
143+
return (got === 2)
144+
? new TestResult({ passed: true })
145+
: new TestResult({ passed: false, message: `Expected 2, got ${got}` });
146+
} catch (e) {
147+
return new TestResult({ passed: false, message: `Error: ${e.message}` });
148+
}
149+
},
150+
message: "Works efficiently on larger arrays."
151+
}
152+
]
153+
}
154+
};

src/chapters/sliding-window/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { glossaryChapter } from './glossary';
66
import { slidingWindowCheckpointChapter } from './checkpoint';
77
import { codeExcerciseOneChapter } from './6fd72967';
88
import { codeExcerciseTwoChapter } from './e49003dd';
9+
import { codeExerciseFixedWindow } from './a8d1c7e2';
10+
import { codeExerciseVariableWindow } from './c3f94b51';
911

1012
export const slidingWindowChapters = [
1113
slidingWindowLearningObjectivesChapter,
@@ -15,7 +17,9 @@ export const slidingWindowChapters = [
1517
glossaryChapter,
1618
slidingWindowCheckpointChapter,
1719
codeExcerciseOneChapter,
18-
codeExcerciseTwoChapter
20+
codeExcerciseTwoChapter,
21+
codeExerciseVariableWindow,
22+
codeExerciseFixedWindow
1923
];
2024

2125
/**

0 commit comments

Comments
 (0)