logo

Lộ trình

Khóa học

Tài liệu

Mock Interview

Liên hệ

Quay lại
  • Trang chủ

    /

  • Tài liệu

    /

  • Thuật toán Dijkstra: Tìm đường đi ngắn nhất
Tài liệu

Thuật toán Dijkstra: Tìm đường đi ngắn nhất

Ronin Engineer

6 Tháng 11 2025

<p></p><h1 id="1-%C4%91%E1%BA%B7t-v%E1%BA%A5n-%C4%91%E1%BB%81">1. Đặt vấn đề</h1><p>Thuật toán Dijkstra là một trong những thuật toán cổ điển để giải quyết bài toán tìm đường đi ngắn nhất từ một điểm cho trước tới tất cả các điểm còn lại trong đồ thị có trọng số. Trong bài viết này chúng ta cùng tìm hiểu ý tưởng cơ bản của thuật toán Dijkstra.</p><p>Mỗi ngày, khi mở Google Maps để tìm đường đến một nơi nào đó, ta thường thấy ứng dụng chỉ trong vài giây đã đề xuất tuyến đường ngắn nhất — đôi khi còn kèm cả thời gian di chuyển dự kiến. Nghe có vẻ đơn giản, nhưng nếu suy nghĩ kỹ, bài toán đằng sau đó không hề nhỏ: <strong>giữa hàng trăm con đường, hàng ngàn ngã rẽ, làm thế nào máy tính biết được con đường nào là ngắn nhất?</strong></p><p>Đó chính là <strong>bài toán đường đi ngắn nhất trong đồ thị có trọng số</strong> — một trong những bài toán kinh điển của khoa học máy tính.Và lời giải nổi tiếng nhất cho nó chính là <strong>thuật toán Dijkstra</strong>, được nhà khoa học người Hà Lan <strong>Edsger W. Dijkstra</strong> giới thiệu vào năm 1959.</p><figure class="kg-card kg-image-card"><img src="https://roninhub.com/content/images/2025/11/data-src-image-ed52c783-bcbf-4e6f-af30-bb33e66f9c67.jpeg" class="kg-image" alt="" loading="lazy" width="1028" height="684" srcset="https://roninhub.com/content/images/size/w600/2025/11/data-src-image-ed52c783-bcbf-4e6f-af30-bb33e66f9c67.jpeg 600w, https://roninhub.com/content/images/size/w1000/2025/11/data-src-image-ed52c783-bcbf-4e6f-af30-bb33e66f9c67.jpeg 1000w, https://roninhub.com/content/images/2025/11/data-src-image-ed52c783-bcbf-4e6f-af30-bb33e66f9c67.jpeg 1028w" sizes="(min-width: 720px) 720px"></figure><p>2. Graph</p><p>Trước khi đi sâu vào cách hoạt động của Dijkstra, chúng ta cần hiểu nền tảng mà nó dựa vào — <strong>lý thuyết đồ thị</strong>. Bởi vì bản chất của bài toán tìm đường ngắn nhất không chỉ là phép tính, mà là việc di chuyển giữa các “<strong>điểm</strong>” được kết nối với nhau bằng các “<strong>đường</strong>”. Trong ngôn ngữ toán học, tập hợp các điểm và đường nối đó chính là đồ thị. Và thuật toán Dijkstra áp dụng cho các bài toán có <strong>trọng số không âm</strong>.</p><p>Hãy tưởng tượng bạn đang ở Hà Nội và muốn tìm đường đến Đà Nẵng. Trên bản đồ, <strong>mỗi thành phố là một điểm, và các tuyến đường là những đoạn nối giữa chúng</strong>. Mỗi tuyến có độ dài hoặc thời gian di chuyển khác nhau — chính là “trọng số” của các cạnh trong một đồ thị có trọng số. Đó chính là mô hình mà thuật toán Dijkstra sử dụng để tính toán đường đi ngắn nhất.</p><h1 id="3-%C3%BD-t%C6%B0%E1%BB%9Fng-thu%E1%BA%ADt-to%C3%A1n">3. Ý tưởng thuật toán</h1><p>Dijkstra hoạt động dựa trên hai nguyên tắc đơn giản:</p><ol><li><strong>Greedy:</strong> luôn chọn đỉnh có khoảng cách tạm thời nhỏ nhất tính từ điểm xuất phát.</li><li><strong>Relaxation:</strong> nếu tìm thấy một con đường ngắn hơn đến đỉnh kề, thì cập nhật lại khoảng cách của nó.</li></ol><h2 id="31-c%C3%A1c-thu%E1%BB%99c-t%C3%ADnh-trong-thu%E1%BA%ADt-to%C3%A1n-dijkstra">3.1. Các thuộc tính trong thuật toán Dijkstra</h2><p>Trong thuật toán Dijkstra, mỗi <strong>đỉnh (vertex)</strong> trong đồ thị sẽ được gắn với hai thuộc tính quan trọng:</p><h3 id="311-visited">3.1.1. Visited</h3><ul><li>Thuộc tính này cho biết liệu đỉnh đó đã được xử lý hay chưa.</li><li>Ta sử dụng nó để tránh việc quay lại thăm một đỉnh đã hoàn tất, bởi khi một đỉnh được đánh dấu “đã thăm”, nghĩa là ta <strong>đã tìm ra đường đi ngắn nhất đến nó rồi</strong> — không cần kiểm tra lại nữa.</li></ul><h3 id="312-%E2%80%9C%C4%91%E1%BB%99-d%C3%A0i-%C4%91%C6%B0%E1%BB%9Dng-%C4%91i%E2%80%9D-path">3.1.2. “Độ dài đường đi” (Path)</h3><ul><li>Thuộc tính này lưu <strong>khoảng cách ngắn nhất hiện tại</strong> từ điểm xuất phát đến đỉnh đó.Ban đầu, tất cả các đỉnh đều có giá trị này là <strong>vô cùng lớn (∞)</strong>, ngoại trừ <strong>đỉnh nguồn</strong> – được gán bằng <strong>0</strong> vì ta bắt đầu từ chính nó.</li><li>Giá trị này sẽ liên tục được <strong>cập nhật (relax)</strong> mỗi khi ta tìm thấy một con đường ngắn hơn đi qua các đỉnh kề.</li></ul><h2 id="32-quy-tr%C3%ACnh-c%E1%BA%ADp-nh%E1%BA%ADt-relaxation">3.2. Quy trình cập nhật (Relaxation)</h2><p>Sau khi chọn đỉnh nguồn và đánh dấu nó là “<strong>đã thăm</strong>”, ta sẽ duyệt qua <strong>tất cả các đỉnh kề</strong> của nó và thử <strong>cập nhật lại đường đi ngắn nhất</strong>.</p><p>Cụ thể, giả sử:</p><ul><li><strong>p[v]</strong> là khoảng cách hiện tại đến đỉnh <strong>v</strong></li><li><strong>p[n]</strong> là khoảng cách đến đỉnh <strong>n</strong> (đỉnh ta vừa xử lý)</li><li><strong>w</strong> là trọng số của cạnh nối giữa <strong>n</strong> và <strong>v</strong></li></ul><p>Khi đó, ta cập nhật:</p><p><strong>p[v] = min(p[v], p[n] + w)</strong></p><p>Nếu tổng đường đi <strong>p[n] + w</strong> nhỏ hơn giá trị cũ <strong>p[v]</strong>, nghĩa là ta vừa tìm được <strong>một con đường tốt hơn</strong> để đến <strong>v</strong>.Đây chính là cốt lõi của thuật toán Dijkstra – <strong>liên tục thư giãn (relax)</strong> các cạnh để dần dần rút ngắn mọi khoảng cách trong đồ thị.</p><h2 id="33-thu%E1%BA%ADt-to%C3%A1n">3.3. Thuật toán</h2><p>Step 1: Đánh dấu đỉnh nguồn với khoảng cách hiện tại bằng <strong>0</strong>, các đỉnh còn lại bằng <strong>vô cùng (∞)</strong>.</p><p>Step 2: Chọn đỉnh <strong>chưa được thăm</strong> có <strong>khoảng cách hiện tại nhỏ nhất</strong>, gọi là <strong>C</strong>.</p><p>Step 3: Với mỗi đỉnh <strong>N</strong> kề với <strong>C</strong>, cộng khoảng cách hiện tại của <strong>C</strong> với trọng số của cạnh nối <strong>C–N</strong>. Nếu giá trị này nhỏ hơn khoảng cách hiện tại của <strong>N</strong>, thì cập nhật lại khoảng cách của <strong>N</strong> bằng giá trị mới.</p><p>Step 4: Đánh dấu <strong>C</strong> là <strong>đã thăm</strong>.</p><p>Step 5: Quay lại step2 nếu vẫn còn đỉnh chưa được thăm.</p><h1 id="4-%E1%BB%A9ng-d%E1%BB%A5ng-trong-t%C3%ACm-%C4%91%C6%B0%E1%BB%9Dng-%C4%91i-ng%E1%BA%AFn-nh%E1%BA%A5t-tr%C3%AAn-map">4. Ứng dụng trong tìm đường đi ngắn nhất trên map</h1><p>Trong thực tế có thể áp dụng dijkstra tìm đường đi ngắn nhất trên google map hay cho các tính năng như tìm đường đi ngắn nhất trên các app giao hàng như shopee…</p><p>Cho đơn giản, ở đây xét 1 ví dụ trực quan tìm đường đi ngắn nhất từ điểm A đến F, với A là vị trí cửa hàng hiện tại, F là điểm cần giao hàng tới, và các điểm còn lại là các nút giao thông (ngã ba, ngã tư, điểm trung gian…). Khoảng cách giữa các nút giao thông là những con đường.</p><p> </p><figure class="kg-card kg-image-card"><img src="https://roninhub.com/content/images/2025/11/data-src-image-e8229dff-161e-4eb5-9c0a-2dd0bbbaac99.png" class="kg-image" alt="" loading="lazy" width="682" height="370" srcset="https://roninhub.com/content/images/size/w600/2025/11/data-src-image-e8229dff-161e-4eb5-9c0a-2dd0bbbaac99.png 600w, https://roninhub.com/content/images/2025/11/data-src-image-e8229dff-161e-4eb5-9c0a-2dd0bbbaac99.png 682w"></figure><p><strong>Step 1:</strong>&nbsp;</p><p><strong>unvisited_node</strong>[A, B, C, D, E, F]</p><p><strong>Step 2:</strong></p><p>A-&gt;B = 4&nbsp; &nbsp; &nbsp; &nbsp; A-&gt;C = 5</p><p><strong>unvisited_node</strong>[ B, C, D, E, F]</p><p><strong>Step 3:</strong></p><p>B-&gt;C= 11 &nbsp; B-&gt;E= 7&nbsp; B-&gt;D= 9</p><p>=&gt; (A-&gt;B-&gt; C = 14) <strong>&gt;</strong> (A-&gt;C) = 5 -&gt; not update C</p><p>A-&gt;B-&gt;E = 11</p><p>A-&gt;B-&gt;D = 13</p><p><strong>&nbsp;&nbsp;</strong> <strong>unvisited_node</strong>[C, D, E, F]</p><p><strong>Step 4:</strong></p><p>C-&gt;E: 3 &nbsp; (<strong>B</strong> is visited, so C-&gt; B not check)</p><p>=&gt; (A-&gt;C-&gt; E =8) <strong>&lt;</strong> (A-&gt;B-&gt;E) = 11 -&gt; update E = 8</p><p><strong>unvisited_node</strong>[D, E, F]</p><p><strong>Step 5:</strong></p><p>D-&gt;E = 13&nbsp; &nbsp; D-&gt;F = 2</p><p>=&gt; A-&gt;B-&gt;D-&gt;F = 15</p><p>(A-&gt;B-&gt;D-&gt;E = 26) &gt; (A-&gt;C-&gt;E = 8) =&gt; not update E</p><p><strong>unvisited_node</strong>[E, F]</p><p><strong>Step 6:</strong></p><p>E-&gt;F = 6 (các node khác đều visited rồi nên không check lại)</p><p>=&gt; (A-&gt;C-&gt;E-&gt;F = 14)&nbsp; <strong>&lt;</strong>&nbsp; (A-&gt;B-&gt;D-&gt;F = 15) =&gt; update F</p><p><strong>unvisited_node</strong>[ F]</p><p>⇒ Shortest distance from A-&gt;F = <strong>14</strong></p><p> </p><figure class="kg-card kg-image-card"><img src="https://roninhub.com/content/images/2025/11/data-src-image-c22c77ef-0d5f-49d2-866d-5e43047bb616.png" class="kg-image" alt="" loading="lazy" width="430" height="230"></figure><p><strong>Code Java</strong></p><pre><code>import java.util.*; public class Main { static final int V = 6; static final int INF = Integer.MAX_VALUE; // Tìm đỉnh có khoảng cách nhỏ nhất trong số các đỉnh chưa được xử lý static int minDist(int[] dist, boolean[] sptSet) { int min = INF, minIndex = -1; for (int v = 0; v &lt; V; v++) { if (!sptSet[v] &amp;&amp; dist[v] &lt;= min) { min = dist[v]; minIndex = v; } } return minIndex; } // In kết quả static void printSolution(int[] distance) { System.out.println("Vertex\tDistance from Source"); for (int i = 0; i &lt; V; i++) System.out.println((char) (i + 65) + "\t\t" + distance[i]); } // Dijkstra static void dijkstra(int[][] graph, int src) { int[] distance = new int[V]; boolean[] sptSet = new boolean[V]; Arrays.fill(distance, INF); Arrays.fill(sptSet, false); distance[src] = 0; for (int count = 0; count &lt; V - 1; count++) { int u = minDist(distance, sptSet); if (u == -1) break; // phòng trường hợp không còn đỉnh hợp lệ sptSet[u] = true; for (int v = 0; v &lt; V; v++) { if (!sptSet[v] &amp;&amp; graph[u][v] != 0 &amp;&amp; distance[u] != INF &amp;&amp; distance[u] + graph[u][v] &lt; distance[v]) { distance[v] = distance[u] + graph[u][v]; } } } printSolution(distance); } public static void main(String[] args) { // Ma trận kề biểu diễn đồ thị // graph[i][j] = khoảng cách giữa nút giao thông i và j (0 nếu không có đường) int[][] graph = { {0, 4, 5, 0, 0, 0}, {4, 0, 11, 9, 7, 0}, {5, 11, 0, 0, 3, 0}, {0, 9, 0, 0, 13, 2}, {0, 7, 3, 13, 0, 6}, {0, 0, 0, 2, 6, 0} }; int source = 0; dijkstra(graph, source); } </code></pre><h1 id="5-%C4%91%E1%BB%99-ph%E1%BB%A9c-t%E1%BA%A1p-thu%E1%BA%ADt-to%C3%A1n">5. Độ phức tạp thuật toán</h1><h2 id="51-%C4%91%E1%BB%99-ph%E1%BB%A9c-t%E1%BA%A1p-c%E1%BB%A7a-thu%E1%BA%ADt-to%C3%A1n-dijkstra-khi-s%E1%BB%AD-d%E1%BB%A5ng-ma-tr%E1%BA%ADn-k%E1%BB%81-%C4%91%E1%BB%83-bi%E1%BB%83u-di%E1%BB%85n-%C4%91%E1%BB%93-th%E1%BB%8B">5.1. Độ phức tạp của thuật toán Dijkstra khi sử dụng ma trận kề để biểu diễn đồ thị</h2><h3 id="511-time-complexity">5.1.1. Time complexity</h3><p>Giả sử đồ thị có <strong>V</strong> đỉnh.<strong>Độ phức tạp thời gian</strong> của thuật toán Dijkstra là <strong>O(V²)</strong>.Giải thích như sau:</p><ul><li>Trước hết, ta cần tìm đỉnh <strong>chưa được thăm</strong> có <strong>độ dài đường đi nhỏ nhất</strong>.Việc này tốn <strong>O(V)</strong> thời gian vì phải duyệt qua tất cả các đỉnh.</li><li>Với mỗi đỉnh vừa chọn, ta cần <strong>thực hiện phép “thư giãn” (relaxation)</strong> cho tất cả các đỉnh kề — tức là cập nhật lại giá trị đường đi của chúng nếu tìm được đường ngắn hơn. Mỗi lần thư giãn một đỉnh kề chỉ tốn <strong>O(1)</strong> (thời gian hằng số).</li><li>Mỗi đỉnh có thể kề tối đa với <strong>V−1 đỉnh khác</strong>, nên việc thư giãn toàn bộ hàng xóm của một đỉnh tốn <strong>O(V)</strong>.</li></ul><p>Từ đó ta có:</p><ul><li>Thời gian để duyệt tất cả các đỉnh: <strong>O(V)</strong></li><li>Thời gian để xử lý (thư giãn) mỗi đỉnh: <strong>O(V)</strong> ⇒ Tổng thời gian: <strong>O(V × V) = O(V²)</strong></li></ul><h3 id="512-%C4%91%E1%BB%99-ph%E1%BB%A9c-t%E1%BA%A1p-b%E1%BB%99-nh%E1%BB%9B">5.1.2. Độ phức tạp bộ nhớ</h3><p>Vì dùng ma trận kề kích thước V×V để lưu đồ thị, nên bộ nhớ cần là O(V²).&nbsp; Ngoài ra, thuật toán còn cần thêm một vài mảng kích thước V (ví dụ như <strong>distance[], visited[]</strong>), nhưng tổng thể độ phức tạp bộ nhớ vẫn là O(V²).</p><h2 id="52-khi-d%C3%B9ng-danh-s%C3%A1ch-k%E1%BB%81-h%C3%A0ng-%C4%91%E1%BB%A3i-%C6%B0u-ti%C3%AAn-min-heap">5.2. Khi dùng danh sách kề + hàng đợi ưu tiên (Min-Heap)</h2><p>Nếu thay vì ma trận kề, ta dùng danh sách kề kết hợp Min-Heap để lưu các đỉnh chưa thăm, ta có thể giảm độ phức tạp xuống còn O((V + E) log V), với:</p><ul><li>E là số cạnh,</li><li>V là số đỉnh.</li></ul><p>Trong cách này:</p><ul><li>Việc duyệt qua các đỉnh có độ phức tạp O(V + E),</li><li>Việc cập nhật (thư giãn) qua Min-Heap tốn O(log V) cho mỗi thao tác.⇒ Tổng thời gian: O((V + E) log V)</li></ul><p>Độ phức tạp bộ nhớ trong trường hợp này cũng được cải thiện còn O(V), vì danh sách kề và Min-Heap đều chiếm không gian tỉ lệ với số đỉnh.</p><h1 id="6-k%E1%BA%BFt-lu%E1%BA%ADn">6. Kết luận</h1><p>Qua bài viết này có thể thấy được sức mạnh của Dijkstra. Đây là một trong những thuật toán cơ bản và quan trọng nhất trong lĩnh vực lý thuyết đồ thị. Nó giúp tìm đường đi ngắn nhất từ một điểm xuất phát đến các điểm khác trong đồ thị có trọng số không âm. Với độ phức tạp O((V + E)logV) khi dùng hàng đợi ưu tiên, Dijkstra vừa hiệu quả vừa dễ triển khai. Nhờ đó, thuật toán này được ứng dụng rộng rãi trong GPS, định tuyến mạng và nhiều hệ thống tối ưu đường đi khác.</p><p>Thank you for your attention!</p><p><strong>Refer:</strong><u> </u><a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm?ref=roninhub.com"><u>https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm</u></a></p><p></p>

1. Đặt vấn đề

Thuật toán Dijkstra là một trong những thuật toán cổ điển để giải quyết bài toán tìm đường đi ngắn nhất từ một điểm cho trước tới tất cả các điểm còn lại trong đồ thị có trọng số. Trong bài viết này chúng ta cùng tìm hiểu ý tưởng cơ bản của thuật toán Dijkstra.

Mỗi ngày, khi mở Google Maps để tìm đường đến một nơi nào đó, ta thường thấy ứng dụng chỉ trong vài giây đã đề xuất tuyến đường ngắn nhất — đôi khi còn kèm cả thời gian di chuyển dự kiến. Nghe có vẻ đơn giản, nhưng nếu suy nghĩ kỹ, bài toán đằng sau đó không hề nhỏ: giữa hàng trăm con đường, hàng ngàn ngã rẽ, làm thế nào máy tính biết được con đường nào là ngắn nhất?

Đó chính là bài toán đường đi ngắn nhất trong đồ thị có trọng số — một trong những bài toán kinh điển của khoa học máy tính.Và lời giải nổi tiếng nhất cho nó chính là thuật toán Dijkstra, được nhà khoa học người Hà Lan Edsger W. Dijkstra giới thiệu vào năm 1959.

2. Graph

Trước khi đi sâu vào cách hoạt động của Dijkstra, chúng ta cần hiểu nền tảng mà nó dựa vào — lý thuyết đồ thị. Bởi vì bản chất của bài toán tìm đường ngắn nhất không chỉ là phép tính, mà là việc di chuyển giữa các “điểm” được kết nối với nhau bằng các “đường”. Trong ngôn ngữ toán học, tập hợp các điểm và đường nối đó chính là đồ thị. Và thuật toán Dijkstra áp dụng cho các bài toán có trọng số không âm.

Hãy tưởng tượng bạn đang ở Hà Nội và muốn tìm đường đến Đà Nẵng. Trên bản đồ, mỗi thành phố là một điểm, và các tuyến đường là những đoạn nối giữa chúng. Mỗi tuyến có độ dài hoặc thời gian di chuyển khác nhau — chính là “trọng số” của các cạnh trong một đồ thị có trọng số. Đó chính là mô hình mà thuật toán Dijkstra sử dụng để tính toán đường đi ngắn nhất.

3. Ý tưởng thuật toán

Dijkstra hoạt động dựa trên hai nguyên tắc đơn giản:

  1. Greedy: luôn chọn đỉnh có khoảng cách tạm thời nhỏ nhất tính từ điểm xuất phát.
  2. Relaxation: nếu tìm thấy một con đường ngắn hơn đến đỉnh kề, thì cập nhật lại khoảng cách của nó.

3.1. Các thuộc tính trong thuật toán Dijkstra

Trong thuật toán Dijkstra, mỗi đỉnh (vertex) trong đồ thị sẽ được gắn với hai thuộc tính quan trọng:

3.1.1. Visited

  • Thuộc tính này cho biết liệu đỉnh đó đã được xử lý hay chưa.
  • Ta sử dụng nó để tránh việc quay lại thăm một đỉnh đã hoàn tất, bởi khi một đỉnh được đánh dấu “đã thăm”, nghĩa là ta đã tìm ra đường đi ngắn nhất đến nó rồi — không cần kiểm tra lại nữa.

3.1.2. “Độ dài đường đi” (Path)

  • Thuộc tính này lưu khoảng cách ngắn nhất hiện tại từ điểm xuất phát đến đỉnh đó.Ban đầu, tất cả các đỉnh đều có giá trị này là vô cùng lớn (∞), ngoại trừ đỉnh nguồn – được gán bằng 0 vì ta bắt đầu từ chính nó.
  • Giá trị này sẽ liên tục được cập nhật (relax) mỗi khi ta tìm thấy một con đường ngắn hơn đi qua các đỉnh kề.

3.2. Quy trình cập nhật (Relaxation)

Sau khi chọn đỉnh nguồn và đánh dấu nó là “đã thăm”, ta sẽ duyệt qua tất cả các đỉnh kề của nó và thử cập nhật lại đường đi ngắn nhất.

Cụ thể, giả sử:

  • p[v] là khoảng cách hiện tại đến đỉnh v
  • p[n] là khoảng cách đến đỉnh n (đỉnh ta vừa xử lý)
  • w là trọng số của cạnh nối giữa n và v

Khi đó, ta cập nhật:

p[v] = min(p[v], p[n] + w)

Nếu tổng đường đi p[n] + w nhỏ hơn giá trị cũ p[v], nghĩa là ta vừa tìm được một con đường tốt hơn để đến v.Đây chính là cốt lõi của thuật toán Dijkstra – liên tục thư giãn (relax) các cạnh để dần dần rút ngắn mọi khoảng cách trong đồ thị.

3.3. Thuật toán

Step 1: Đánh dấu đỉnh nguồn với khoảng cách hiện tại bằng 0, các đỉnh còn lại bằng vô cùng (∞).

Step 2: Chọn đỉnh chưa được thăm có khoảng cách hiện tại nhỏ nhất, gọi là C.

Step 3: Với mỗi đỉnh N kề với C, cộng khoảng cách hiện tại của C với trọng số của cạnh nối C–N. Nếu giá trị này nhỏ hơn khoảng cách hiện tại của N, thì cập nhật lại khoảng cách của N bằng giá trị mới.

Step 4: Đánh dấu C là đã thăm.

Step 5: Quay lại step2 nếu vẫn còn đỉnh chưa được thăm.

4. Ứng dụng trong tìm đường đi ngắn nhất trên map

Trong thực tế có thể áp dụng dijkstra tìm đường đi ngắn nhất trên google map hay cho các tính năng như tìm đường đi ngắn nhất trên các app giao hàng như shopee…

Cho đơn giản, ở đây xét 1 ví dụ trực quan tìm đường đi ngắn nhất từ điểm A đến F, với A là vị trí cửa hàng hiện tại, F là điểm cần giao hàng tới, và các điểm còn lại là các nút giao thông (ngã ba, ngã tư, điểm trung gian…). Khoảng cách giữa các nút giao thông là những con đường.

Step 1: 

unvisited_node[A, B, C, D, E, F]

Step 2:

A->B = 4        A->C = 5

unvisited_node[ B, C, D, E, F]

Step 3:

B->C= 11   B->E= 7  B->D= 9

=> (A->B-> C = 14) > (A->C) = 5 -> not update C

A->B->E = 11

A->B->D = 13

   unvisited_node[C, D, E, F]

Step 4:

C->E: 3   (B is visited, so C-> B not check)

=> (A->C-> E =8) < (A->B->E) = 11 -> update E = 8

unvisited_node[D, E, F]

Step 5:

D->E = 13    D->F = 2

=> A->B->D->F = 15

(A->B->D->E = 26) > (A->C->E = 8) => not update E

unvisited_node[E, F]

Step 6:

E->F = 6 (các node khác đều visited rồi nên không check lại)

=> (A->C->E->F = 14)  <  (A->B->D->F = 15) => update F

unvisited_node[ F]

⇒ Shortest distance from A->F = 14

Code Java

import java.util.*;  
  
public class Main {  
    static final int V = 6;  
    static final int INF = Integer.MAX_VALUE;  
    // Tìm đỉnh có khoảng cách nhỏ nhất trong số các đỉnh chưa được xử lý
    static int minDist(int[] dist, boolean[] sptSet) {  
        int min = INF, minIndex = -1;  
        for (int v = 0; v < V; v++) {  
            if (!sptSet[v] && dist[v] <= min) {  
                min = dist[v];  
                minIndex = v;  
            }  
        }  
        return minIndex;  
    }  
    // In kết quả
    static void printSolution(int[] distance) {  
        System.out.println("Vertex\tDistance from Source");  
        for (int i = 0; i < V; i++)  
            System.out.println((char) (i + 65) + "\t\t" + distance[i]);  
    }  
   
    // Dijkstra 
    static void dijkstra(int[][] graph, int src) {  
        int[] distance = new int[V];  
        boolean[] sptSet = new boolean[V];  
  
        Arrays.fill(distance, INF);  
        Arrays.fill(sptSet, false);  
        distance[src] = 0;  
  
        for (int count = 0; count < V - 1; count++) {  
            int u = minDist(distance, sptSet);  
            if (u == -1) break; // phòng trường hợp không còn đỉnh hợp lệ  
            sptSet[u] = true;  
  
            for (int v = 0; v < V; v++) {  
                if (!sptSet[v] && graph[u][v] != 0 && distance[u] != INF  
                        && distance[u] + graph[u][v] < distance[v]) {  
                    distance[v] = distance[u] + graph[u][v];  
                }  
            }  
        }  
  
        printSolution(distance);  
    }  
    
    public static void main(String[] args) {  
	// Ma trận kề biểu diễn đồ thị
       // graph[i][j] = khoảng cách giữa nút giao thông i và j (0 nếu không có đường)
        int[][] graph = {  
            {0, 4, 5, 0, 0, 0},  
            {4, 0, 11, 9, 7, 0},  
            {5, 11, 0, 0, 3, 0},  
            {0, 9, 0, 0, 13, 2},  
            {0, 7, 3, 13, 0, 6},  
            {0, 0, 0, 2, 6, 0}  
        };  
  
        int source = 0;  
        dijkstra(graph, source);  
    }  

5. Độ phức tạp thuật toán

5.1. Độ phức tạp của thuật toán Dijkstra khi sử dụng ma trận kề để biểu diễn đồ thị

5.1.1. Time complexity

Giả sử đồ thị có V đỉnh.Độ phức tạp thời gian của thuật toán Dijkstra là O(V²).Giải thích như sau:

  • Trước hết, ta cần tìm đỉnh chưa được thăm có độ dài đường đi nhỏ nhất.Việc này tốn O(V) thời gian vì phải duyệt qua tất cả các đỉnh.
  • Với mỗi đỉnh vừa chọn, ta cần thực hiện phép “thư giãn” (relaxation) cho tất cả các đỉnh kề — tức là cập nhật lại giá trị đường đi của chúng nếu tìm được đường ngắn hơn. Mỗi lần thư giãn một đỉnh kề chỉ tốn O(1) (thời gian hằng số).
  • Mỗi đỉnh có thể kề tối đa với V−1 đỉnh khác, nên việc thư giãn toàn bộ hàng xóm của một đỉnh tốn O(V).

Từ đó ta có:

  • Thời gian để duyệt tất cả các đỉnh: O(V)
  • Thời gian để xử lý (thư giãn) mỗi đỉnh: O(V) ⇒ Tổng thời gian: O(V × V) = O(V²)

5.1.2. Độ phức tạp bộ nhớ

Vì dùng ma trận kề kích thước V×V để lưu đồ thị, nên bộ nhớ cần là O(V²).  Ngoài ra, thuật toán còn cần thêm một vài mảng kích thước V (ví dụ như distance[], visited[]), nhưng tổng thể độ phức tạp bộ nhớ vẫn là O(V²).

5.2. Khi dùng danh sách kề + hàng đợi ưu tiên (Min-Heap)

Nếu thay vì ma trận kề, ta dùng danh sách kề kết hợp Min-Heap để lưu các đỉnh chưa thăm, ta có thể giảm độ phức tạp xuống còn O((V + E) log V), với:

  • E là số cạnh,
  • V là số đỉnh.

Trong cách này:

  • Việc duyệt qua các đỉnh có độ phức tạp O(V + E),
  • Việc cập nhật (thư giãn) qua Min-Heap tốn O(log V) cho mỗi thao tác.⇒ Tổng thời gian: O((V + E) log V)

Độ phức tạp bộ nhớ trong trường hợp này cũng được cải thiện còn O(V), vì danh sách kề và Min-Heap đều chiếm không gian tỉ lệ với số đỉnh.

6. Kết luận

Qua bài viết này có thể thấy được sức mạnh của Dijkstra. Đây là một trong những thuật toán cơ bản và quan trọng nhất trong lĩnh vực lý thuyết đồ thị. Nó giúp tìm đường đi ngắn nhất từ một điểm xuất phát đến các điểm khác trong đồ thị có trọng số không âm. Với độ phức tạp O((V + E)logV) khi dùng hàng đợi ưu tiên, Dijkstra vừa hiệu quả vừa dễ triển khai. Nhờ đó, thuật toán này được ứng dụng rộng rãi trong GPS, định tuyến mạng và nhiều hệ thống tối ưu đường đi khác.

Thank you for your attention!

Refer: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm

beginner
dsa
graph
dijkstra
shortestpath
java

Bài viết liên quan

Top 10 Câu Hỏi Phỏng Vấn Về Java Core

Kiến thức nền tảng rất quan trọng! Nó không chỉ giúp ta phát triển nhanh và bền vững, mà còn giúp ta debug được những bug khoai, design những giải pháp mới, sáng tạo.

Những Tiêu Chí Và Vấn Đề Khi Review Code

Code Review là quá trình đánh giá code để đảm bảo code đạt được những tiêu chuẩn về chất lượng và thiết kế.

CORS: Bản Chất, Hạn Chế và Best Practices

By Đức Hiếu Cross-Origin Resource Sharing (CORS) là một cơ chế bảo mật quan trọng trong phát triển web application hiện đại, cho phép kiểm soát việc truy cập tài nguyên giữa các domain khác nhau. 1. Vấn đề mà CORS  giải quyết 1.1. Thời kỳ đầu Web: không có Same-Origin Policy (SOP) Trong những ngày đầu của World Wide Web, các trình duyệt không có bất kỳ hạn chế nào về việc truy cập tài nguyên giữa các domain khác nhau. JavaScript code từ một trang web có thể: * Gửi requests đến mọi server

ACID - A Deep Dive into Transactions

By @wuan580 Trong thế giới khắc nghiệt của các hệ thống dữ liệu, mọi thứ đều có thể xảy ra: * Phần mềm hoặc phần cứng cơ sở dữ liệu có thể bị lỗi bất cứ lúc nào (kể cả giữa thao tác ghi) * Ứng dụng có thể sập giữa chừng, khi một loạt thao tác vẫn chưa hoàn tất. * Sự cố mạng có thể bất ngờ cắt đứt ứng dụng khỏi cơ sở dữ liệu hoặc một nút cơ sở dữ liệu này khỏi nút khác. * Nhiều client có thể ghi đồng thời, vô tình ghi đè lên thay đổi của nhau. * Client có thể đọc dữ liệu vô nghĩa vì nó chỉ

Áp dụng Connection Multiplexing trong ProxySQL để tối ưu kết nối Database

By Đức Hiếu 1. Tổng quan Connection Multiplexing là một trong những tính năng nổi bật nhất của ProxySQL, giúp giải quyết vấn đề bottleneck về kết nối với hệ thống Database. Khác với connection pooling truyền thống, multiplexing cho phép nhiều frontend connections chia sẻ các backend connections thông qua tỷ lệ N:M thay vì 1:1, từ đó giảm đáng kể áp lực lên Database layer. 2. Vấn đề với mô hình Thread-per-Connection của MySQL Trong mô hình truyền thống, MySQL sử dụng thread-per-connection m

Tất cả bài viết
logo

HỘ KINH DOANH LẬP VƯƠNG

Giấy chứng nhận đăng ký doanh nghiệp số: 8656162915-001. Cấp ngày 21/02/2024. Nơi cấp: Sở Kế hoạch và Đầu tư TP. Hà Nội

PHƯƠNG THỨC THANH TOÁN

vnpay

LIÊN HỆ

roninengineer88@gmail.com

0362228388

26 ngõ 156 Hồng Mai, Hai Bà Trưng, Hà Nội

THEO DÕI CHÚNG TÔI

Facebook

Youtube

Tiktok

CHÍNH SÁCH

Chính sách bảo mật

Chính sách thanh toán

Đổi trả/Hoàn tiền

Hướng dẫn thanh toán VNPAY

PHƯƠNG THỨC THANH TOÁN

vnpay

Ronin Engineer 2024