خوارزمية Dijkstra
آخر تحديث
تجد خوارزمية Dijkstra أقصر مسار من عقدة المصدر إلى كل عقدة أخرى في رسم بياني ذي أوزان حواف غير سالبة. تحتفظ بمسافة مؤقتة لكل عقدة، وتثبّت مرارًا العقدة غير المثبتة ذات أصغر مسافة مؤقتة، وتسترخي حوافها، محدّثةً مسافة الجار كلما وُجد مسار أقصر عبر العقدة الحالية. اضغط تشغيل بالأعلى لمشاهدة المسافات تنخفض مع تثبيت كل عقدة.
الفكرة الأساسية جشعة: بمجرد اختيار أقرب عقدة غير مثبتة تصبح مسافتها نهائية، لأن أي مسار آخر إليها يجب أن يمر عبر عقدة أبعد بالفعل. باستخدام طابور أولوية قائم على كومة ثنائية، تعمل Dijkstra في زمن O((V + E) log V). تتطلب أوزانًا غير سالبة - استخدم Bellman-Ford إذا كانت الحواف قد تكون سالبة.
التعقيد الزمني والمكاني
| التنفيذ | التعقيد | ملاحظات |
|---|---|---|
| كومة ثنائية | O((V + E) log V) | الخيار الشائع والعملي |
| مسح المصفوفة | O(V²) | أبسط؛ مناسب للرسوم البيانية الكثيفة |
| المساحة | O(V) | المسافات + طابور الأولوية |
| يتطلب | أوزان غير سالبة | الحواف السالبة تكسر الاختيار الجشع |
خطوة بخطوة
| الخطوة | ماذا يحدث |
|---|---|
| 1 | اضبط مسافة المصدر إلى 0 وكل الباقي إلى ما لا نهاية. |
| 2 | اختر العقدة غير المثبتة ذات أصغر مسافة مؤقتة. |
| 3 | علّمها كمثبتة - أصبحت أقصر مسافة لها نهائية الآن. |
| 4 | لكل جار، احسب المسافة-عبر-الحالية + وزن الحافة. |
| 5 | إذا كانت أصغر من مسافة الجار الحالية، فاسترخِها. |
| 6 | كرّر حتى تُثبَّت كل العقد القابلة للوصول. |
مثال محلول
أقصر المسارات من المصدر A في الرسم البياني ذي الحواف A-B=4 وA-C=1 وC-B=2 وC-D=5 وB-D=1:
| الخطوة | تثبيت | المسافات | الإجراء |
|---|---|---|---|
| 0 | - | A=0, B=∞, C=∞, D=∞ | التهيئة: المصدر A=0، وكل الباقي ما لا نهاية. |
| 1 | A (0) | B=4, C=1, D=∞ | استرخِ حواف A: اضبط B=4 وC=1. |
| 2 | C (1) | B=3, D=6 | عبر C: B=1+2=3 يتفوق على 4؛ D=1+5=6. |
| 3 | B (3) | D=4 | عبر B: D=3+1=4 يتفوق على 6. |
| 4 | D (4) | A=0, C=1, B=3, D=4 | ثبّت D؛ لا شيء متبقٍّ للاسترخاء. انتهى. |
متى تستخدم خوارزمية Dijkstra
| استخدمها عندما | تجنّبها عندما |
|---|---|
| تكون كل أوزان الحواف غير سالبة | قد تكون أي حافة سالبة - استخدم Bellman-Ford |
| تحتاج أقصر المسارات من مصدر واحد إلى كل العقد | تحتاج أقصر المسارات بين كل الأزواج - Floyd-Warshall أبسط |
| يكون الرسم البياني موزونًا وتريد مسافات دقيقة | يكون الرسم البياني غير موزون - BFS البسيط أسرع وأبسط |
| يتوفر لديك كومة أو طابور أولوية جيد | تريد الوصول بسرعة إلى هدف واحد بمساعدة إرشادية - استخدم A* |
كود Dijkstra's Algorithm
تنفيذ نظيف وقابل للتشغيل لخوارزمية Dijkstra's Algorithm بلغات Python, JavaScript, Java, C++, C. اختر لغة، وانسخ الكود، أو افتحه محمّلًا مسبقًا في ساحة تجربة Coddy.
كود Dijkstra's Algorithm بلغة Python
1import heapq2
3
4def dijkstra(graph, start):5 dist = {node: float("inf") for node in graph}6 dist[start] = 07 pq = [(0, start)]8 while pq:9 d, node = heapq.heappop(pq)10 if d > dist[node]:11 continue # stale entry, a shorter path was already found12 for neighbor, weight in graph[node]:13 new_dist = d + weight14 if new_dist < dist[neighbor]:15 dist[neighbor] = new_dist16 heapq.heappush(pq, (new_dist, neighbor))17 return dist18
19
20graph = {21 "A": [("B", 4), ("C", 1)],22 "B": [("D", 1)],23 "C": [("B", 2), ("D", 5)],24 "D": [("E", 3)],25 "E": [],26}27
28for node, d in dijkstra(graph, "A").items():29 print(f"A -> {node}: {d}")كود Dijkstra's Algorithm بلغة JavaScript
1const graph = {2 A: { B: 4, C: 2 },3 B: { C: 5, D: 10 },4 C: { E: 3 },5 D: { F: 11 },6 E: { D: 4 },7 F: {},8};9
10function dijkstra(source) {11 const dist = {};12 const visited = new Set();13 for (const node in graph) dist[node] = Infinity;14 dist[source] = 0;15 while (visited.size < Object.keys(graph).length) {16 // JS has no built-in heap: linear scan for the closest node (O(V^2))17 let u = null;18 for (const node in dist) {19 if (!visited.has(node) && (u === null || dist[node] < dist[u])) {20 u = node;21 }22 }23 if (dist[u] === Infinity) break;24 visited.add(u);25 for (const [v, w] of Object.entries(graph[u])) {26 if (dist[u] + w < dist[v]) dist[v] = dist[u] + w;27 }28 }29 return dist;30}31
32console.log("Shortest distances from A:", dijkstra("A"));كود Dijkstra's Algorithm بلغة Java
1import java.util.ArrayList;2import java.util.Arrays;3import java.util.List;4import java.util.PriorityQueue;5
6public class Main {7 public static void main(String[] args) {8 int n = 6;9 List<List<int[]>> adj = new ArrayList<>();10 for (int i = 0; i < n; i++) adj.add(new ArrayList<>());11 int[][] edges = {12 {0, 1, 4}, {0, 2, 1}, {2, 1, 2}, {1, 3, 5},13 {2, 3, 8}, {3, 4, 3}, {2, 5, 10}, {4, 5, 2}14 };15 for (int[] e : edges) {16 adj.get(e[0]).add(new int[]{e[1], e[2]});17 adj.get(e[1]).add(new int[]{e[0], e[2]});18 }19
20 int[] dist = new int[n];21 Arrays.fill(dist, Integer.MAX_VALUE);22 dist[0] = 0;23 PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);24 pq.add(new int[]{0, 0}); // {node, distance}25 while (!pq.isEmpty()) {26 int[] cur = pq.poll();27 int node = cur[0], d = cur[1];28 if (d > dist[node]) continue; // stale queue entry29 for (int[] edge : adj.get(node)) {30 int next = edge[0], nd = d + edge[1];31 if (nd < dist[next]) {32 dist[next] = nd;33 pq.add(new int[]{next, nd});34 }35 }36 }37 System.out.println("Shortest distances from 0: " + Arrays.toString(dist));38 }39}كود Dijkstra's Algorithm بلغة C++
1#include <iostream>2#include <queue>3#include <vector>4
5int main() {6 const int INF = 1000000000;7 // Weighted directed graph: adj[u] = {(neighbor, weight), ...}8 std::vector<std::vector<std::pair<int, int>>> adj = {9 {{1, 4}, {2, 1}}, // 010 {{3, 1}}, // 111 {{1, 2}, {3, 5}}, // 212 {{4, 3}}, // 313 {}, // 414 };15 int n = static_cast<int>(adj.size());16 std::vector<int> dist(n, INF);17 dist[0] = 0;18 using State = std::pair<int, int>; // (distance, node)19 std::priority_queue<State, std::vector<State>, std::greater<State>> pq;20 pq.push({0, 0});21 while (!pq.empty()) {22 auto [d, u] = pq.top();23 pq.pop();24 if (d > dist[u]) continue; // stale queue entry25 for (auto [v, w] : adj[u]) {26 if (d + w < dist[v]) {27 dist[v] = d + w;28 pq.push({dist[v], v});29 }30 }31 }32 for (int v = 0; v < n; ++v) {33 std::cout << "Distance 0 -> " << v << ": " << dist[v] << "\n";34 }35 return 0;36}كود Dijkstra's Algorithm بلغة C
1#include <stdbool.h>2#include <stdio.h>3
4#define N 55#define INF 10000000006
7int main(void) {8 // Weighted directed graph: w[u][v] = 0 means no edge9 int w[N][N] = {10 {0, 4, 1, 0, 0},11 {0, 0, 0, 1, 0},12 {0, 2, 0, 5, 0},13 {0, 0, 0, 0, 3},14 {0, 0, 0, 0, 0},15 };16 int dist[N];17 bool done[N] = {false};18 for (int v = 0; v < N; v++) dist[v] = INF;19 dist[0] = 0;20 // O(V^2) scan for the closest unfinished node (no heap needed here)21 for (int iter = 0; iter < N; iter++) {22 int u = -1;23 for (int v = 0; v < N; v++) {24 if (!done[v] && (u == -1 || dist[v] < dist[u])) u = v;25 }26 if (dist[u] == INF) break; // remaining nodes are unreachable27 done[u] = true;28 for (int v = 0; v < N; v++) {29 if (w[u][v] > 0 && dist[u] + w[u][v] < dist[v]) {30 dist[v] = dist[u] + w[u][v];31 }32 }33 }34 for (int v = 0; v < N; v++) {35 printf("Distance 0 -> %d: %d\n", v, dist[v]);36 }37 return 0;38}أسئلة شائعة حول خوارزمية Dijkstra
ما هو التعقيد الزمني لخوارزمية Dijkstra؟
O((V + E) log V). النسخة البسيطة التي تمسح مصفوفة بحثًا عن الأصغر في كل خطوة هي O(V²)، والتي قد تكون في الواقع أسرع على الرسوم البيانية الكثيفة. كلتاهما تستخدم مساحة O(V).لماذا لا تعمل Dijkstra مع أوزان الحواف السالبة؟
ما الفرق بين Dijkstra وBFS؟
ما الفرق بين Dijkstra وبحث A*؟
متى ينبغي أن أستخدم Dijkstra بدلًا من Bellman-Ford؟
O((V + E) log V) مقابل O(V·E) لـ Bellman-Ford. اختر Bellman-Ford فقط عندما قد تكون الحواف سالبة أو تحتاج إلى اكتشاف الدورات السالبة. في الرسوم البيانية غير السالبة تكون Dijkstra الخيار الأفضل دائمًا تقريبًا.