From bd06212420e9b2d5a6021b63f8aca84b079e60fb Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 12 Jan 2026 11:36:47 +0100 Subject: [PATCH 1/3] Setup Problem --- Problems/Shortes-Path-Dijkstra/README.md | 16 ++ .../shortes-path-dijkstra.test.ts | 249 ++++++++++++++++++ Problems/Shortes-Path-Dijkstra/solver.ts | 14 + 3 files changed, 279 insertions(+) create mode 100644 Problems/Shortes-Path-Dijkstra/README.md create mode 100644 Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts create mode 100644 Problems/Shortes-Path-Dijkstra/solver.ts diff --git a/Problems/Shortes-Path-Dijkstra/README.md b/Problems/Shortes-Path-Dijkstra/README.md new file mode 100644 index 0000000..f0858cd --- /dev/null +++ b/Problems/Shortes-Path-Dijkstra/README.md @@ -0,0 +1,16 @@ +# Shortes Path Dijkstra-Algorithmus + +This problem is about finding the shortest path between given destinations and the connections between them, where each connection has a specific length. + +## Documentation + +### Solution Idea + +I implemented the Dijkstra-Algorithmus. +You can see [here](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) how this algorithm works. + +--- + +### [Implementation](./solver.ts) + +--- diff --git a/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts b/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts new file mode 100644 index 0000000..19c7010 --- /dev/null +++ b/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts @@ -0,0 +1,249 @@ +import { describe, expect, it } from "vitest"; +import findShortesPaths from "./solver"; + +describe("Returns the shortes path from 'A' to all other Destinations", () => { + const stations1 = ["A", "B", "C", "D", "E", "F", "G"]; + const streets1 = [ + { + A: "A", + B: "B", + length: 1, + }, + { + A: "A", + B: "C", + length: 4, + }, + { + A: "A", + B: "G", + length: 3, + }, + { + A: "B", + B: "C", + length: 2, + }, + { + A: "C", + B: "D", + length: 7, + }, + { + A: "D", + B: "E", + length: 3, + }, + { + A: "F", + B: "G", + length: 2, + }, + { + A: "G", + B: "C", + length: 2, + }, + ]; + + it("should return all paths for 7 destinations", () => { + expect(findShortesPaths("A", stations1, streets1)).toEqual(true); + }); + + const stations2 = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]; + const streets2 = [ + { + A: "A", + B: "B", + length: 5, + }, + { + A: "A", + B: "C", + length: 2, + }, + { + A: "A", + B: "D", + length: 6, + }, + { + A: "B", + B: "C", + length: 9, + }, + { + A: "B", + B: "D", + length: 8, + }, + { + A: "C", + B: "F", + length: 20, + }, + { + A: "C", + B: "D", + length: 15, + }, + { + A: "D", + B: "F", + length: 13, + }, + { + A: "D", + B: "G", + length: 2, + }, + { + A: "E", + B: "F", + length: 6, + }, + { + A: "E", + B: "H", + length: 9, + }, + { + A: "E", + B: "I", + length: 7, + }, + { + A: "F", + B: "H", + length: 8, + }, + { + A: "G", + B: "H", + length: 8, + }, + { + A: "G", + B: "J", + length: 6, + }, + { + A: "I", + B: "J", + length: 6, + }, + { + A: "H", + B: "J", + length: 10, + }, + ]; + + it("should return all paths for 10 destinations", () => { + expect(findShortesPaths("A", stations2, streets2)).toEqual(true); + }); + + const stations3 = ["A", "B", "C", "D", "E", "F", "G", "H", "Z"]; + const streets3 = [ + { A: "A", B: "B", length: 2 }, + { A: "A", B: "C", length: 2 }, + { A: "A", B: "D", length: 3 }, + + { A: "B", B: "C", length: 1 }, + { A: "B", B: "F", length: 2 }, + + { A: "C", B: "G", length: 8 }, + { A: "C", B: "E", length: 9 }, + + { A: "D", B: "E", length: 1 }, + { A: "D", B: "H", length: 2 }, + + { A: "E", B: "G", length: 5 }, + { A: "E", B: "H", length: 2 }, + + { A: "F", B: "G", length: 2 }, + { A: "F", B: "Z", length: 4 }, + + { A: "G", B: "Z", length: 1 }, + + { A: "H", B: "Z", length: 2 }, + ]; + + it("should return all paths for 9 destinations", () => { + expect(findShortesPaths("A", stations3, streets3)).toEqual(true); + }); + + const stations4 = [ + "A", + "AK1", + "Lu", + "Ma", + "Vie", + "We", + "AK2", + "Hd", + "Sch", + "Sp", + "Nb", + "SIL", + "Br", + "Ka", + "Wo", + "Kan", + "LD", + "Neu", + "Ger", + ]; + const streets4 = [ + { A: "A", B: "AK1", length: 12 }, + { A: "AK1", B: "Lu", length: 10 }, + { A: "Lu", B: "Ma", length: 3 }, + { A: "Ma", B: "Vie", length: 9 }, + { A: "Vie", B: "We", length: 8 }, + + { A: "Ma", B: "AK2", length: 16 }, + { A: "AK2", B: "Hd", length: 9 }, + { A: "Hd", B: "We", length: 19 }, + + { A: "AK2", B: "Sch", length: 8 }, + { A: "Sch", B: "Hd", length: 8 }, + + { A: "AK1", B: "Sp", length: 20 }, + { A: "Lu", B: "Sp", length: 15 }, + + { A: "Sp", B: "Nb", length: 11 }, + { A: "Nb", B: "SIL", length: 8 }, + + { A: "SIL", B: "Hd", length: 15 }, + { A: "SIL", B: "Br", length: 16 }, + + { A: "Br", B: "We", length: 12 }, + { A: "Br", B: "Ka", length: 22 }, + + { A: "Ka", B: "We", length: 14 }, + { A: "Ka", B: "Wo", length: 12 }, + + { A: "Wo", B: "Kan", length: 6 }, + { A: "Kan", B: "LD", length: 11 }, + + { A: "LD", B: "Neu", length: 20 }, + { A: "Neu", B: "AK1", length: 14 }, + + { A: "Neu", B: "Sp", length: 21 }, + { A: "LD", B: "Sp", length: 28 }, + + { A: "Sp", B: "Ger", length: 18 }, + { A: "Ger", B: "Br", length: 20 }, + { A: "Ger", B: "Wo", length: 23 }, + ]; + + it("should return all paths for 19 destinations", () => { + expect(findShortesPaths("A", stations4, streets4)).toEqual(true); + }); +}); + +// describe("Another description for the tests. Maybe those which are throwing errors", () => { +// it("should return something for this input", () => { +// expect(true).toEqual(true); +// }); +// }); diff --git a/Problems/Shortes-Path-Dijkstra/solver.ts b/Problems/Shortes-Path-Dijkstra/solver.ts new file mode 100644 index 0000000..9f39425 --- /dev/null +++ b/Problems/Shortes-Path-Dijkstra/solver.ts @@ -0,0 +1,14 @@ +interface Street { + A: string; // An id of a station + B: string; // An id of a station + length: number; // The length, how much time it will take to proceed +} + +interface Output { + path: string[]; + length: number; +} + +export default function findShortesPaths(start: string, stations: string[], streets: Street[]): Output { + throw new Error("Solution yet not implemented!"); +} From 01203534841ba6c7a6e81cbe4feba0544932977d Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 12 Jan 2026 13:19:51 +0100 Subject: [PATCH 2/3] First solution --- .vscode/settings.json | 2 +- Problems/Dependable-Jobs-Schedule/solver.ts | 2 - .../shortes-path-dijkstra.test.ts | 7 +- Problems/Shortes-Path-Dijkstra/solver.ts | 81 ++++++++++++++++++- 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index de4ff35..376d887 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,5 @@ "source.organizeImports.biome": "explicit" }, "cSpell.language": "en,de-DE", - "cSpell.words": ["amira", "bella", "fibonacci"] + "cSpell.words": ["amira", "bella", "distanz", "fibonacci"] } diff --git a/Problems/Dependable-Jobs-Schedule/solver.ts b/Problems/Dependable-Jobs-Schedule/solver.ts index 02468d8..59eb59a 100644 --- a/Problems/Dependable-Jobs-Schedule/solver.ts +++ b/Problems/Dependable-Jobs-Schedule/solver.ts @@ -14,8 +14,6 @@ export default function finishAll(taskAmount: number, dependencies: number[][]): } } - console.log(dep); - for (let i = 0; i < dependencies.length; i++) { for (const d of Array.from(dep.entries())) { if (!canBeFinished.get(d[0])) { diff --git a/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts b/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts index 19c7010..92362c0 100644 --- a/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts +++ b/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts @@ -37,13 +37,18 @@ describe("Returns the shortes path from 'A' to all other Destinations", () => { { A: "F", B: "G", - length: 2, + length: 5, }, { A: "G", B: "C", length: 2, }, + { + A: "E", + B: "F", + length: 1, + }, ]; it("should return all paths for 7 destinations", () => { diff --git a/Problems/Shortes-Path-Dijkstra/solver.ts b/Problems/Shortes-Path-Dijkstra/solver.ts index 9f39425..dda38f2 100644 --- a/Problems/Shortes-Path-Dijkstra/solver.ts +++ b/Problems/Shortes-Path-Dijkstra/solver.ts @@ -10,5 +10,84 @@ interface Output { } export default function findShortesPaths(start: string, stations: string[], streets: Street[]): Output { - throw new Error("Solution yet not implemented!"); + const resultMap: Map = new Map(); + const connections: Map> = new Map(); // Map> + const isStationVisited: Map = new Map(); + + const visitedStations = []; + + const findSmallest = () => { + let smallest = ""; + let smallestLength = Infinity; + resultMap.forEach((element, key) => { + if (!isStationVisited.get(key) && element.length < smallestLength) { + smallest = key; + smallestLength = element.length; + } + }); + return smallest; + }; + + // Create result map + stations.forEach((station) => { + if (station === start) { + resultMap.set(station, { length: 0, previousStation: "" }); + } else resultMap.set(station, { length: Infinity, previousStation: "" }); + }); + + // Create connection map + streets.forEach((street) => { + const addConnection = (stationA: string, stationB: string) => { + if (!connections.has(stationA)) { + connections.set(stationA, new Map().set(stationB, street.length)); + } else { + connections.get(stationA)?.set(stationB, street.length); + } + }; + + addConnection(street.A, street.B); + addConnection(street.B, street.A); + }); + + // Create isStationVisited + stations.forEach((station) => isStationVisited.set(station, false)); + + let current = start; + let smallest = start; + let smallestLength = Infinity; + while (visitedStations.length < stations.length) { + if (!connections.has(current)) { + throw new Error("The given current location isn't connected to any other station!"); + } else { + const currentLength = resultMap.get(current)!.length; + + if (current === smallest) { + smallestLength = Infinity; + } + + console.log(current); + Array.from(connections.get(current)!.entries()).forEach(([key, distanz]) => { + console.log(`${key} -> ${distanz}`); + if (!isStationVisited.get(key)) { + const length = currentLength + distanz; + + if (resultMap.get(key)!.length > length) { + if (length < smallestLength) { + smallest = key; + smallestLength = length; + } + + resultMap.set(key, { length, previousStation: current }); + } + } + }); + visitedStations.push(current); + isStationVisited.set(current, true); + + current = smallestLength >= Infinity ? findSmallest() : findSmallest(); + // current = smallest; + } + } + + console.log(resultMap); } From a36a16e546ea390689159cc63bcb7ef5d3fd85a7 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 12 Jan 2026 14:18:03 +0100 Subject: [PATCH 3/3] Better solution --- .../shortes-path-dijkstra.test.ts | 214 +++++++++++++++++- Problems/Shortes-Path-Dijkstra/solver.ts | 186 +++++++++------ 2 files changed, 324 insertions(+), 76 deletions(-) diff --git a/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts b/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts index 92362c0..ed7ddf9 100644 --- a/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts +++ b/Problems/Shortes-Path-Dijkstra/shortes-path-dijkstra.test.ts @@ -50,9 +50,39 @@ describe("Returns the shortes path from 'A' to all other Destinations", () => { length: 1, }, ]; + const result1 = [ + { + length: 0, + path: ["A"], + }, + { + length: 1, + path: ["A", "B"], + }, + { + length: 3, + path: ["A", "B", "C"], + }, + { + length: 10, + path: ["A", "B", "C", "D"], + }, + { + length: 9, + path: ["A", "G", "F", "E"], + }, + { + length: 8, + path: ["A", "G", "F"], + }, + { + length: 3, + path: ["A", "G"], + }, + ]; it("should return all paths for 7 destinations", () => { - expect(findShortesPaths("A", stations1, streets1)).toEqual(true); + expect(findShortesPaths("A", stations1, streets1)).toEqual(result1); }); const stations2 = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]; @@ -143,9 +173,51 @@ describe("Returns the shortes path from 'A' to all other Destinations", () => { length: 10, }, ]; + const result2 = [ + { + length: 0, + path: ["A"], + }, + { + length: 5, + path: ["A", "B"], + }, + { + length: 2, + path: ["A", "C"], + }, + { + length: 6, + path: ["A", "D"], + }, + { + length: 25, + path: ["A", "D", "G", "H", "E"], + }, + { + length: 19, + path: ["A", "D", "F"], + }, + { + length: 8, + path: ["A", "D", "G"], + }, + { + length: 16, + path: ["A", "D", "G", "H"], + }, + { + length: 20, + path: ["A", "D", "G", "J", "I"], + }, + { + length: 14, + path: ["A", "D", "G", "J"], + }, + ]; it("should return all paths for 10 destinations", () => { - expect(findShortesPaths("A", stations2, streets2)).toEqual(true); + expect(findShortesPaths("A", stations2, streets2)).toEqual(result2); }); const stations3 = ["A", "B", "C", "D", "E", "F", "G", "H", "Z"]; @@ -173,9 +245,47 @@ describe("Returns the shortes path from 'A' to all other Destinations", () => { { A: "H", B: "Z", length: 2 }, ]; + const result3 = [ + { + length: 0, + path: ["A"], + }, + { + length: 2, + path: ["A", "B"], + }, + { + length: 2, + path: ["A", "C"], + }, + { + length: 3, + path: ["A", "D"], + }, + { + length: 4, + path: ["A", "D", "E"], + }, + { + length: 4, + path: ["A", "B", "F"], + }, + { + length: 6, + path: ["A", "B", "F", "G"], + }, + { + length: 5, + path: ["A", "D", "H"], + }, + { + length: 7, + path: ["A", "D", "H", "Z"], + }, + ]; it("should return all paths for 9 destinations", () => { - expect(findShortesPaths("A", stations3, streets3)).toEqual(true); + expect(findShortesPaths("A", stations3, streets3)).toEqual(result3); }); const stations4 = [ @@ -241,14 +351,100 @@ describe("Returns the shortes path from 'A' to all other Destinations", () => { { A: "Ger", B: "Br", length: 20 }, { A: "Ger", B: "Wo", length: 23 }, ]; + const result4 = [ + { + length: 0, + path: ["A"], + }, + { + length: 12, + path: ["A", "AK1"], + }, + { + length: 22, + path: ["A", "AK1", "Lu"], + }, + { + length: 25, + path: ["A", "AK1", "Lu", "Ma"], + }, + { + length: 34, + path: ["A", "AK1", "Lu", "Ma", "Vie"], + }, + { + length: 42, + path: ["A", "AK1", "Lu", "Ma", "Vie", "We"], + }, + { + length: 41, + path: ["A", "AK1", "Lu", "Ma", "AK2"], + }, + { + length: 50, + path: ["A", "AK1", "Lu", "Ma", "AK2", "Hd"], + }, + { + length: 49, + path: ["A", "AK1", "Lu", "Ma", "AK2", "Sch"], + }, + { + length: 32, + path: ["A", "AK1", "Sp"], + }, + { + length: 43, + path: ["A", "AK1", "Sp", "Nb"], + }, + { + length: 51, + path: ["A", "AK1", "Sp", "Nb", "SIL"], + }, + { + length: 54, + path: ["A", "AK1", "Lu", "Ma", "Vie", "We", "Br"], + }, + { + length: 56, + path: ["A", "AK1", "Lu", "Ma", "Vie", "We", "Ka"], + }, + { + length: 63, + path: ["A", "AK1", "Neu", "LD", "Kan", "Wo"], + }, + { + length: 57, + path: ["A", "AK1", "Neu", "LD", "Kan"], + }, + { + length: 46, + path: ["A", "AK1", "Neu", "LD"], + }, + { + length: 26, + path: ["A", "AK1", "Neu"], + }, + { + length: 50, + path: ["A", "AK1", "Sp", "Ger"], + }, + ]; it("should return all paths for 19 destinations", () => { - expect(findShortesPaths("A", stations4, streets4)).toEqual(true); + expect(findShortesPaths("A", stations4, streets4)).toEqual(result4); }); }); -// describe("Another description for the tests. Maybe those which are throwing errors", () => { -// it("should return something for this input", () => { -// expect(true).toEqual(true); -// }); -// }); +describe("When the given Stations are invalid, the function should throw the correct error!", () => { + it("should throw an Error because the start isn't connected to other stations", () => { + expect(() => findShortesPaths("A", ["A", "B", "C"], [{ A: "B", B: "C", length: 15 }])).toThrowError( + `The station ${"A"} has no connection to any other station!` + ); + }); + + it("should throw an Error because some stations aren't connected to other stations", () => { + expect(() => findShortesPaths("A", ["A", "B", "C"], [{ A: "A", B: "C", length: 15 }])).toThrowError( + `It is not possible to calculate a path for station '${"B"}'. It probably hasn't a connection to other stations!` + ); + }); +}); diff --git a/Problems/Shortes-Path-Dijkstra/solver.ts b/Problems/Shortes-Path-Dijkstra/solver.ts index dda38f2..b214415 100644 --- a/Problems/Shortes-Path-Dijkstra/solver.ts +++ b/Problems/Shortes-Path-Dijkstra/solver.ts @@ -4,90 +4,142 @@ interface Street { length: number; // The length, how much time it will take to proceed } -interface Output { +type Output = { path: string[]; length: number; +}[]; + +// Tiny min-heap (priority queue) for { station, length } +type PQItem = { station: string; length: number }; + +class MinHeap { + private data: PQItem[] = []; + + size() { + return this.data.length; + } + + push(item: PQItem) { + this.data.push(item); + this.bubbleUp(this.data.length - 1); + } + + pop(): PQItem | undefined { + if (this.data.length === 0) return undefined; + const top = this.data[0]; + const last = this.data.pop()!; + if (this.data.length > 0) { + this.data[0] = last; + this.bubbleDown(0); + } + return top; + } + + private bubbleUp(i: number) { + while (i > 0) { + const p = Math.floor((i - 1) / 2); + if (this.data[p].length <= this.data[i].length) break; + [this.data[p], this.data[i]] = [this.data[i], this.data[p]]; + i = p; + } + } + + private bubbleDown(i: number) { + for (;;) { + const l = i * 2 + 1; + const r = i * 2 + 2; + let smallest = i; + + if (l < this.data.length && this.data[l].length < this.data[smallest].length) { + smallest = l; + } + if (r < this.data.length && this.data[r].length < this.data[smallest].length) { + smallest = r; + } + if (smallest === i) break; + + [this.data[i], this.data[smallest]] = [this.data[smallest], this.data[i]]; + i = smallest; + } + } } export default function findShortesPaths(start: string, stations: string[], streets: Street[]): Output { const resultMap: Map = new Map(); const connections: Map> = new Map(); // Map> - const isStationVisited: Map = new Map(); - - const visitedStations = []; - const findSmallest = () => { - let smallest = ""; - let smallestLength = Infinity; - resultMap.forEach((element, key) => { - if (!isStationVisited.get(key) && element.length < smallestLength) { - smallest = key; - smallestLength = element.length; - } + // Init resultMap + for (const station of stations) { + resultMap.set(station, { + length: station === start ? 0 : Infinity, + previousStation: "", }); - return smallest; - }; - - // Create result map - stations.forEach((station) => { - if (station === start) { - resultMap.set(station, { length: 0, previousStation: "" }); - } else resultMap.set(station, { length: Infinity, previousStation: "" }); - }); - - // Create connection map - streets.forEach((street) => { - const addConnection = (stationA: string, stationB: string) => { - if (!connections.has(stationA)) { - connections.set(stationA, new Map().set(stationB, street.length)); - } else { - connections.get(stationA)?.set(stationB, street.length); - } + } + + // Build adjacency map (undirected) + for (const street of streets) { + const add = (from: string, to: string, len: number) => { + if (!connections.has(from)) connections.set(from, new Map()); + connections.get(from)!.set(to, len); }; + add(street.A, street.B, street.length); + add(street.B, street.A, street.length); + } + + // Dijkstra with a priority queue (no findSmallest scan) + const visited = new Set(); + const pq = new MinHeap(); + pq.push({ station: start, length: 0 }); + + while (pq.size() > 0) { + const item = pq.pop()!; + const current = item.station; + const currentLength = resultMap.get(current)!.length; - addConnection(street.A, street.B); - addConnection(street.B, street.A); - }); + // Skip outdated queue entries (common trick when no decrease-key) + if (item.length !== currentLength) continue; - // Create isStationVisited - stations.forEach((station) => isStationVisited.set(station, false)); + // If we already finalized this node, skip + if (visited.has(current)) continue; + visited.add(current); - let current = start; - let smallest = start; - let smallestLength = Infinity; - while (visitedStations.length < stations.length) { - if (!connections.has(current)) { - throw new Error("The given current location isn't connected to any other station!"); - } else { - const currentLength = resultMap.get(current)!.length; + const neighbors = connections.get(current); + if (!neighbors) throw new Error(`The station ${current} has no connection to any other station!`); - if (current === smallest) { - smallestLength = Infinity; + for (const [next, dist] of neighbors.entries()) { + if (visited.has(next)) continue; + + const newLen = currentLength + dist; + if (newLen < resultMap.get(next)!.length) { + resultMap.set(next, { length: newLen, previousStation: current }); + pq.push({ station: next, length: newLen }); } + } + } + + const outputs: Output = []; + + for (const station of stations) { + if (resultMap.get(station)!.length >= Infinity) + throw new Error( + `It is not possible to calculate a path for station '${station}'. It probably hasn't a connection to other stations!` + ); - console.log(current); - Array.from(connections.get(current)!.entries()).forEach(([key, distanz]) => { - console.log(`${key} -> ${distanz}`); - if (!isStationVisited.get(key)) { - const length = currentLength + distanz; - - if (resultMap.get(key)!.length > length) { - if (length < smallestLength) { - smallest = key; - smallestLength = length; - } - - resultMap.set(key, { length, previousStation: current }); - } - } - }); - visitedStations.push(current); - isStationVisited.set(current, true); - - current = smallestLength >= Infinity ? findSmallest() : findSmallest(); - // current = smallest; + const path: string[] = []; + let current = station; + + while (current !== "") { + path.push(current); + current = resultMap.get(current)!.previousStation; } + + path.reverse(); + + outputs.push({ + path, + length: resultMap.get(station)!.length, + }); } - console.log(resultMap); + return outputs; }