0Pricing
Coding Interview Prep · درس

زمن تأخير الشبكة وإعادة بناء المسار

حلّ مسألة network-delay-time باستخدام Dijkstra، وأعد بناء أقصر مسار فعلي باستخدام خريطة الأسلاف، وناقش BFS ثنائي الاتجاه للرسوم البيانية الكبيرة.

زمن تأخير الشبكة وإعادة بناء المسار درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

مسألة Network Delay Time

Network Delay Time (LeetCode 743): لديك شبكة مكوّنة من n عقد وحواف موجهة موزونة تمثل أزمنة انتقال الإشارة، والمطلوب إيجاد أقل زمن تحتاجه إشارة مرسلة من العقدة k للوصول إلى جميع العقد. إذا تعذر الوصول إلى إحدى العقد، فأعد -1. هذا تطبيق مباشر لخوارزمية Dijkstra: الإجابة هي أكبر مسافة لأقصر مسار من k إلى جميع العقد.

الحل: Dijkstra + أكبر قيمة للمسافات

شغّل Dijkstra من المصدر k لإيجاد dist[v] لجميع العقد v. تكون الإجابة هي max(dist.values()). إذا كانت أي قيمة dist[v] لا تزال تساوي inf، فهذا يعني أن العقدة غير قابلة للوصول — فأعد -1. تنتقل الإشارة عبر جميع المسارات في الوقت نفسه، ولذلك تكون العقدة التي تستغرق أطول وقت للوصول إليها هي العامل المحدِّد.

import heapq
from collections import defaultdict

def networkDelayTime(times, n, k):
    graph = defaultdict(list)
    for u, v, w in times:
        graph[u].append((v, w))
    
    dist = {i: float('inf') for i in range(1, n+1)}
    dist[k] = 0
    heap = [(0, k)]
    
    while heap:
        d, u = heapq.heappop(heap)
        if d > dist[u]:
            continue
        for v, w in graph[u]:
            if dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                heapq.heappush(heap, (dist[v], v))
    
    ans = max(dist.values())
    return ans if ans < float('inf') else -1

print(networkDelayTime([[2,1,1],[2,3,1],[3,4,1]], 4, 2))  # 2

إعادة بناء المسار باستخدام المصفوفة prev

لإعادة بناء المسار الأقصر الفعلي بالتزامن مع حساب المسافات، احتفظ بقاموس prev يسجل أفضل عقدة سابقة لكل عقدة. كلما حدّثت dist[v]، اضبط prev[v] = u. بعد انتهاء Dijkstra، تتبّع المؤشرات prev إلى الخلف بدءاً من الوجهة حتى الوصول إلى المصدر، ثم اعكس الترتيب للحصول على المسار بالاتجاه الصحيح.

import heapq
from collections import defaultdict

def shortest_path_with_reconstruction(times, n, src, dst):
    graph = defaultdict(list)
    for u, v, w in times:
        graph[u].append((v, w))
    
    dist = {i: float('inf') for i in range(1, n+1)}
    prev = {i: None for i in range(1, n+1)}
    dist[src] = 0
    heap = [(0, src)]
    
    while heap:
        d, u = heapq.heappop(heap)
        if d > dist[u]: continue
        for v, w in graph[u]:
            if dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                prev[v] = u
                heapq.heappush(heap, (dist[v], v))
    
    # Reconstruct path from src to dst
    path, node = [], dst
    while node is not None:
        path.append(node)
        node = prev[node]
    return dist[dst], path[::-1]

خوارزمية BFS ثنائية الاتجاه للرسوم البيانية الكبيرة غير الموزونة

بالنسبة إلى الرسوم البيانية الكبيرة غير الموزونة التي تحتاج فيها إلى زوج واحد فقط من المصدر والوجهة، يمكن أن تكون خوارزمية BFS ثنائية الاتجاه أسرع بكثير من BFS التقليدية. فهي تشغّل BFS من المصدر ومن الوجهة في الوقت نفسه، وتتوقف عندما تلتقي جبهتا البحث. ويكون تسارعها العملي ملحوظاً لأن كل جبهة لا تحتاج إلا إلى استكشاف نصف عمق الرسم البياني — مما يقلل عدد العقد المستكشفة من O(b^d) إلى O(2 × b^(d/2))، حيث b هو عامل التفرع.

from collections import deque

def bidir_bfs(graph, src, dst):
    if src == dst: return 0
    
    front_q = deque([src]); front_visited = {src: 0}
    back_q = deque([dst]);  back_visited = {dst: 0}
    
    def expand(queue, visited, other_visited):
        node = queue.popleft()
        for nxt in graph[node]:
            if nxt not in visited:
                visited[nxt] = visited[node] + 1
                queue.append(nxt)
                if nxt in other_visited:
                    return visited[nxt] + other_visited[nxt]
        return -1
    
    while front_q or back_q:
        res = expand(front_q, front_visited, back_visited)
        if res != -1: return res
        res = expand(back_q, back_visited, front_visited)
        if res != -1: return res
    return -1

متى تختار كل خوارزمية

دليل اتخاذ القرار: رسم بياني غير موزون، وزوج واحد → BFS أو BFS ثنائية الاتجاه. موزون، بأوزان غير سالبة، ومصدر واحد → Dijkstra. موزون، وقد يحتوي على أوزان سالبة، ومصدر واحد → Bellman-Ford. جميع الأزواج → Floyd-Warshall (عندما تكون V صغيرة) أو V × Dijkstra (للرسوم البيانية المتناثرة). عدد قفزات مقيّد → Bellman-Ford معدّلة بتمريرات محدودة. إن توضيح هذا المنطق لاتخاذ القرار بصوت عالٍ في المقابلات يبرهن على نضجك الخوارزمي.

العثور على المدينة ذات أقل عدد من الجيران القابلة للوصول (LeetCode 1334)

معطى مدن ومسارات موزونة وdistanceThreshold، أوجد المدينة التي يمكن الوصول إليها من أقل عدد من المدن الأخرى ضمن هذا الحد، مع تفضيل مؤشر المدينة الأكبر عند التعادل. الحل: احسب أقصر المسارات بين جميع الأزواج باستخدام Floyd-Warshall، ثم احسب لكل مدينة عدد المدن الأخرى التي يمكن الوصول إليها ضمن الحد. أعد المدينة ذات أقل عدد (وعند التعادل: المؤشر الأكبر).

def findTheCity(n, edges, distanceThreshold):
    INF = float('inf')
    dist = [[INF]*n for _ in range(n)]
    for i in range(n): dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = dist[v][u] = w
    for k in range(n):
        for i in range(n):
            for j in range(n):
                dist[i][j] = min(dist[i][j], dist[i][k]+dist[k][j])
    
    best_city, best_count = -1, n
    for city in range(n):
        count = sum(1 for j in range(n) if j != city and dist[city][j] <= distanceThreshold)
        if count <= best_count:
            best_count = count
            best_city = city
    return best_city

print(findTheCity(4,[[0,1,3],[1,2,1],[1,3,4],[2,3,1]],4))  # 3

مسار في رسم بياني موجّه لا دورات فيه وموزون

في الرسم البياني الموجّه غير الدوري (DAG)، يمكن إيجاد أقصر المسارات (أو أطولها) باستخدام الترتيب الطوبولوجي + إرخاء الحواف بتعقيد O(V+E)، وهو أسرع من Dijkstra. عالج العقد وفق الترتيب الطوبولوجي؛ وعند معالجة العقدة u، أرخِ جميع الحواف الخارجة منها. لإيجاد أطول المسارات (وهو مفيد في جدولة المشاريع / المسار الحرج)، اعكس الأوزان أو غيّر min إلى max.

from collections import deque

def dag_shortest_path(V, edges, source):
    graph = [[] for _ in range(V)]
    in_degree = [0] * V
    for u, v, w in edges:
        graph[u].append((v, w))
        in_degree[v] += 1
    # Topological sort (Kahn's)
    queue = deque(i for i in range(V) if in_degree[i] == 0)
    topo = []
    while queue:
        node = queue.popleft(); topo.append(node)
        for nxt, _ in graph[node]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0: queue.append(nxt)
    # Relax in topological order
    dist = [float('inf')] * V
    dist[source] = 0
    for u in topo:
        if dist[u] != float('inf'):
            for v, w in graph[u]:
                dist[v] = min(dist[v], dist[u] + w)
    return dist

أقصر مسار في مصفوفة تحتوي على عوائق

من الأنماط الشائعة في المقابلات: إيجاد أقصر مسار في شبكة ثنائية الأبعاد من الزاوية العلوية اليسرى إلى الزاوية السفلية اليمنى، حيث يمكن أن تكون بعض الخلايا محجوبة. هذه مسألة BFS غير موزونة (تكلفة كل خطوة هي 1). استخدم BFS مع الحركة في الاتجاهات الأربعة، وعلّم الخلايا بأنها زِيرت عند إدراجها في الطابور (وليس عند إخراجها منه) لتجنب زيارتها مجدداً. إذا كان يمكن المرور عبر العوائق (مع وجود تكلفة)، فاستخدم Dijkstra على الشبكة الثنائية الأبعاد باعتبارها رسماً بيانياً موزوناً.

from collections import deque

def shortest_path_binary_matrix(grid):
    n = len(grid)
    if grid[0][0] == 1 or grid[n-1][n-1] == 1:
        return -1
    queue = deque([(0, 0, 1)])  # (row, col, distance)
    visited = {(0, 0)}
    dirs = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]
    while queue:
        r, c, d = queue.popleft()
        if r == n-1 and c == n-1:
            return d
        for dr, dc in dirs:
            nr, nc = r+dr, c+dc
            if 0<=nr<n and 0<=nc<n and grid[nr][nc]==0 and (nr,nc) not in visited:
                visited.add((nr,nc))
                queue.append((nr, nc, d+1))
    return -1

print(shortest_path_binary_matrix([[0,0,0],[1,1,0],[1,1,0]]))  # 4

خوارزمية BFS متعددة المصادر

عند وجود عدة نقاط بداية (مثل عدة «بوابات» في شبكة أو عدة مصادر في خريطة)، شغّل BFS متعددة المصادر: أدرج جميع المصادر في الطابور مع مسافة 0 في الوقت نفسه. تحسب هذه الخوارزمية أقصر مسافة من أقرب مصدر إلى كل خلية في تمريرة BFS واحدة. وتجنب هذه التقنية تشغيل BFS من كل مصدر على حدة، ويكون تعقيدها الإجمالي O(V+E).

مراجعة اختيار الخوارزمية

شجرة قرار موجزة: مصدر واحد، أوزان غير سالبة → Dijkstra O((V+E) log V). مصدر واحد، أوزان سالبة → Bellman-Ford O(VE). جميع الأزواج، وV صغيرة → Floyd-Warshall O(V³). DAG، وأي أوزان → الترتيب الطوبولوجي + الإرخاء O(V+E). غير موزون → BFS O(V+E). مسارات الشبكة → BFS (غير موزونة) أو Dijkstra مع كومة (موزونة). احفظ هذا الجدول — فهو يجيب عن أسئلة المتابعة في أي مقابلة حول أقصر المسارات.

العثور على المسار في أسئلة المقابلات

تطلب منك مسائل كثيرة في المقابلات إيجاد المسار الفعلي، وليس التكلفة فقط. احرص دائماً على التوضيح: هل تحتاج إلى المسار أم إلى المسافة فقط؟ إذا كان المسار مطلوباً، فأنشئ قاموس prev منذ البداية. من الأخطاء الشائعة: نسيان تهيئة prev[source] = None كشرط نهائي، والخلط بين ترتيب إعادة البناء (تتبّع المسار عائداً من الوجهة إلى المصدر، ثم اعكسه). تدرّب على إعادة بناء المسارات في أمثلة مكوّنة من 3 إلى 4 عقد قبل تطبيق ذلك على مسائل أكبر.

تحقق سريع

اختبر مدى فهمك لمفاهيم هياكل البيانات والخوارزميات — الاستعداد لمقابلات البرمجة — التي تناولها هذا الدرس.

مراجعة الدرس

تعلمت في هذا الدرس أن: مسألة Network Delay Time تُحل باستخدام max(dist.values()) بعد تشغيل Dijkstra، وأن إعادة بناء المسار تستخدم مصفوفة prev تُحدَّث كلما تحسنت dist[v]، وأن BFS ثنائية الاتجاه يمكن أن تقلل مساحة البحث إلى النصف في أقصر المسارات غير الموزونة بين زوج واحد. سنتناول تالياً ترتيب الرسوم البيانية باستخدام خوارزمية Kahn للترتيب الطوبولوجي.

الأسئلة الشائعة

هل درس «زمن تأخير الشبكة وإعادة بناء المسار» مجاني؟

نعم — نص درس «زمن تأخير الشبكة وإعادة بناء المسار» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «زمن تأخير الشبكة وإعادة بناء المسار»؟

حلّ مسألة network-delay-time باستخدام Dijkstra، وأعد بناء أقصر مسار فعلي باستخدام خريطة الأسلاف، وناقش BFS ثنائي الاتجاه للرسوم البيانية الكبيرة. تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟

لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «زمن تأخير الشبكة وإعادة بناء المسار»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟

نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. خوارزمية Dijkstra مع طابور أولوية
  2. Bellman-Ford والدورات سالبة الوزن
  3. Floyd-Warshall: أقصر المسارات بين جميع الأزواج
  4. زمن تأخير الشبكة وإعادة بناء المسار
← العودة إلى Coding Interview Prep