0Pricing
Coding Interview Prep · บทเรียน

เวลาหน่วงของเครือข่ายและการสร้างเส้นทางกลับคืน

แก้ปัญหาเวลาหน่วงของเครือข่ายด้วย Dijkstra สร้างเส้นทางสั้นที่สุดจริงกลับคืนโดยใช้แผนที่โหนดก่อนหน้า และอภิปราย BFS แบบสองทิศทางสำหรับกราฟขนาดใหญ่

เวลาหน่วงของเครือข่ายและการสร้างเส้นทางกลับคืน เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

ปัญหาเวลาหน่วงของเครือข่าย

เวลาหน่วงของเครือข่าย (LeetCode 743): เมื่อกำหนดเครือข่ายที่มีโหนดจำนวน n โหนดและเส้นเชื่อมแบบมีทิศทางพร้อมน้ำหนักซึ่งแทนเวลาเดินทางของสัญญาณ ให้หาเวลาต่ำสุดที่สัญญาณซึ่งส่งจากโหนด k จะเดินทางไปถึงโหนดทั้งหมด หากมีโหนดใดไปไม่ถึง ให้คืนค่า -1 นี่เป็นการประยุกต์ใช้ไดก์สตราโดยตรง โดยคำตอบคือระยะทางเส้นทางสั้นที่สุดจาก k ไปยังทุกโหนดที่มีค่ามากที่สุด

วิธีแก้ปัญหา: ไดก์สตรา + ค่าสูงสุดของระยะทาง

เรียกใช้ไดก์สตราจากต้นทาง 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 หลังไดก์สตราทำงานเสร็จ ให้ไล่ย้อนจากจุดปลายทางผ่านตัวชี้ 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 สองทิศทาง มีน้ำหนัก ไม่เป็นลบ ต้นทางเดียว → ไดก์สตรา มีน้ำหนักและอาจเป็นลบ ต้นทางเดียว → เบลล์แมน–ฟอร์ด ทุกคู่ → ฟลอยด์–วอร์แชลล์ (เมื่อ V มีขนาดเล็ก) หรือ V × ไดก์สตรา (เมื่อกราฟเบาบาง) จำนวนก้าวมีข้อจำกัด → เบลล์แมน–ฟอร์ดที่ปรับให้มีรอบการประมวลผลจำกัด การอธิบายเหตุผลในการตัดสินใจนี้ออกมาในการสัมภาษณ์งานแสดงให้เห็นถึงความเข้าใจอัลกอริทึมในระดับดี

หาเมืองที่มีเพื่อนบ้านซึ่งไปถึงได้น้อยที่สุด (LeetCode 1334)

เมื่อกำหนดเมืองที่เชื่อมต่อด้วยเส้นทางมีน้ำหนักและ distanceThreshold ให้หาเมืองที่สามารถไปถึงเมืองอื่นได้น้อยที่สุดภายในเกณฑ์ดังกล่าว (หากจำนวนเท่ากัน ให้เลือกดัชนีเมืองที่ใหญ่กว่า) วิธีแก้ปัญหา: คำนวณเส้นทางสั้นที่สุดระหว่างทุกคู่ด้วยฟลอยด์–วอร์แชลล์ จากนั้นนับสำหรับแต่ละเมืองว่ามีเมืองอื่นกี่เมืองที่ไปถึงได้ภายในเกณฑ์ คืนค่าเมืองที่มีจำนวนดังกล่าวน้อยที่สุด (หากเท่ากัน ให้เลือกดัชนีสูงสุด)

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 ที่มีน้ำหนัก

สำหรับกราฟมีทิศทางแบบไม่มีวัฏจักร (DAG) สามารถหาเส้นทางสั้นที่สุด (หรือเส้นทางยาวที่สุด) ได้ด้วยการเรียงลำดับเชิงทอพอโลยี + การปรับปรุงค่าใน O(V+E) ซึ่งเร็วกว่าการใช้ไดก์สตรา ให้ประมวลผลโหนดตามลำดับเชิงทอพอโลยี เมื่อประมวลผลโหนด 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

เส้นทางสั้นที่สุดในเมทริกซ์ที่มีสิ่งกีดขวาง

รูปแบบโจทย์ที่พบบ่อยในการสัมภาษณ์งานคือการหาเส้นทางสั้นที่สุดในตาราง 2 มิติจากมุมซ้ายบนไปยังมุมขวาล่าง โดยบางช่องอาจถูกกีดขวาง นี่คือปัญหา BFS แบบไม่มีน้ำหนัก เพราะแต่ละก้าวมีค่าใช้จ่าย 1 ให้ใช้ BFS ที่เคลื่อนที่ได้สี่ทิศทาง และทำเครื่องหมายช่องว่าเยี่ยมชมแล้วเมื่อใส่ลงคิว (ไม่ใช่เมื่อดึงออกจากคิว) เพื่อป้องกันการเยี่ยมชมซ้ำ หากสามารถเดินผ่านสิ่งกีดขวางได้โดยมีค่าใช้จ่าย ให้ใช้ไดก์สตรากับตาราง 2 มิติ โดยถือว่าตารางนั้นเป็นกราฟที่มีน้ำหนัก

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)

ทบทวนการเลือกอัลกอริทึม

แผนผังการตัดสินใจแบบกระชับ: ต้นทางเดียว น้ำหนักไม่เป็นลบ → ไดก์สตรา O((V+E) log V) ต้นทางเดียว น้ำหนักเป็นลบ → เบลล์แมน–ฟอร์ด O(VE) ทุกคู่ V มีขนาดเล็ก → ฟลอยด์–วอร์แชลล์ O(V³) DAG น้ำหนักใดก็ได้ → การเรียงลำดับเชิงทอพอโลยี + การปรับปรุงค่า O(V+E) ไม่มีน้ำหนัก → BFS O(V+E) เส้นทางในตาราง → BFS (ไม่มีน้ำหนัก) หรือไดก์สตราที่ใช้ฮีป (มีน้ำหนัก) จดจำตารางนี้ไว้ เพราะช่วยตอบคำถามต่อยอดในการสัมภาษณ์งานเกี่ยวกับเส้นทางสั้นที่สุดได้ทุกแบบ

การค้นหาเส้นทางในคำถามสัมภาษณ์งาน

โจทย์สัมภาษณ์งานจำนวนมากต้องการเส้นทางจริง ไม่ใช่เพียงค่าใช้จ่าย ควรถามให้ชัดเจนเสมอว่า: ต้องการเส้นทางหรือเพียงระยะทาง หากต้องการเส้นทาง ให้สร้างพจนานุกรม prev ตั้งแต่เริ่มต้น ข้อผิดพลาดที่พบบ่อยคือการลืมกำหนดค่าเริ่มต้น prev[source] = None เพื่อใช้เป็นเงื่อนไขสิ้นสุด และการสับสนลำดับการสร้างเส้นทางกลับคืน (ไล่ย้อนจากปลายทางไปยังต้นทาง แล้วจึงกลับลำดับ) ควรฝึกสร้างเส้นทางกลับคืนจากตัวอย่างที่มี 3–4 โหนดก่อนนำไปใช้กับปัญหาขนาดใหญ่

ตรวจสอบความเข้าใจ

ทดสอบความเข้าใจแนวคิดโครงสร้างข้อมูลและอัลกอริทึม — การเตรียมตัวสัมภาษณ์การเขียนโปรแกรมจากบทเรียนนี้

ทบทวนบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ว่า เวลาหน่วงของเครือข่ายหาคำตอบได้จาก max(dist.values()) หลังใช้ไดก์สตรา การสร้างเส้นทางกลับคืนใช้อาร์เรย์ prev ซึ่งอัปเดตทุกครั้งที่ dist[v] มีค่าดีขึ้น และ BFS สองทิศทางสามารถลดพื้นที่ค้นหาลงครึ่งหนึ่งสำหรับเส้นทางสั้นที่สุดแบบไม่มีน้ำหนักระหว่างคู่เดียว บทถัดไป เราจะเข้าสู่การจัดลำดับกราฟด้วยอัลกอริทึมคาห์นสำหรับการเรียงลำดับเชิงทอพอโลยี

คำถามที่พบบ่อย

บทเรียน “เวลาหน่วงของเครือข่ายและการสร้างเส้นทางกลับคืน” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เวลาหน่วงของเครือข่ายและการสร้างเส้นทางกลับคืน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เวลาหน่วงของเครือข่ายและการสร้างเส้นทางกลับคืน”

แก้ปัญหาเวลาหน่วงของเครือข่ายด้วย Dijkstra สร้างเส้นทางสั้นที่สุดจริงกลับคืนโดยใช้แผนที่โหนดก่อนหน้า และอภิปราย BFS แบบสองทิศทางสำหรับกราฟขนาดใหญ่ คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “เวลาหน่วงของเครือข่ายและการสร้างเส้นทางกลับคืน” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม

ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. อัลกอริทึมของ Dijkstra กับคิวลำดับความสำคัญ
  2. Bellman-Ford และวงจรน้ำหนักลบ
  3. Floyd-Warshall: เส้นทางสั้นที่สุดระหว่างทุกคู่
  4. เวลาหน่วงของเครือข่ายและการสร้างเส้นทางกลับคืน
← กลับไปที่ Coding Interview Prep