From 062703489b92aeaea8269f5d2b2005d7faf48860 Mon Sep 17 00:00:00 2001 From: trekhleb <3000285+trekhleb@users.noreply.github.com> Date: Sat, 8 Nov 2025 23:03:44 +0100 Subject: [PATCH 1/2] Add Dijkstra algorithm illustrations and explanations --- src/algorithms/graph/dijkstra/README.md | 52 +++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/algorithms/graph/dijkstra/README.md b/src/algorithms/graph/dijkstra/README.md index ec74531bf..98e859697 100644 --- a/src/algorithms/graph/dijkstra/README.md +++ b/src/algorithms/graph/dijkstra/README.md @@ -33,55 +33,93 @@ and updates the neighbor's distance if smaller. Mark visited ## Step-by-step Dijkstra's algorithm example -Let's say we have a graph of nodes, where each edge has a distance parameter between nodes. Let's say the distance from node `A` and node `B` is `7 meters`, and so on. +Let's say we have a weighted graph of nodes, where each edge has a distance parameter between nodes. Let's say the distance from node `A` and node `B` is `7 meters` (or just `7m` for brevity), and so on. The algorithm uses [Priority Queue](../../../data-structures/priority-queue/) to always pull the next unvisited vertex that has the smallest distance from the origin node. -The start node by definition has a distance of `0m` from itself. So we start from it, the only node in the priority queue. +The start node, by definition, has a distance of `0m` from itself. So we start from it, the only node in the priority queue. + +The rest of the nodes will be added to the priority queue later during the graph traversal (while visiting the neighbors). ![Dijkstra step 1](./images/dijkstra-01.png) -Each neighbor of the pulled (from the queue) node is being traversed to calculate the distance to it from the origin. For example the distance from `A` to `B` is `0 + 7 = 7m`, and so on. +Each neighbor of the pulled (from the queue) node is being traversed to calculate the distance to it from the origin. For example, the distance from `A` to `B` is `0m + 7m = 7m`, and so on. + +Every time we visit a not-yet-seen neighbor, we add it to the priority queue, where the priority is the distance to the neighbor node from the origin node. -Every time we visit a not yet seen neighbor, we add it to the priority queue, where the priority is a distance to the neighbor node from the origin node. +The node `B` is being added to the min priority queue to be traversed later. ![Dijkstra step 2](./images/dijkstra-02.png) +Visiting the next neighbor `C` of the node `A`. The distance from the origin node `A` to `C` is `0m + 9m = 9m`. + +The node `C` is being added to the min priority queue to be traversed later. + ![Dijkstra step 3](./images/dijkstra-03.png) +Same for the node `F`. The current distance to `F` from the origin node `A` is `0m + 14m = 14m`. + +The node `F` is being added to the min priority queue to be traversed later. + ![Dijkstra step 4](./images/dijkstra-04.png) -Once all the neighbors of the current nodes were checked, the current node is being put to the `visited` set. We don't want to visit such nodes ones again during the upcoming traverses. +Once all the neighbors of the current node were checked, the current node was added to the `visited` set. We don't want to visit such nodes once again during the upcoming traverses. Now, let's pull out the next node from the priority queue that is closest to the origin (has a shorter distance). We start visiting its neighbors as well. ![Dijkstra step 5](./images/dijkstra-05.png) -If the node that we're visiting (in this case the node `C`) is already in the queue, it means we already calculated the distance to it before but from another node/path (`A → C`). If the current distance to it (from the current neighbor node `A → B → C`) is shorter than the one that was calculated before, we update the distance (in the priority queue) to the shortest one, since we're searching for the shortest paths. If the distance from the current neighbor is actually longer that the once was already calculated, we leave it like this without updating the `C` node distance in the queue. +If the node that we're visiting (in this case, the node `C`) is already in the queue, it means we already calculated the distance to it before, but from another node/path (`A → C`). If the current distance to it (from the current neighbor node `A → B → C`) is shorter than the one that was calculated before, we update the distance (in the priority queue) to the shortest one, since we're searching for the shortest paths. If the distance from the current neighbor is actually longer than the one that was already calculated, we leave it like this without updating the `C` node distance in the queue. + +While visiting the node `C` via `B` (the path `A → B → C`), we see that the distance to it would be `7m + 10m = 17m`. This is actually longer than the already recorded distance of `9m` for the path `A → C`. In such cases, we just ignore the longest distance and don't update the priority (the minimum found distance at the moment from the origin node). ![Dijkstra step 6](./images/dijkstra-06.png) +Visiting another neighbor of `B`, which is `D`. The distance to `D` equals `7m + 15m = 22m`. Since we didn't visit `D` yet and it is not in the min priority queue, let's just add it to the queue with a priority (distance) of `22m`. + ![Dijkstra step 7](./images/dijkstra-07.png) +At this point, all of the neighbors of `B` were traversed, so we add `B` to the `visited` set. Next, we pull the node that is closest to the origin node from the priority queue. + ![Dijkstra step 8](./images/dijkstra-08.png) +Traversing the unvisited neighbors of the node `C`. The distance to node `F` via `C` (the path `A → C → F`) is `9m + 2m = 11m`. This is actually shorter than the previously recorded path `A → F` of `14m` length. In such cases, we update the distance to `F` to `11m` and update its priority in the queue from `14m` to `11m`. We've just found a shorter path to `F`. + ![Dijkstra step 9](./images/dijkstra-09.png) +The same goes for `D`. We've just found a shorter path to `D`, where `A → C → D` is shorter than `A → B → D`. Updating the distance from `22m` to `20m`. + ![Dijkstra step 10](./images/dijkstra-10.png) +All neighbors of `C` were traversed, so we may add `C` to the `visited` set. Pulling the next closest node from the queue, which is `F`. + ![Dijkstra step 11](./images/dijkstra-11.png) +Recording the distance to `E` as `11m + 9m = 20m`. + ![Dijkstra step 12](./images/dijkstra-12.png) +Adding the node `F` to the `visited` set, and pulling the next closest node `D` from the queue. + ![Dijkstra step 13](./images/dijkstra-13.png) +Distance to `E` via `D` is `20m + 6m = 26m`. This is longer than the already calculated distance to `E` from `F`, which is `20m`. We can discard the longer distance. + ![Dijkstra step 14](./images/dijkstra-14.png) +Node `D` is visited now. + ![Dijkstra step 15](./images/dijkstra-15.png) +Node `E` is visited now as well. We've finished the graph traversal. + ![Dijkstra step 16](./images/dijkstra-16.png) -Now we now the shortest distances to ech node from the start node `A`. In practice, during the distance calculations we also record the the `previousVertices` for each node to be able to show the exact sequence of nodes that forms the shortest path. +Now we know the shortest distances to each node from the start node `A`. + +In practice, during the distance calculations, we also record the `previousVertices` for each node to be able to show the exact sequence of nodes that form the shortest path. + +For example, the shorter path from `A` to `E` is `A → C → F → E`. ## Implementation example From f41e6abe8a2f79b2edf54782badeae90752cbdfb Mon Sep 17 00:00:00 2001 From: trekhleb <3000285+trekhleb@users.noreply.github.com> Date: Sat, 8 Nov 2025 23:08:37 +0100 Subject: [PATCH 2/2] Add Dijkstra algorithm illustrations and explanations --- src/algorithms/graph/dijkstra/README.md | 3 +- src/algorithms/graph/dijkstra/README.uk-UA.md | 132 ++++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/algorithms/graph/dijkstra/README.uk-UA.md diff --git a/src/algorithms/graph/dijkstra/README.md b/src/algorithms/graph/dijkstra/README.md index 98e859697..91cde7908 100644 --- a/src/algorithms/graph/dijkstra/README.md +++ b/src/algorithms/graph/dijkstra/README.md @@ -1,7 +1,8 @@ # Dijkstra's Algorithm _Read this in other languages:_ -[_한국어_](README.ko-KR.md) +[_한국어_](README.ko-KR.md), +[_Українська_](README.uk-UA.md) Dijkstra's algorithm is an algorithm for finding the shortest paths between nodes in a graph, which may represent, for example, diff --git a/src/algorithms/graph/dijkstra/README.uk-UA.md b/src/algorithms/graph/dijkstra/README.uk-UA.md new file mode 100644 index 000000000..0b901b5b0 --- /dev/null +++ b/src/algorithms/graph/dijkstra/README.uk-UA.md @@ -0,0 +1,132 @@ +# Алгоритм Дейкстри + +_Читайте іншими мовами:_ +[_English_](README.md), +[_한국어_](README.ko-KR.md) + +Алгоритм Дейкстри — це алгоритм пошуку найкоротших шляхів між вершинами графа, який може представляти, наприклад, дорожню мережу. + +Існує багато варіантів цього алгоритму; оригінальний варіант Дейкстри знаходив найкоротший шлях між двома вершинами, але більш поширений варіант фіксує одну вершину як «джерело» і знаходить найкоротші шляхи від неї до всіх інших вершин графа, утворюючи дерево найкоротших шляхів. + +![Dijkstra](https://upload.wikimedia.org/wikipedia/commons/5/57/Dijkstra_Animation.gif) + +Алгоритм Дейкстри для пошуку найкоротшого шляху між `a` та `b`. +Він вибирає непереглянуту вершину з найменшою відстанню, обчислює відстань через неї до кожного непереглянутого сусіда й оновлює відстань до сусіда, якщо вона менша. Коли всі сусіди опрацьовані — вершина позначається як відвідана (червоним кольором). + +## Практичні застосування алгоритму Дейкстри + +- GPS / навігаційні системи +- Оптимізація маршрутів громадського транспорту та авіаліній +- Інтернет-маршрутизація (протоколи OSPF, IS-IS) +- Оптимізація мережевого трафіку та затримок +- Пошук шляху в іграх (найкоротший шлях на карті) +- Оптимізація маршрутів доставки +- Проєктування логістичних та транспортних мереж + +## Покроковий приклад алгоритму Дейкстри + +Припустімо, ми маємо зважений граф вершин, де кожне ребро має певну довжину. Наприклад, відстань між вершинами `A` і `B` становить `7 метрів` (або просто `7m`). + +Алгоритм використовує [чергу з пріоритетом](../../../data-structures/priority-queue/), щоб завжди вибирати наступну непереглянуту вершину з найменшою відстанню від початкової вершини. + +Початкова вершина, за визначенням, має відстань `0m` від самої себе. З неї й починається пошук — вона єдина в черзі на початку. + +Решта вершин додаються до черги з пріоритетом пізніше, у процесі обходу графа (під час відвідування сусідів). + +![Dijkstra step 1](./images/dijkstra-01.png) + +Кожен сусід витягнутої з черги вершини перевіряється для обчислення відстані до нього від початкової вершини. Наприклад, відстань від `A` до `B` — це `0m + 7m = 7m`. + +Щоразу, коли ми відвідуємо нового (ще не баченого) сусіда, ми додаємо його в чергу з пріоритетом, де пріоритет — це відстань до цієї вершини від початкової. + +Вершину `B` додаємо до мінімальної черги з пріоритетом, щоб відвідати її пізніше. + +![Dijkstra step 2](./images/dijkstra-02.png) + +Відвідуємо наступного сусіда `C` вершини `A`. Відстань від `A` до `C` становить `0m + 9m = 9m`. + +Додаємо вершину `C` до мінімальної черги з пріоритетом. + +![Dijkstra step 3](./images/dijkstra-03.png) + +Те саме робимо для вершини `F`. Поточна відстань від `A` до `F` — `0m + 14m = 14m`. + +Вершину `F` додаємо до черги для подальшого обходу. + +![Dijkstra step 4](./images/dijkstra-04.png) + +Коли всі сусіди поточної вершини перевірені, ми додаємо її до множини `visited`. Такі вершини більше не відвідуємо. + +Тепер вибираємо з черги наступну вершину, найближчу до початкової, і починаємо відвідувати її сусідів. + +![Dijkstra step 5](./images/dijkstra-05.png) + +Якщо вершина, яку ми відвідуємо (наприклад, `C`), уже є в черзі, це означає, що відстань до неї вже обчислювалася раніше з іншого шляху (`A → C`). Якщо нова відстань (через інший шлях, наприклад `A → B → C`) менша, ми оновлюємо її в черзі. Якщо більша — залишаємо без змін. + +Під час відвідування `C` через `B` (`A → B → C`), відстань дорівнює `7m + 10m = 17m`. Це більше, ніж уже відома `9m` для шляху `A → C`. Тож ми ігноруємо довший шлях. + +![Dijkstra step 6](./images/dijkstra-06.png) + +Відвідуємо іншого сусіда `B` — вершину `D`. Відстань до `D` дорівнює `7m + 15m = 22m`. +Оскільки `D` ще не відвідано і її немає в черзі, додаємо її з пріоритетом `22m`. + +![Dijkstra step 7](./images/dijkstra-07.png) + +Тепер усіх сусідів `B` відвідано, тож додаємо `B` до множини `visited`. +Наступною вибираємо вершину, що найближча до початкової. + +![Dijkstra step 8](./images/dijkstra-08.png) + +Відвідуємо непереглянутих сусідів вершини `C`. +Відстань до вершини `F` через `C` (`A → C → F`) дорівнює `9m + 2m = 11m`. +Це коротше за попередній шлях `A → F` довжиною `14m`. +Тому оновлюємо відстань до `F` — з `14m` до `11m`. Ми щойно знайшли коротший шлях. + +![Dijkstra step 9](./images/dijkstra-09.png) + +Так само для `D`: шлях `A → C → D` коротший за `A → B → D`. +Оновлюємо відстань з `22m` до `20m`. + +![Dijkstra step 10](./images/dijkstra-10.png) + +Усі сусіди `C` пройдені, додаємо її до `visited`. +Дістаємо з черги наступну найближчу вершину — `F`. + +![Dijkstra step 11](./images/dijkstra-11.png) + +Записуємо відстань до `E`: `11m + 9m = 20m`. + +![Dijkstra step 12](./images/dijkstra-12.png) + +Додаємо `F` до множини `visited`, далі дістаємо `D`. + +![Dijkstra step 13](./images/dijkstra-13.png) + +Відстань до `E` через `D`: `20m + 6m = 26m`. +Це більше, ніж уже обчислені `20m` через `F`, тому ігноруємо довший шлях. + +![Dijkstra step 14](./images/dijkstra-14.png) + +Вершину `D` відвідано. + +![Dijkstra step 15](./images/dijkstra-15.png) + +Вершину `E` також відвідано. Обхід графа завершено. + +![Dijkstra step 16](./images/dijkstra-16.png) + +Тепер ми знаємо найкоротші відстані до кожної вершини від початкової `A`. + +На практиці під час обчислення відстаней також зберігаються `previousVertices` — попередні вершини, щоб можна було відновити повний шлях. + +Наприклад, найкоротший шлях від `A` до `E` — це `A → C → F → E`. + +## Приклад реалізації + +- [dijkstra.js](./dijkstra.js) + +## Джерела + +- [Вікіпедія](https://uk.wikipedia.org/wiki/Алгоритм_Дейкстри) +- [Відео на YouTube від Nathaniel Fan](https://www.youtube.com/watch?v=gdmfOwyQlcI&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) +- [Відео на YouTube від Tushar Roy](https://www.youtube.com/watch?v=lAXZGERcDf4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)