feat(curriculum): add shortest path algo to js v9 cert (#66681)

Co-authored-by: jdwilkin4 <jwilkin4@hotmail.com>
This commit is contained in:
Kolade Chris
2026-04-01 07:24:12 +01:00
committed by GitHub
parent 35fccf2415
commit 9721f9c59f
29 changed files with 1971 additions and 0 deletions
+7
View File
@@ -5701,6 +5701,13 @@
"In this lesson, you will learn about fundamental data structures like graphs, trees, and their practical applications in computer science."
]
},
"workshop-shortest-path-algorithm-js": {
"title": "Implement the Shortest Path Algorithm",
"intro": [
"The shortest path algorithm finds the minimum distance between nodes in a weighted graph.",
"In this workshop, you'll implement the shortest path algorithm in JavaScript and return both the shortest distances and the paths taken."
]
},
"lab-adjacency-list-to-matrix-converter-js": {
"title": "Build an Adjacency List to Matrix Converter",
"intro": [
@@ -0,0 +1,38 @@
---
id: 0095d0567b6817114989a1dd
title: Step 1
challengeType: 1
dashedName: step-1
---
# --description--
In this workshop, you will implement the shortest path algorithm in JavaScript. You will write a function that computes the shortest path between nodes in a weighted graph and returns both the distances and the paths taken.
For example, given a graph where cities are connected by roads with different distances, the algorithm will find the shortest route from one city to another. If you want to travel from City A to City D, the algorithm might find that going A ⇨ B ⇨ C ⇨ D (total: 15km) is shorter than going directly A ⇨ D (20km).
To get started, define a variable named `INF` and assign it the value `Infinity`. Later, you'll use it to indicate that there is no direct connection between two nodes.
# --hints--
You should define a variable named `INF`.
```js
assert.isDefined(INF);
```
Your `INF` variable should have the value `Infinity`.
```js
assert.strictEqual(INF, Infinity);
```
# --seed--
## --seed-contents--
```js
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,59 @@
---
id: 69c918841987bf16c1d1baba
title: Step 2
challengeType: 1
dashedName: step-2
---
# --description--
You will need a 2D array to represent the adjacency matrix of the graph. Each value represents the weight (distance) of the edge between two nodes. A value of `INF` means there is no direct edge.
Create another variable named `adjMatrix` and assign it a 2D array with the following values:
```js
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0]
```
# --hints--
You should create a variable named `adjMatrix`.
```js
assert.isDefined(adjMatrix);
```
`adjMatrix` should be a 2D array with 6 rows and 6 columns.
```js
assert.isArray(adjMatrix);
assert.lengthOf(adjMatrix, 6);
adjMatrix.forEach(row => assert.lengthOf(row, 6));
```
`adjMatrix` should contain the correct values.
```js
assert.deepEqual(adjMatrix[0], [0, 5, 3, INF, 11, INF]);
assert.deepEqual(adjMatrix[1], [5, 0, 1, INF, INF, 2]);
assert.deepEqual(adjMatrix[2], [3, 1, 0, 1, 5, INF]);
assert.deepEqual(adjMatrix[3], [INF, INF, 1, 0, 9, 3]);
assert.deepEqual(adjMatrix[4], [11, INF, 5, 9, 0, INF]);
assert.deepEqual(adjMatrix[5], [INF, 2, INF, 3, INF, 0]);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,52 @@
---
id: 69c918841987bf16c1d1babb
title: Step 3
challengeType: 1
dashedName: step-3
---
# --description--
Now you will create the main function that does all the work.
Create a function named `shortestPath` that takes three parameters: `matrix`, `startNode`, and `targetNode`.
Over the next few steps, you will build out the logic for this function.
# --hints--
You should create a function named `shortestPath`.
```js
assert.isFunction(shortestPath);
```
Your `shortestPath` function should have the parameters `matrix`, `startNode`, and `targetNode`.
```js
const params = __helpers.getFunctionParams(shortestPath.toString());
const names = params.map(p => p.name);
assert.include(names, 'matrix');
assert.include(names, 'startNode');
assert.include(names, 'targetNode');
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,54 @@
---
id: 69c918841987bf16c1d1babc
title: Step 5
challengeType: 1
dashedName: step-5
---
# --description--
You need to store the number of nodes in the graph.
Inside the `shortestPath` function, create a variable named `n` and assign it `matrix.length`.
# --hints--
You should create a variable `n` inside the `shortestPath` function.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(const|let|var)\s+n\s*=/
);
```
Your `n` variable should have the value `matrix.length`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(const|let|var)\s+n\s*=\s*matrix\.length/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
--fcc-editable-region--
function shortestPath(matrix, startNode, targetNode = null) {
}
--fcc-editable-region--
```
@@ -0,0 +1,57 @@
---
id: 69c918841987bf16c1d1babd
title: Step 6
challengeType: 1
dashedName: step-6
---
# --description--
You need to keep track of the shortest known distance from the start node to every other node. To begin, you'll assume every node is infinitely far away.
In JavaScript, you can create an array pre-filled with a value using `new Array(n).fill(value)`. For example, `new Array(3).fill(0)` creates `[0, 0, 0]`.
Inside the `shortestPath` function, create a variable named `distances` and assign it `new Array(n).fill(INF)`.
# --hints--
You should create a variable named `distances` inside `shortestPath`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(const|let|var)\s+distances\s*=/
);
```
Your `distances` variable should be set to `new Array(n).fill(INF)`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/new\s+Array\s*\(\s*n\s*\)\s*\.fill\s*\(\s*INF\s*\)/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
--fcc-editable-region--
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
}
--fcc-editable-region--
```
@@ -0,0 +1,47 @@
---
id: 69c918841987bf16c1d1babe
title: Step 7
challengeType: 1
dashedName: step-7
---
# --description--
The distance from the starting node to itself is always `0`. You have to update the `distances` array to reflect this.
After the `distances` array, set `distances[startNode]` to `0`.
# --hints--
You should set `distances[startNode]` to `0`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/distances\s*\[\s*startNode\s*\]\s*=\s*0/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
--fcc-editable-region--
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
}
--fcc-editable-region--
```
@@ -0,0 +1,83 @@
---
id: 69c918841987bf16c1d1babf
title: Step 8
challengeType: 1
dashedName: step-8
---
# --description--
You also need to track the actual path taken to reach each node. You will store a list of paths where each entry is an array of node indices representing the route taken.
Initially, each node's path will just contain itself. You can create this structure using `Array.from()`:
```js
Array.from({ length: n }, (_, i) => [i]);
```
This creates an array of `n` elements, where the element at index `i` is `[i]`.
Still within the `shortestPath` function, create a variable named `paths` and assign it `Array.from({ length: n }, (_, i) => [i])`.
# --hints--
You should create a variable `paths` inside the `shortestPath` function.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(const|let|var)\s+paths\s*=/
);
```
Your `paths` variable should be initialized using `Array.from`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/paths\s*=\s*Array\.from/
);
```
Your `paths` variable should use `{ length: n }` as the first argument to `Array.from`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/Array\.from\s*\(\s*\{\s*length\s*:\s*n\s*\}/
);
```
The second argument to `Array.from` should be a callback `(_, i) => [i]`, where `_` is a placeholder for the unused element and `i` is the index. It should return `[i]`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/Array\.from\s*\([\s\S]*?,\s*function\s*\(\s*\w+\s*,\s*(\w+)\s*\)\s*\{[\s\S]*?return\s*\[\s*\1\s*\]|Array\.from\s*\([\s\S]*?,\s*\(\s*\w+\s*,\s*\w+\s*\)\s*=>\s*\[\s*\w+\s*\]/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
--fcc-editable-region--
--fcc-editable-region--
}
```
@@ -0,0 +1,58 @@
---
id: 69c918841987bf16c1d1bac0
title: Step 9
challengeType: 1
dashedName: step-9
---
# --description--
As the algorithm runs, you need to track which nodes have already been processed so you don't revisit them.
Create a variable named `visited` and assign it `new Array(n).fill(false)`.
# --hints--
You should create a variable named `visited`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(const|let|var)\s+visited\s*=/
);
```
Your `visited` variable should be set to `new Array(n).fill(false)`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/new\s+Array\s*\(\s*n\s*\)\s*\.fill\s*\(\s*false\s*\)/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
--fcc-editable-region--
--fcc-editable-region--
}
```
@@ -0,0 +1,70 @@
---
id: 69c918841987bf16c1d1bac1
title: Step 10
challengeType: 1
dashedName: step-10
---
# --description--
Now you'll add the main loop that drives the algorithm. It runs once for each node in the graph, selecting the closest unvisited node each time.
Inside the `shortestPath` function, add a `for` loop that runs `n` times using `i` as the loop variable.
At the start of the loop body, using `let`, declare two variables `minDistance` and `current` set to `INF`, and `-1` respectively. These will track the closest unvisited node found in each iteration.
# --hints--
You should have a `for` loop inside `shortestPath` that runs `n` times. Don't forget to set `i` with `let` inside it.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/for\s*\(\s*(let|var)\s+i\s*=\s*0\s*;\s*i\s*<\s*n\s*;\s*i\+\+\s*\)/
);
```
You should have a `minDistance` variable set to `INF` inside the loop.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(let|var)\s+minDistance\s*=\s*INF/
);
```
You should have a `current` variable set to `-1` inside the loop.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(let|var)\s+current\s*=\s*-1/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
--fcc-editable-region--
--fcc-editable-region--
}
```
@@ -0,0 +1,57 @@
---
id: 69c918841987bf16c1d1bac2
title: Step 11
challengeType: 1
dashedName: step-11
---
# --description--
Inside the loop, you need to scan every node to find the one with the smallest known distance that hasn't been visited yet.
Add a `for` loop inside the main loop that iterates through each node. Use `nodeNo` as the loop variable going from `0` to `n`.
Leave the body empty for now.
# --hints--
You should have a second (inner) `for` loop inside the main loop with `nodeNo` as its loop variable.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/for\s*\(\s*(let|var)\s+nodeNo\s*=\s*0\s*;\s*nodeNo\s*<\s*n\s*;\s*nodeNo\+\+\s*\)/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
--fcc-editable-region--
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
}
--fcc-editable-region--
}
```
@@ -0,0 +1,59 @@
---
id: 69c918841987bf16c1d1bac3
title: Step 12
challengeType: 1
dashedName: step-12
---
# --description--
Inside the inner `for` loop, you need to check whether the current node is unvisited and closer than the best you've found so far.
Add an `if` statement that checks if `nodeNo` has not been visited and `distances[nodeNo]` is less than `minDistance`.
Leave the body empty for now.
# --hints--
You should have an `if` statement with the condition `!visited[nodeNo] && distances[nodeNo] < minDistance` inside the inner `for` loop.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/if\s*\(\s*!visited\s*\[\s*nodeNo\s*\]\s*&&\s*distances\s*\[\s*nodeNo\s*\]\s*<\s*minDistance\s*\)/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
--fcc-editable-region--
--fcc-editable-region--
}
}
}
```
@@ -0,0 +1,68 @@
---
id: 69c918841987bf16c1d1bac4
title: Step 13
challengeType: 1
dashedName: step-13
---
# --description--
If the condition is true, the current node is the best unvisited candidate found so far. You need to update your tracking variables to reflect that.
Inside the `if` statement, update `minDistance` to `distances[nodeNo]` and `current` to `nodeNo`.
# --hints--
You should set `minDistance` to `distances[nodeNo]` inside the `if` block.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/minDistance\s*=\s*distances\s*\[\s*nodeNo\s*\]/
);
```
You should set `current` to `nodeNo` inside the `if` block.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/current\s*=\s*nodeNo/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
--fcc-editable-region--
--fcc-editable-region--
}
}
}
}
```
@@ -0,0 +1,70 @@
---
id: 69c918841987bf16c1d1bac5
title: Step 14
challengeType: 1
dashedName: step-14
---
# --description--
After scanning all nodes to find the closest unvisited one, you need to handle the case where no valid node is found. This happens when all remaining nodes are unreachable.
After the inner `for` loop, but still inside the outer loop, add an `if` statement that checks if `current` is strictly equal to `-1`. If that is true, use `break` inside the `if` block to stop the outer loop early.
# --hints--
You should add an `if` statement that checks if `current === -1`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/if\s*\(\s*current\s*===\s*-1\s*\)/
);
```
You should use `break` inside the `if` block.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/if\s*\(\s*current\s*===\s*-1\s*\)[\s\S]*?break/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
--fcc-editable-region--
--fcc-editable-region--
}
}
```
@@ -0,0 +1,65 @@
---
id: 69c918841987bf16c1d1bac6
title: Step 15
challengeType: 1
dashedName: step-15
---
# --description--
Once you've confirmed a valid node was found, mark it as visited so the algorithm won't process it again.
After the `if` statement with the condition `current === -1`, set `visited[current]` to `true`.
# --hints--
You should set `visited[current]` to `true`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/visited\s*\[\s*current\s*\]\s*=\s*true/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
--fcc-editable-region--
--fcc-editable-region--
}
}
```
@@ -0,0 +1,85 @@
---
id: 69c918841987bf16c1d1bac7
title: Step 16
challengeType: 1
dashedName: step-16
---
# --description--
Now that the current node is marked as visited, you need to look at all its neighbors to see if you can reach them more efficiently through the current node.
After `visited[current] = true`, add a neighbor `for` loop that iterates through each `nodeNo` from `0` to `n`. Note that this is another inner loop on the same level as the first.
Inside the loop, declare a variable named `distance` and assign it `matrix[current][nodeNo]`. This gives you the edge weight between the current node and its neighbor.
# --hints--
You should add a neighbor `for` loop after marking the current node as visited.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/visited\s*\[\s*current\s*\]\s*=\s*true[\s\S]*?for\s*\(/
);
```
The neighbor `for` loop should iterate from `0` to `n`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/visited\s*\[\s*current\s*\]\s*=\s*true[\s\S]*?for\s*\(\s*(var|let)\s+\w+\s*=\s*0\s*;\s*\w+\s*<\s*n\s*;\s*\w+\+\+\s*\)/
);
```
You should have a `distance` variable assigned to `matrix[current][nodeNo]` inside the neighbor loop.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(const|let|var)\s+distance\s*=\s*matrix\s*\[\s*current\s*\]\s*\[\s*\w+\s*\]/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
--fcc-editable-region--
--fcc-editable-region--
}
}
```
@@ -0,0 +1,80 @@
---
id: 69c918841987bf16c1d1bac8
title: Step 17
challengeType: 1
dashedName: step-17
---
# --description--
Before updating distances, you need to verify the neighbor is worth considering. There must be an actual edge to it (`distance !== INF`) and it must not have been visited yet.
Inside the neighbor `for` loop, add an `if` statement that checks if `distance` is not strictly equal to `INF` **and** if `nodeNo` is not visited.
Inside the `if` block, declare a variable `newDistance` assigned to `distances[current] + distance`. This is the total cost of reaching the neighbor through the current node.
# --hints--
You should add an `if` statement with the condition `distance !== INF && !visited[nodeNo]`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/if\s*\(\s*distance\s*!==\s*INF\s*&&\s*!\s*visited\s*\[\s*\w+\s*\]\s*\)/
);
```
You should declare a `newDistance` with the value `distances[current] + distance` inside the `if` block.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(const|let|var)\s+newDistance\s*=\s*distances\s*\[\s*current\s*\]\s*\+\s*distance/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
--fcc-editable-region--
--fcc-editable-region--
}
}
}
```
@@ -0,0 +1,81 @@
---
id: 69c918841987bf16c1d1bac9
title: Step 18
challengeType: 1
dashedName: step-18
---
# --description--
Now you should check whether going through the current node gives a shorter route to the neighbor. If `newDistance` is better than what's already stored, then you should update it.
Inside the existing `if` block, add a nested `if` statement that checks if `newDistance` is less than `distances[nodeNo]`. Inside that nested `if` block, update `distances[nodeNo]` to `newDistance`.
# --hints--
You should add a nested `if` statement with the condition `newDistance < distances[nodeNo]`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/if\s*\(\s*newDistance\s*<\s*distances\s*\[\s*\w+\s*\]\s*\)/
);
```
You should update `distances[nodeNo]` to `newDistance` inside the nested `if` block.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/distances\s*\[\s*\w+\s*\]\s*=\s*newDistance/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
--fcc-editable-region--
--fcc-editable-region--
}
}
}
}
```
@@ -0,0 +1,90 @@
---
id: 69c918841987bf16c1d1baca
title: Step 19
challengeType: 1
dashedName: step-19
---
# --description--
When you find a shorter path to a neighbor, you also need to update the recorded path to reach it.
In JavaScript, you can create a new array combining an existing array and a new element using the spread operator:
```js
const newPath = [...existingArray, newElement];
```
Inside the nested `if` block, update `paths[nodeNo]` to be the path to the current node with `nodeNo` appended at the end using spread syntax like so: `[...paths[current], nodeNo]`.
# --hints--
You should update `paths[nodeNo]` inside the nested `if` block.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/paths\s*\[\s*\w+\s*\]\s*=/
);
```
`paths[nodeNo]` should be set to `[...paths[current], nodeNo]`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/paths\s*\[\s*\w+\s*\]\s*=\s*\[\s*\.\.\.\s*paths\s*\[\s*current\s*\]\s*,\s*\w+\s*\]/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
--fcc-editable-region--
--fcc-editable-region--
}
}
}
}
}
```
@@ -0,0 +1,98 @@
---
id: 69c918841987bf16c1d1bacb
title: Step 20
challengeType: 1
dashedName: step-20
---
# --description--
Once the main loop finishes, you need to decide which node(s) to display results for.
If a specific `targetNode` was provided, only show results for that node. Otherwise, show results for all nodes.
In JavaScript, you can get all indices of an array as an iterable using `[...Array(n).keys()]`.
After the outer `for` loop, create a variable named `targets`. Using a ternary expression, if `targetNode` is strictly not `null`, assign `[targetNode]`, otherwise assign `[...Array(n).keys()]`.
# --hints--
You should create a variable `targets` after the main loop.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/(const|let|var)\s+targets\s*=/
);
```
Your `targets` variable should use a ternary checking `targetNode !== null`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/targets\s*=\s*targetNode\s*!==\s*null\s*\?/
);
```
When a target is provided, `targets` should be `[targetNode]`, otherwise it should be `[...Array(n).keys()]`.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/\?\s*\[\s*targetNode\s*\]\s*:\s*\[\s*\.\.\.\s*Array\s*\(\s*n\s*\)\s*\.keys\s*\(\s*\)\s*\]/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
paths[nodeNo] = [...paths[current], nodeNo];
}
}
}
}
--fcc-editable-region--
--fcc-editable-region--
}
```
@@ -0,0 +1,77 @@
---
id: 69c918841987bf16c1d1bacc
title: Step 21
challengeType: 1
dashedName: step-21
---
# --description--
Now loop through the target nodes to display results for each one.
Add a `for...of` loop that iterates over `targets` using `nodeNo` as the loop variable. Leave the body empty for now.
# --hints--
You should create a `for...of` loop with the variable `nodeNo` iterating over `targets`.
```js
assert.match(
__helpers.removeJSComments(code),
/for\s*\(\s*(const|let)\s+nodeNo\s+of\s+targets\s*\)/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
paths[nodeNo] = [...paths[current], nodeNo];
}
}
}
}
const targets = targetNode !== null ? [targetNode] : [...Array(n).keys()];
--fcc-editable-region--
--fcc-editable-region--
}
```
@@ -0,0 +1,89 @@
---
id: 69c918841987bf16c1d1bacd
title: Step 22
challengeType: 1
dashedName: step-22
---
# --description--
You only want to display results for nodes that are reachable and different from the start node.
Inside the `for...of` loop, add an `if` statement that checks if `nodeNo` is strictly equal to `startNode` **or** `distances[nodeNo]` is strictly equal to `INF`. If either is true, use `continue` inside the `if` block to skip to the next iteration.
# --hints--
You should have an `if` statement with the condition `nodeNo === startNode || distances[nodeNo] === INF` inside your `for...of` loop.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/if\s*\(\s*\w+\s*===\s*startNode\s*\|\|\s*distances\s*\[\s*\w+\s*\]\s*===\s*INF\s*\)/
);
```
You should use `continue` in the `if` block.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/if\s*\(\s*\w+\s*===\s*startNode\s*\|\|\s*distances\s*\[\s*\w+\s*\]\s*===\s*INF\s*\)[\s\S]*?continue/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
paths[nodeNo] = [...paths[current], nodeNo];
}
}
}
}
const targets = targetNode !== null ? [targetNode] : [...Array(n).keys()];
--fcc-editable-region--
for (const nodeNo of targets) {
}
--fcc-editable-region--
}
```
@@ -0,0 +1,94 @@
---
id: 69c918841987bf16c1d1bace
title: Step 23
challengeType: 1
dashedName: step-23
---
# --description--
Now that you know the node is reachable and not the start, format its path into a readable string.
The `paths[nodeNo]` array holds the sequence of node indices visited to reach `nodeNo`. You can turn that into a human-readable string like `0 -> 2 -> 3` using the `.join()` method.
Inside the `for...of` loop, after the `if` statement with `continue`, declare a variable named `path` and assign it the value `paths[nodeNo].join(' -> ')`.
# --hints--
You should have a variable named `path` inside the `for...of` loop.
```js
assert.match(
__helpers.removeJSComments(code),
/for\s*\(\s*(const|let)\s+nodeNo\s+of\s+targets\s*\)[\s\S]*?(const|let)\s+path\s*=/
);
```
Your `path` variable should have the value `paths[nodeNo].join(' -> ')`.
```js
assert.match(
__helpers.removeJSComments(code),
/path\s*=\s*paths\s*\[\s*\w+\s*\]\s*\.\s*join\s*\(\s*['"]\s*->\s*['"]\s*\)/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
paths[nodeNo] = [...paths[current], nodeNo];
}
}
}
}
const targets = targetNode !== null ? [targetNode] : [...Array(n).keys()];
for (const nodeNo of targets) {
if (nodeNo === startNode || distances[nodeNo] === INF) {
continue;
}
--fcc-editable-region--
--fcc-editable-region--
}
}
```
@@ -0,0 +1,113 @@
---
id: 69c918841987bf16c1d1bacf
title: Step 24
challengeType: 1
dashedName: step-24
---
# --description--
Now, you should print the result for each reachable node.
After the `path` variable, add a `console.log()` call with a template literal that outputs:
```md
\n{startNode}-{nodeNo} distance: {distances[nodeNo]}\nPath: {path}
```
For example, if `startNode` is `0` and `nodeNo` is `3`, the output should look like this:
```md
0-3 distance: 4
Path: 0 -> 2 -> 3
```
# --hints--
You should have a `console.log()` inside the `for...of` loop.
```js
assert.match(
__helpers.removeJSComments(code),
/for\s*\(\s*(const|let)\s+nodeNo\s+of\s+targets\s*\)[\s\S]*?console\s*\.\s*log\s*\(/
);
```
The `console.log()` should use a template literal containing `${startNode}-${nodeNo} distance: ${distances[nodeNo]}`.
```js
assert.match(
__helpers.removeJSComments(code),
/console\s*\.\s*log\s*\(\s*`[\s\S]*?\$\{\s*startNode\s*\}[\s\S]*?\$\{\s*nodeNo\s*\}[\s\S]*?\$\{\s*distances\s*\[\s*nodeNo\s*\]\s*\}[\s\S]*?`\s*\)/
);
```
The template literal should also include `${path}`.
```js
assert.match(
__helpers.removeJSComments(code),
/console\s*\.\s*log\s*\(\s*`[\s\S]*?\$\{\s*path\s*\}[\s\S]*?`\s*\)/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
paths[nodeNo] = [...paths[current], nodeNo];
}
}
}
}
const targets = targetNode !== null ? [targetNode] : [...Array(n).keys()];
for (const nodeNo of targets) {
if (nodeNo === startNode || distances[nodeNo] === INF) {
continue;
}
const path = paths[nodeNo].join(' -> ');
--fcc-editable-region--
--fcc-editable-region--
}
}
```
@@ -0,0 +1,86 @@
---
id: 69c918841987bf16c1d1bad0
title: Step 25
challengeType: 1
dashedName: step-25
---
# --description--
The function should return the computed data so callers can use it programmatically.
After the `for...of` loop, add a `return` statement that returns an array containing `distances` and `paths`, in that order.
# --hints--
You should return `[distances, paths]` after the `for...of` loop.
```js
assert.match(
__helpers.removeJSComments(shortestPath.toString()),
/return\s*\[\s*distances\,\s*paths\]/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
paths[nodeNo] = [...paths[current], nodeNo];
}
}
}
}
const targets = targetNode !== null ? [targetNode] : [...Array(n).keys()];
for (const nodeNo of targets) {
if (nodeNo === startNode || distances[nodeNo] === INF) {
continue;
}
const path = paths[nodeNo].join(' -> ');
console.log(`\n${startNode}-${nodeNo} distance: ${distances[nodeNo]}\nPath: ${path}`);
}
--fcc-editable-region--
--fcc-editable-region--
}
```
@@ -0,0 +1,151 @@
---
id: 69c918841987bf16c1d1bad1
title: Step 26
challengeType: 1
dashedName: step-26
---
# --description--
The function is complete. Now call it to see the results.
At the bottom of the file (outside the function), call `shortestPath` with `adjMatrix` as the matrix, `0` as the start node, and `5` as the target node.
This will print the shortest path from node `0` to node `5` in the adjacency matrix.
With that, your shortest path algorithm workshop is complete!
# --hints--
You should call the `shortestPath` function with `adjMatrix, 0, 5`.
```js
assert.match(
__helpers.removeJSComments(code),
/shortestPath\s*\(\s*adjMatrix\s*,\s*0\s*,\s*5\s*\)/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
paths[nodeNo] = [...paths[current], nodeNo];
}
}
}
}
const targets = targetNode !== null ? [targetNode] : [...Array(n).keys()];
for (const nodeNo of targets) {
if (nodeNo === startNode || distances[nodeNo] === INF) {
continue;
}
const path = paths[nodeNo].join(' -> ');
console.log(`\n${startNode}-${nodeNo} distance: ${distances[nodeNo]}\nPath: ${path}`);
}
return [distances, paths];
}
--fcc-editable-region--
--fcc-editable-region--
```
# --solutions--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
function shortestPath(matrix, startNode, targetNode = null) {
const n = matrix.length;
const distances = new Array(n).fill(INF);
distances[startNode] = 0;
const paths = Array.from({ length: n }, (_, i) => [i]);
const visited = new Array(n).fill(false);
for (let i = 0; i < n; i++) {
let minDistance = INF;
let current = -1;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
if (!visited[nodeNo] && distances[nodeNo] < minDistance) {
minDistance = distances[nodeNo];
current = nodeNo;
}
}
if (current === -1) {
break;
}
visited[current] = true;
for (let nodeNo = 0; nodeNo < n; nodeNo++) {
const distance = matrix[current][nodeNo];
if (distance !== INF && !visited[nodeNo]) {
const newDistance = distances[current] + distance;
if (newDistance < distances[nodeNo]) {
distances[nodeNo] = newDistance;
paths[nodeNo] = [...paths[current], nodeNo];
}
}
}
}
const targets = targetNode !== null ? [targetNode] : [...Array(n).keys()];
for (const nodeNo of targets) {
if (nodeNo === startNode || distances[nodeNo] === INF) {
continue;
}
const path = paths[nodeNo].join(' -> ');
console.log(`\n${startNode}-${nodeNo} distance: ${distances[nodeNo]}\nPath: ${path}`);
}
return [distances, paths];
}
shortestPath(adjMatrix, 0, 5);
```
@@ -0,0 +1,45 @@
---
id: 69c937c73f23b1140bcd297a
title: Step 4
challengeType: 1
dashedName: step-4
---
# --description--
The `targetNode` parameter is optional. When not provided, the function will compute shortest paths from `startNode` to all other nodes in the graph.
Give the `targetNode` parameter a default value of `null`.
# --hints--
The `targetNode` parameter of your `shortestPath` function should default to `null`.
```js
assert.match(
__helpers.removeJSComments(code),
/targetNode\s*=\s*null/
);
```
# --seed--
## --seed-contents--
```js
const INF = Infinity;
const adjMatrix = [
[0, 5, 3, INF, 11, INF],
[5, 0, 1, INF, INF, 2],
[3, 1, 0, 1, 5, INF],
[INF, INF, 1, 0, 9, 3],
[11, INF, 5, 9, 0, INF],
[INF, 2, INF, 3, INF, 0],
];
--fcc-editable-region--
function shortestPath(matrix, startNode, targetNode) {
}
--fcc-editable-region--
```
@@ -0,0 +1,37 @@
{
"isUpcomingChange": true,
"dashedName": "workshop-shortest-path-algorithm-js",
"helpCategory": "JavaScript",
"blockLayout": "challenge-grid",
"blockLabel": "workshop",
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"challengeOrder": [
{ "id": "0095d0567b6817114989a1dd", "title": "Step 1" },
{ "id": "69c918841987bf16c1d1baba", "title": "Step 2" },
{ "id": "69c918841987bf16c1d1babb", "title": "Step 3" },
{ "id": "69c937c73f23b1140bcd297a", "title": "Step 4" },
{ "id": "69c918841987bf16c1d1babc", "title": "Step 5" },
{ "id": "69c918841987bf16c1d1babd", "title": "Step 6" },
{ "id": "69c918841987bf16c1d1babe", "title": "Step 7" },
{ "id": "69c918841987bf16c1d1babf", "title": "Step 8" },
{ "id": "69c918841987bf16c1d1bac0", "title": "Step 9" },
{ "id": "69c918841987bf16c1d1bac1", "title": "Step 10" },
{ "id": "69c918841987bf16c1d1bac2", "title": "Step 11" },
{ "id": "69c918841987bf16c1d1bac3", "title": "Step 12" },
{ "id": "69c918841987bf16c1d1bac4", "title": "Step 13" },
{ "id": "69c918841987bf16c1d1bac5", "title": "Step 14" },
{ "id": "69c918841987bf16c1d1bac6", "title": "Step 15" },
{ "id": "69c918841987bf16c1d1bac7", "title": "Step 16" },
{ "id": "69c918841987bf16c1d1bac8", "title": "Step 17" },
{ "id": "69c918841987bf16c1d1bac9", "title": "Step 18" },
{ "id": "69c918841987bf16c1d1baca", "title": "Step 19" },
{ "id": "69c918841987bf16c1d1bacb", "title": "Step 20" },
{ "id": "69c918841987bf16c1d1bacc", "title": "Step 21" },
{ "id": "69c918841987bf16c1d1bacd", "title": "Step 22" },
{ "id": "69c918841987bf16c1d1bace", "title": "Step 23" },
{ "id": "69c918841987bf16c1d1bacf", "title": "Step 24" },
{ "id": "69c918841987bf16c1d1bad0", "title": "Step 25" },
{ "id": "69c918841987bf16c1d1bad1", "title": "Step 26" }
]
}
@@ -329,6 +329,7 @@
"comingSoon": true,
"blocks": [
"lecture-understanding-graphs-and-trees-js",
"workshop-shortest-path-algorithm-js",
"lab-adjacency-list-to-matrix-converter-js",
"workshop-breadth-first-search-js",
"lab-depth-first-search-js",