feat(curriculum): add JS BFS workshop (#65848)

Co-authored-by: Jeevankumar S <110320697+Jeevankumar-s@users.noreply.github.com>
Co-authored-by: majestic-owl448 <26656284+majestic-owl448@users.noreply.github.com>
Co-authored-by: Kolade <chrisjay967@gmail.com>
This commit is contained in:
Zaira
2026-03-31 16:33:11 +05:00
committed by GitHub
parent 20fa1de878
commit 81a9867dd4
17 changed files with 822 additions and 0 deletions

View File

@@ -5707,6 +5707,12 @@
"In this lab, you will implement a function that converts an adjacency list representation of a graph into an adjacency matrix representation."
]
},
"workshop-breadth-first-search-js": {
"title": "Implement the Breadth-First Search Algorithm",
"intro": [
"In this workshop, you will use the breadth-first search algorithm to generate all valid combinations of parentheses."
]
},
"lab-depth-first-search-js": {
"title": "Implement the Depth-First Search Algorithm",
"intro": [

View File

@@ -0,0 +1,44 @@
---
id: 6989eb65324d97775c4965fe
title: Step 1
challengeType: 1
dashedName: step-1
---
# --description--
In this workshop, you'll implement a function that generates all valid combinations of parentheses using a breadth-first search (BFS) approach. For example, the valid combinations of two pairs of parentheses are `(())` and `()()`.
Start by creating a function named `genParentheses` with a single parameter `pairs`. For now, return an empty array from the function.
# --hints--
You should define a function named `genParentheses`.
```js
assert.isFunction(genParentheses);
```
Your `genParentheses` function should have a single parameter named `pairs`.
```js
const genParenthesesParams = __helpers.getFunctionParams(genParentheses.toString());
assert.equal(genParenthesesParams[0].name, 'pairs')
```
Your function should return an empty array.
```js
assert.isArray(genParentheses(1));
assert.lengthOf(genParentheses(1), 0);
```
# --seed--
## --seed-contents--
```js
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,50 @@
---
id: 6989ef885db3a3488a13b426
title: Step 2
challengeType: 1
dashedName: step-2
---
# --description--
Before implementing the core algorithm, you need to validate the input. The `pairs` parameter should be an integer, as it represents the number of parentheses pairs to generate.
Add an `if` statement at the beginning of your function to check if `pairs` is not an integer. Use the `Number.isInteger()` method for that.
If the condition is true, return the string `The number of pairs should be an integer`.
# --hints--
You should have an if statement with the condition `!Number.isInteger(pairs)` inside the `genParentheses` function.
```js
assert.match(genParentheses.toString(), /if\s*\(\s*!\s*Number\.isInteger\s*\(\s*pairs\s*\)\s*\)/);
```
You should return a string when the input is not an integer.
```js
let result = genParentheses("i");
assert.isString(result);
```
You should return the string `The number of pairs should be an integer` when the input is a string.
```js
let result = genParentheses("i");
assert.strictEqual(result, 'The number of pairs should be an integer');
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
--fcc-editable-region--
--fcc-editable-region--
return [];
}
```

View File

@@ -0,0 +1,43 @@
---
id: 6989f10a72ad424d4ad6d514
title: Step 3
challengeType: 1
dashedName: step-3
---
# --description--
Next, you need to validate that the number of pairs is at least one, since you can't generate parentheses combinations with zero or negative pairs.
Add another `if` statement to check if `pairs` is less than `1`. If this condition is true, return the string `The number of pairs should be at least 1`.
# --hints--
You should have a second `if` statement with the condition `pairs < 1`.
```js
assert.match(genParentheses.toString(), /if\s*\(\s*pairs\s*<\s*1\s*\)/);
```
You should return the string `The number of pairs should be at least 1` when `pairs` is less than `1`.
```js
let result = genParentheses(0);
assert.strictEqual(result, 'The number of pairs should be at least 1');
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
--fcc-editable-region--
--fcc-editable-region--
return [];
}
```

View File

@@ -0,0 +1,51 @@
---
id: 6989f1ba358acba651d5ee6b
title: Step 4
challengeType: 1
dashedName: step-4
---
# --description--
Now you'll set up the data structure to store your results. Create a variable named `result` and initialize it to an empty array. This array will store all the valid parentheses combinations you generate.
Update your return statement to return `result` instead of an empty array.
# --hints--
You should declare a variable named `result` in your `genParentheses` function.
```js
assert.match(genParentheses.toString(), /result/);
```
You should initialize `result` to an empty array.
```js
assert.match(genParentheses.toString(), /result\s*=\s*\[\]/);
```
You should return `result` at the end of your function.
```js
assert.match(genParentheses.toString(), /return\s*result/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
--fcc-editable-region--
return [];
--fcc-editable-region--
}
```

View File

@@ -0,0 +1,50 @@
---
id: 6989f207306332c06b0add39
title: Step 5
challengeType: 1
dashedName: step-5
---
# --description--
For the breadth-first search approach, you'll use a queue to track different states as you build the parentheses combinations. Each state will be represented as an array containing three elements:
- The current string being built
- The number of opening parentheses used so far
- The number of closing parentheses used so far
Create a variable named `queue` and initialize it to an array containing one array: `['', 0, 0]`. This represents the starting state with an empty string and zero parentheses used.
# --hints--
You should declare a variable named `queue` in your `genParentheses` function.
```js
assert.match(genParentheses.toString(), /queue/);
```
You should initialize `queue` with an array containing the array `['', 0, 0]`.
```js
assert.match(genParentheses.toString(), /queue\s*=\s*\[\s*\['',\s*0,\s*0\]\s*\]/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
--fcc-editable-region--
--fcc-editable-region--
let result = [];
return result;
}
```

View File

@@ -0,0 +1,49 @@
---
id: 6989f2e35e6941e13e445156
title: Step 6
challengeType: 1
dashedName: step-6
---
# --description--
Now you'll implement the main BFS loop. Create a `while` loop that continues as long as the `queue` is not empty, that is, `queue.length` is greater than `0`.
Inside the loop, log `queue` to the console.
# --hints--
You should create a `while` loop with the condition `queue.length > 0`.
```js
assert.match(genParentheses.toString(), /while\s*\(\s*queue\.length\s*>\s*0\s*\)/);
```
You should log `queue` to the console inside your `while` loop.
```js
assert.match(genParentheses.toString(), /while\s*\(\s*queue\.length\s*>\s*0\s*\)/);
assert.match(genParentheses.toString(), /console\.log\s*\(\s*queue\s*\)/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
--fcc-editable-region--
--fcc-editable-region--
return result;
}
```

View File

@@ -0,0 +1,56 @@
---
id: 6989f3632f8b8bdf168aa321
title: Step 7
challengeType: 1
dashedName: step-7
---
# --description--
Inside your `while` loop, use `queue.shift()` to remove and get the first element from the queue. This implements the first-in-first-out (FIFO) behavior characteristic of BFS.
Destructure this array into three variables: `current`, `opensUsed`, and `closesUsed`.
# --hints--
You should use `queue.shift()` to get the first element from the queue.
```js
assert.match(genParentheses.toString(), /queue\.shift\s*\(\s*\)/);
```
You should destructure the result into three variables: `current`, `opensUsed`, and `closesUsed`.
```js
assert.match(genParentheses.toString(), /opensUsed/);
assert.match(genParentheses.toString(), /closesUsed/);
assert.match(genParentheses.toString(), /current/);
assert.match(genParentheses.toString(), /queue\.shift\s*\(\s*\)/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
console.log(queue);
--fcc-editable-region--
--fcc-editable-region--
}
return result;
}
```

View File

@@ -0,0 +1,47 @@
---
id: 6989f46b6e9366b0e6ad32fe
title: Step 8
challengeType: 1
dashedName: step-8
---
# --description--
Before you keep working on the BFS logic, call `genParentheses(1)` and log the result to the console.
# --hints--
You should log `genParentheses(1)` to the console.
```js
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*genParentheses\s*\(\s*1\s*\)\s*\)/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
console.log(queue);
let [current, opensUsed, closesUsed] = queue.shift();
}
return result;
}
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,55 @@
---
id: 6989f48c3f0292c84f8d8938
title: Step 9
challengeType: 1
dashedName: step-9
---
# --description--
Now you need to identify when you've built a complete parentheses combination. A complete combination has a length equal to twice the number of pairs (since each pair contributes one opening and one closing parenthesis).
Inside your `while` loop, add an `if` statement to check if `current.length === 2 * pairs`. When this condition is true, push `current` to the `result` array.
# --hints--
You should have an `if` statement with the condition `current.length === 2 * pairs` inside your `while` loop.
```js
assert.match(genParentheses.toString(), /if\s*\(\s*current\.length\s*===?\s*2\s*\*\s*pairs\s*\)/);
```
You should push `current` to the `result` array inside your new `if` statement.
```js
assert.match(genParentheses.toString(), /if\s*\(\s*current\.length\s*===?\s*2\s*\*\s*pairs\s*\)\s*\{[\s\S]*?result\.push\s*\(\s*current\s*\)/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
console.log(queue);
let [current, opensUsed, closesUsed] = queue.shift();
--fcc-editable-region--
--fcc-editable-region--
}
return result;
}
console.log(genParentheses(1));
```

View File

@@ -0,0 +1,65 @@
---
id: 6989f555728e63ad8b349d84
title: Step 10
challengeType: 1
dashedName: step-10
---
# --description--
If the current string isn't complete yet, you need to explore the next possible states. Add an `else` clause for the current `if` statement.
Inside the `else` block, you'll handle adding opening parentheses. Add an `if` statement to check if `opensUsed` is less than `pairs`. This ensures you don't use more opening parentheses than allowed.
If this condition is `true`, push a new array to the `queue`: `[current + '(', opensUsed + 1, closesUsed]`. This represents the state after adding an opening parenthesis.
# --hints--
You should have an `else` clause for your `if` statement inside the `while` loop.
```js
assert.match(genParentheses.toString(), /if\s*\(\s*current\.length\s*===?\s*2\s*\*\s*pairs\s*\)\s*\{[\s\S]*?\}\s*else\s*\{/);
```
You should have an `if` statement inside the `else` block with the condition `opensUsed < pairs`.
```js
assert.match(genParentheses.toString(), /else\s*\{[\s\S]*?if\s*\(\s*opensUsed\s*<\s*pairs\s*\)/);
```
You should push `[current + '(', opensUsed + 1, closesUsed]` to the queue when the condition is `true`.
```js
assert.match(genParentheses.toString(), /queue\.push\s*\(\s*\[\s*current\s*\+\s*['"`]\(['"`]/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
console.log(queue);
let [current, opensUsed, closesUsed] = queue.shift();
if (current.length === 2 * pairs) {
result.push(current);
--fcc-editable-region--
}
--fcc-editable-region--
}
return result;
}
console.log(genParentheses(1));
```

View File

@@ -0,0 +1,65 @@
---
id: 6989f5bf7f1a1d0fc8ebd1e8
title: Step 11
challengeType: 1
dashedName: step-11
---
# --description--
Now you need to handle adding closing parentheses. The key rule is that you can only add a closing parenthesis if it maintains balance, meaning there must be more opening parentheses used than closing parentheses.
Still within the `else` block, add a second `if` statement to check if `closesUsed` is less than `opensUsed`.
If this condition is true, push another new array to the `queue`: `[current + ')', opensUsed, closesUsed + 1]`. This represents the state after adding a closing parenthesis.
# --hints--
You should have a second `if` statement with the condition `closesUsed < opensUsed` inside your `else` block.
```js
assert.match(genParentheses.toString(), /if\s*\(\s*closesUsed\s*<\s*opensUsed\s*\)/);
```
You should push `[current + ')', opensUsed, closesUsed + 1]` to the queue when the condition is true.
```js
assert.match(genParentheses.toString(), /queue\.push\s*\(\s*\[\s*current\s*\+\s*['"`]\)['"`]/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
console.log(queue);
let [current, opensUsed, closesUsed] = queue.shift();
if (current.length === 2 * pairs) {
result.push(current);
} else {
if (opensUsed < pairs) {
queue.push([current + '(', opensUsed + 1, closesUsed]);
}
--fcc-editable-region--
--fcc-editable-region--
}
}
return result;
}
console.log(genParentheses(1));
```

View File

@@ -0,0 +1,63 @@
---
id: 6989f711e57008f3a23482f2
title: Step 12
challengeType: 1
dashedName: step-12
---
# --description--
Your function is now complete. Test it again by logging `genParentheses(2)` instead of `genParentheses(1)`.
# --hints--
You should log `genParentheses(2)`.
```js
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*genParentheses\s*\(\s*2\s*\)\s*\)/);
```
You should not have `console.log(genParentheses(1))` in your code.
```js
assert.notMatch(__helpers.removeJSComments(code), /console\.log\s*\(\s*genParentheses\s*\(\s*1\s*\)\s*\)/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
console.log(queue);
let [current, opensUsed, closesUsed] = queue.shift();
if (current.length === 2 * pairs) {
result.push(current);
} else {
if (opensUsed < pairs) {
queue.push([current + '(', opensUsed + 1, closesUsed]);
}
if (closesUsed < opensUsed) {
queue.push([current + ')', opensUsed, closesUsed + 1]);
}
}
}
return result;
}
--fcc-editable-region--
console.log(genParentheses(1));
--fcc-editable-region--
```

View File

@@ -0,0 +1,58 @@
---
id: 6989f788362592a7238b1f94
title: Step 13
challengeType: 1
dashedName: step-13
---
# --description--
Now you don't need to log the queue anymore. So remove `console.log(queue)` from your `while` loop.
# --hints--
You should not have `console.log(queue)` in your `while` loop.
```js
assert.notMatch(__helpers.removeJSComments(code), /console\.log\s*\(\s*queue\s*\)/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
--fcc-editable-region--
console.log(queue);
--fcc-editable-region--
let [current, opensUsed, closesUsed] = queue.shift();
if (current.length === 2 * pairs) {
result.push(current);
} else {
if (opensUsed < pairs) {
queue.push([current + '(', opensUsed + 1, closesUsed]);
}
if (closesUsed < opensUsed) {
queue.push([current + ')', opensUsed, closesUsed + 1]);
}
}
}
return result;
}
console.log(genParentheses(2));
```

View File

@@ -0,0 +1,94 @@
---
id: 6989f7d2f0bac8cfe05a068d
title: Step 14
challengeType: 1
dashedName: step-14
---
# --description--
Finally, call `genParentheses` with `3` as its argument to generate all five valid combinations of three pairs of parentheses and log the result to the console.
With that, your breadth-first search workshop is complete.
# --hints--
You should log `genParentheses(3)`.
```js
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*genParentheses\s*\(\s*3\s*\)\s*\)/);
```
# --seed--
## --seed-contents--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
let [current, opensUsed, closesUsed] = queue.shift();
if (current.length === 2 * pairs) {
result.push(current);
} else {
if (opensUsed < pairs) {
queue.push([current + '(', opensUsed + 1, closesUsed]);
}
if (closesUsed < opensUsed) {
queue.push([current + ')', opensUsed, closesUsed + 1]);
}
}
}
return result;
}
console.log(genParentheses(2));
--fcc-editable-region--
--fcc-editable-region--
```
# --solutions--
```js
function genParentheses(pairs) {
if (!Number.isInteger(pairs)) {
return 'The number of pairs should be an integer';
}
if (pairs < 1) {
return 'The number of pairs should be at least 1';
}
let queue = [['', 0, 0]];
let result = [];
while (queue.length > 0) {
let [current, opensUsed, closesUsed] = queue.shift();
if (current.length === 2 * pairs) {
result.push(current);
} else {
if (opensUsed < pairs) {
queue.push([current + '(', opensUsed + 1, closesUsed]);
}
if (closesUsed < opensUsed) {
queue.push([current + ')', opensUsed, closesUsed + 1]);
}
}
}
return result;
}
console.log(genParentheses(2));
console.log(genParentheses(3));
```

View File

@@ -0,0 +1,25 @@
{
"isUpcomingChange": true,
"dashedName": "workshop-breadth-first-search-js",
"helpCategory": "JavaScript",
"blockLayout": "challenge-grid",
"challengeOrder": [
{ "id": "6989eb65324d97775c4965fe", "title": "Step 1" },
{ "id": "6989ef885db3a3488a13b426", "title": "Step 2" },
{ "id": "6989f10a72ad424d4ad6d514", "title": "Step 3" },
{ "id": "6989f1ba358acba651d5ee6b", "title": "Step 4" },
{ "id": "6989f207306332c06b0add39", "title": "Step 5" },
{ "id": "6989f2e35e6941e13e445156", "title": "Step 6" },
{ "id": "6989f3632f8b8bdf168aa321", "title": "Step 7" },
{ "id": "6989f46b6e9366b0e6ad32fe", "title": "Step 8" },
{ "id": "6989f48c3f0292c84f8d8938", "title": "Step 9" },
{ "id": "6989f555728e63ad8b349d84", "title": "Step 10" },
{ "id": "6989f5bf7f1a1d0fc8ebd1e8", "title": "Step 11" },
{ "id": "6989f711e57008f3a23482f2", "title": "Step 12" },
{ "id": "6989f788362592a7238b1f94", "title": "Step 13" },
{ "id": "6989f7d2f0bac8cfe05a068d", "title": "Step 14" }
],
"blockLabel": "workshop",
"usesMultifileEditor": true,
"hasEditableBoundaries": true
}

View File

@@ -330,6 +330,7 @@
"blocks": [
"lecture-understanding-graphs-and-trees-js",
"lab-adjacency-list-to-matrix-converter-js",
"workshop-breadth-first-search-js",
"lab-depth-first-search-js",
"lab-n-queens-problem-js",
"review-graphs-and-trees-js",