Menu
×

Được chứng nhận

Ghi lại kiến ​​thức của bạn

Đăng nhập Đăng ký

Tạo Tài khoản Example.com.vn miễn phí để cải thiện trải nghiệm học tập của bạn

Người tìm đường và việc học của tôi

Theo dõi tiến độ học tập của bạn tại Example.com.vn và thu thập phần thưởng

Nâng cấp

Trở thành người dùng PLUS và mở khóa các tính năng mạnh mẽ (không có quảng cáo, lưu trữ, hỗ trợ, ..)

Bắt đầu từ đâu

Bạn không chắc chắn muốn bắt đầu từ đâu? Đi theo con đường được hướng dẫn của chúng tôi

Trình chỉnh sửa mã (Dùng thử)

Với trình chỉnh sửa mã trực tuyến của chúng tôi, bạn có thể chỉnh sửa mã và xem kết quả trong trình duyệt của mình

Video

Tìm hiểu những điều cơ bản về HTML qua video hướng dẫn thú vị và hấp dẫn

Mẫu

Chúng tôi đã tạo một loạt mẫu trang web đáp ứng mà bạn có thể sử dụng - miễn phí!

Web hosting

Lưu trữ trang web của riêng bạn và chia sẻ nó với mọi người với Example.com.vn Spaces

Tạo một máy chủ

Tạo máy chủ của riêng bạn bằng Python, PHP, React.js, Node.js, Java, C#, v.v.

Làm thế nào để

Bộ sưu tập lớn các đoạn mã cho HTML, CSS và JavaScript

Khung CSS

Xây dựng các trang web nhanh và phản hồi bằng cách sử dụng khung W3.CSS miễn phí của chúng tôi

Thống kê trình duyệt

Đọc xu hướng dài hạn của việc sử dụng trình duyệt

Tốc độ gõ

Kiểm tra tốc độ đánh máy của bạn

Đào tạo AWS

Tìm hiểu dịch vụ web của Amazon

Bộ chọn màu

Sử dụng công cụ chọn màu của chúng tôi để tìm các màu RGB, HEX và HSL khác nhau. Bánh xe màu hình tròn thể hiện sự chuyển màu trong quang phổ

Trò chơi mã

Trò chơi mã hóa W3Schools! Giúp linh miêu thu thập nón thông Logo Lynx

Đặt mục tiêu

Nhận hành trình học tập được cá nhân hóa dựa trên các kỹ năng và mục tiêu hiện tại của bạn

Bản tin

Tham gia bản tin của chúng tôi và có quyền truy cập vào nội dung độc quyền mỗi tháng

Việc làm

Thuê những tài năng công nghệ hàng đầu. Hợp lý hóa quy trình tuyển dụng của bạn để có đội ngũ phù hợp hoàn hảo

Lớp học

Hãy liên hệ để sử dụng Example.com.vn Plus và các chứng chỉ với tư cách là một tổ chức giáo dục

×
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP CÁCH W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS R TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI GO KOTLIN SASS VUE DSA GEN AI SCIPY AWS AN NINH MẠNG DỮ LIỆU KHOA HỌC

Hướng dẫn DSA

DSA TRANG CHỦ DSA Giới thiệu Thuật toán đơn giản DSA

Mảng

Mảng DSA Sắp xếp bong bóng DSA Sắp xếp lựa chọn DSA Sắp xếp chèn DSA Sắp xếp nhanh DSA Sắp xếp đếm DSA Sắp xếp cơ số DSA Sắp xếp hợp nhất DSA Tìm kiếm tuyến tính DSA Tìm kiếm nhị phân DSA

Danh sách liên kết

Danh sách liên kết DSA Danh sách liên kết DSA trong bộ nhớ Danh sách liên kết DSA Các loại Danh sách liên kết Hoạt động

Ngăn xếp & hàng đợi

Ngăn xếp DSA Hàng đợi DSA

Bảng băm

Bảng băm DSA Bộ hàm băm DSA Bản đồ hàm băm DSA

Cây

Cây DSA Cây nhị phân DSA DSA Traversal đặt hàng trước DSA Traversal theo thứ tự DSA Traversal DSA sau thực hiện mảng DSA Cây tìm kiếm nhị phân DSA Cây AVL DSA

Đồ thị

Đồ thị DSA Thực hiện đồ thị Đồ thị DSA Phát hiện chu trình DSA truyền tải

Con đường ngắn nhất

Đường đi ngắn nhất DSA DSA Bellman-Ford của DSA Dijkstra

Cây bao trùm tối thiểu

Cây khung tối thiểu DSA Prim's DSA Kruskal's

Lưu lượng cực đại

DSA Lưu lượng tối đa DSA Ford-Fulkerson DSA Edmonds-Karp

Độ phức tạp thời gian

Giới thiệu Sắp xếp bong bóng Lựa chọn Sắp xếp Chèn Sắp xếp Sắp xếp nhanh Đếm Sắp xếp Cơ số Sắp xếp Hợp nhất Sắp xếp Tìm kiếm tuyến tính Tìm kiếm nhị phân

Tham chiếu DSA

Thuật toán Euclide DSA Thuật toán tham lam DSA Ghi nhớ DSA DSA Người bán hàng du lịch

Ví dụ về DSA

Ví dụ về DSA Bài tập DSA Câu hỏi DSA Chứng chỉ DSA

Thuật toán DSA Dijkstra

Thuật toán đường đi ngắn nhất của Dijkstra được phát minh vào năm 1956 bởi nhà khoa học máy tính người Hà Lan Edsger W. Dijkstra trong một giờ giải lao uống cà phê kéo dài 20 phút, khi đang đi mua sắm với vợ sắp cưới ở Amsterdam.

Lý do phát minh ra thuật toán này là để thử nghiệm một máy tính mới có tên ARMAC.

Thuật toán Dijkstra

Thuật toán Dijkstra tìm đường đi ngắn nhất từ ​​một đỉnh đến tất cả các đỉnh khác.

Nó làm như vậy bằng cách liên tục chọn đỉnh chưa được thăm gần nhất và tính toán khoảng cách tới tất cả các đỉnh lân cận chưa được thăm.


{{ msgDone }}

Thuật toán Dijkstra thường được coi là thuật toán đơn giản nhất để giải bài toán đường đi ngắn nhất.

Thuật toán Dijkstra được sử dụng để giải các bài toán đường đi ngắn nhất một nguồn cho đường đi có hướng hoặc không có hướng. Nguồn đơn có nghĩa là một đỉnh được chọn làm điểm bắt đầu và thuật toán sẽ tìm đường đi ngắn nhất từ ​​đỉnh đó đến tất cả các đỉnh khác.

Thuật toán Dijkstra không áp dụng được cho đồ thị có cạnh âm. Đối với các đồ thị có cạnh âm, có thể sử dụng thuật toán Bellman-Ford được mô tả ở trang tiếp theo.

Để tìm đường đi ngắn nhất, thuật toán của Dijkstra cần biết đỉnh nào là nguồn, nó cần một cách để đánh dấu các đỉnh là đã ghé thăm và nó cần một cái nhìn tổng quan về khoảng cách ngắn nhất hiện tại tới mỗi đỉnh khi nó di chuyển qua biểu đồ, cập nhật những khoảng cách này khi tìm thấy một khoảng cách ngắn hơn.

Làm thế nào nó hoạt động:

  1. Đặt khoảng cách ban đầu cho tất cả các đỉnh: 0 cho đỉnh nguồn và vô cực cho tất cả các đỉnh còn lại.
  2. Chọn đỉnh chưa được thăm có khoảng cách ngắn nhất tính từ điểm bắt đầu làm đỉnh hiện tại. Vì vậy, thuật toán sẽ luôn bắt đầu với nguồn là đỉnh hiện tại.
  3. Đối với mỗi đỉnh lân cận chưa được thăm của đỉnh hiện tại, hãy tính khoảng cách từ nguồn và cập nhật khoảng cách nếu khoảng cách mới được tính toán thấp hơn.
  4. Bây giờ chúng ta đã hoàn thành đỉnh hiện tại, vì vậy chúng ta đánh dấu nó là đã ghé thăm. Một đỉnh đã ghé thăm sẽ không được kiểm tra lại.
  5. Quay lại bước 2 để chọn một đỉnh hiện tại mới và tiếp tục lặp lại các bước này cho đến khi đi qua tất cả các đỉnh.
  6. Cuối cùng, chúng ta còn lại đường đi ngắn nhất từ ​​đỉnh nguồn đến mọi đỉnh khác trong biểu đồ.

Trong hoạt ảnh ở trên, khi một đỉnh được đánh dấu là đã ghé thăm, đỉnh đó và các cạnh của nó sẽ mờ đi để biểu thị rằng thuật toán của Dijkstra hiện đã được thực hiện với đỉnh đó và sẽ không truy cập lại đỉnh đó nữa.

Lưu ý: Phiên bản cơ bản này của thuật toán Dijkstra cung cấp cho chúng ta giá trị của chi phí đường đi ngắn nhất tới mọi đỉnh, chứ không phải đường đi thực tế là gì. Vì vậy, ví dụ, trong hình ảnh động ở trên, chúng ta nhận được giá trị đường đi ngắn nhất là 10 đến đỉnh F, nhưng thuật toán không cung cấp cho chúng ta đỉnh nào (D->E->C->D->F) tạo nên đường đi ngắn nhất này con đường. Chúng tôi sẽ bổ sung thêm chức năng này ở dưới đây trên trang này.


Mô phỏng Dijkstra chi tiết

Chạy mô phỏng bên dưới để hiểu chi tiết hơn về cách thuật toán Dijkstra chạy trên một biểu đồ cụ thể, tìm khoảng cách ngắn nhất từ ​​đỉnh D.

thông tin F 2 5 5 3 thông tin B thông tin C 5 5 2 2 thông tin MỘT 4 4 4 thông tin E 0 D thông tin G 2 2 5 5 4 4 2 2 6 6 số 8 2

Mô phỏng này cho thấy cách tính khoảng cách từ đỉnh D đến tất cả các đỉnh khác, bằng cách luôn chọn đỉnh tiếp theo là đỉnh chưa được thăm gần nhất tính từ điểm bắt đầu.

Hãy làm theo mô tả từng bước bên dưới để biết tất cả thông tin chi tiết về cách thuật toán Dijkstra tính toán khoảng cách ngắn nhất.


Chạy qua thủ công

Hãy xem xét biểu đồ dưới đây.

F 2 5 3 4 5 2 B C 5 5 2 MỘT 4 4 E D G

Chúng ta muốn tìm đường đi ngắn nhất từ ​​đỉnh nguồn D đến tất cả các đỉnh khác, ví dụ như đường đi ngắn nhất tới C là D->E->C, với trọng số đường đi là 2+4=6.

Để tìm đường đi ngắn nhất, thuật toán Dijkstra sử dụng một mảng có khoảng cách đến tất cả các đỉnh khác và ban đầu đặt các khoảng cách này thành vô hạn hoặc một số rất lớn. Và khoảng cách đến đỉnh mà chúng ta bắt đầu từ (nguồn) được đặt thành 0.

 distances = [inf, inf, inf, 0, inf, inf, inf]
#vertices   [ A ,  B ,  C , D,  E ,  F ,  G ]

Hình ảnh bên dưới hiển thị khoảng cách vô hạn ban đầu đến các đỉnh khác tính từ đỉnh bắt đầu D. Giá trị khoảng cách cho đỉnh D là 0 vì đó là điểm bắt đầu.

thông tin F 2 5 3 4 5 2 thông tin B thông tin C 5 5 2 thông tin MỘT 4 4 thông tin E 0 D thông tin G

Thuật toán Dijkstra sau đó đặt đỉnh D làm đỉnh hiện tại và xem xét khoảng cách đến các đỉnh liền kề. Vì khoảng cách ban đầu tới các đỉnh A và E là vô hạn nên khoảng cách mới tới các đỉnh này được cập nhật theo trọng số của các cạnh. Vì vậy, đỉnh A được thay đổi khoảng cách từ inf thành 4 và đỉnh E được thay đổi khoảng cách thành 2. Như đã đề cập ở trang trước, việc cập nhật các giá trị khoảng cách theo cách này được gọi là 'thư giãn'.

thông tin F 2 5 3 4 5 2 thông tin B thông tin C 5 5 2 4 MỘT 4 4 2 E 0 D thông tin G

Sau khi nới lỏng đỉnh A và E, đỉnh D được coi là đã thăm và sẽ không được thăm nữa.

Đỉnh tiếp theo được chọn làm đỉnh hiện tại phải là đỉnh có khoảng cách ngắn nhất tới đỉnh nguồn (đỉnh D), trong số các đỉnh chưa được thăm trước đó. Do đó, đỉnh E được chọn làm đỉnh hiện tại sau đỉnh D.

thông tin F 2 5 3 4 5 2 thông tin B 6 C 5 5 2 4 MỘT 4 4 2 E 0 D 7 G

Khoảng cách đến tất cả các đỉnh liền kề và chưa được thăm trước đó từ đỉnh E bây giờ phải được tính toán và cập nhật nếu cần.

Khoảng cách tính được từ D đến đỉnh A, qua E, là 2+4=6. Nhưng khoảng cách hiện tại đến đỉnh A đã là 4, thấp hơn nên khoảng cách đến đỉnh A không được cập nhật.

Khoảng cách đến đỉnh C được tính là 2+4=6, nhỏ hơn vô cực, do đó khoảng cách đến đỉnh C được cập nhật.

Tương tự, khoảng cách đến nút G được tính toán và cập nhật thành 2+5=7.

Đỉnh tiếp theo cần thăm là đỉnh A vì nó có khoảng cách đến D ngắn nhất trong số tất cả các đỉnh chưa được thăm.

thông tin F 2 5 3 4 5 2 thông tin B 6 C 5 5 2 4 MỘT 4 4 2 E 0 D 7 G

Khoảng cách được tính đến đỉnh C, qua A, là 4+3=7, cao hơn khoảng cách đã đặt đến đỉnh C, do đó khoảng cách đến đỉnh C không được cập nhật.

Đỉnh A hiện được đánh dấu là đã ghé thăm và đỉnh hiện tại tiếp theo là đỉnh C vì đỉnh đó có khoảng cách thấp nhất đến đỉnh D giữa các đỉnh chưa được thăm còn lại.

11 F 2 5 3 4 5 2 số 8 B 6 C 5 5 2 4 MỘT 4 4 2 E 0 D 7 G

Đỉnh F được cập nhật khoảng cách 6+5=11 và đỉnh B được cập nhật khoảng cách 6+2=8.

Khoảng cách tính toán đến đỉnh G qua đỉnh C là 6+5=11, cao hơn khoảng cách đã đặt là 7, do đó khoảng cách đến đỉnh G không được cập nhật.

Đỉnh C được đánh dấu là đã thăm và đỉnh tiếp theo được thăm là G vì nó có khoảng cách thấp nhất giữa các đỉnh chưa được thăm còn lại.

11 F 2 5 3 4 5 2 số 8 B 6 C 5 5 2 4 MỘT 4 4 2 E 0 D 7 G

Đỉnh F đã có khoảng cách là 11. Khoảng cách này thấp hơn khoảng cách tính toán từ G, là 7+5=12, do đó khoảng cách đến đỉnh F không được cập nhật.

Đỉnh G được đánh dấu là đã thăm và đỉnh B trở thành đỉnh hiện tại vì nó có khoảng cách thấp nhất trong số các đỉnh chưa được thăm còn lại.

10 F 2 5 3 4 5 2 số 8 B 6 C 5 5 2 4 MỘT 4 4 2 E 0 D 7 G

Khoảng cách mới đến F qua B là 8+2=10, vì nó thấp hơn khoảng cách hiện tại của F là 11.

Đỉnh B được đánh dấu là đã thăm và không có gì để kiểm tra đỉnh F cuối cùng chưa được thăm, vì vậy thuật toán Dijkstra đã kết thúc.

Mỗi đỉnh chỉ được thăm một lần và kết quả là khoảng cách thấp nhất từ ​​đỉnh nguồn D đến mọi đỉnh khác trong đồ thị.


Triển khai thuật toán Dijkstra

Để triển khai thuật toán Dijkstra, chúng tôi tạo một lớp Graph . Graph biểu thị đồ thị với các đỉnh và cạnh của nó:

 class Graph:
    def __init__(self, size):
        self.adj_matrix = [[0] * size for _ in range(size)]
        self.size = size
        self.vertex_data = [''] * size

    def add_edge(self, u, v, weight):
        if 0 <= u < self.size and 0 <= v < self.size:
            self.adj_matrix[u][v] = weight
            self.adj_matrix[v][u] = weight  # For undirected graph

    def add_vertex_data(self, vertex, data):
        if 0 <= vertex < self.size:
            self.vertex_data[vertex] = data

Dòng 3: Chúng ta tạo adj_matrix để giữ tất cả các cạnh và trọng số của cạnh. Giá trị ban đầu được đặt thành 0 .

Dòng 4: size là số đỉnh của đồ thị.

Dòng 5: vertex_data chứa tên của tất cả các đỉnh.

Dòng 7-10: Phương thức add_edge được sử dụng để thêm một cạnh từ đỉnh u đến đỉnh v , với weight số cạnh .

Dòng 12-14: Phương thức add_vertex_data được sử dụng để thêm một đỉnh vào biểu đồ. Chỉ mục nơi đỉnh đó thuộc về được đưa ra cùng với đối vertexdata là tên của đỉnh.

Lớp Graph cũng chứa phương thức chạy thuật toán Dijkstra:

    def dijkstra(self, start_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        distances = [float('inf')] * self.size
        distances[start_vertex] = 0
        visited = [False] * self.size

        for _ in range(self.size):
            min_distance = float('inf')
            u = None
            for i in range(self.size):
                if not visited[i] and distances[i] < min_distance:
                    min_distance = distances[i]
                    u = i

            if u is None:
                break

            visited[u] = True

            for v in range(self.size):
                if self.adj_matrix[u][v] != 0 and not visited[v]:
                    alt = distances[u] + self.adj_matrix[u][v]
                    if alt < distances[v]:
                        distances[v] = alt

        return distances

Dòng 18-19: Khoảng cách ban đầu được đặt thành vô cùng cho tất cả các đỉnh trong mảng distances , ngoại trừ đỉnh bắt đầu, trong đó khoảng cách là 0.

Dòng 20: Tất cả các đỉnh ban đầu được đặt thành False để đánh dấu chúng là chưa được truy cập trong mảng visited .

Dòng 23-28: Đã tìm thấy đỉnh hiện tại tiếp theo. Các cạnh đi từ đỉnh này sẽ được kiểm tra để xem liệu có thể tìm thấy khoảng cách ngắn hơn hay không. Đó là đỉnh chưa được thăm với khoảng cách thấp nhất tính từ điểm bắt đầu.

Dòng 30-31: Nếu không tìm thấy đỉnh hiện tại tiếp theo, thuật toán kết thúc. Điều này có nghĩa là tất cả các đỉnh có thể truy cập được từ nguồn đều đã được truy cập.

Dòng 33: Đỉnh hiện tại được đặt là đã ghé thăm trước khi thư giãn các đỉnh liền kề. Điều này hiệu quả hơn vì chúng ta tránh kiểm tra khoảng cách đến đỉnh hiện tại.

Dòng 35-39: Khoảng cách được tính cho các đỉnh liền kề chưa được truy cập và được cập nhật nếu khoảng cách tính toán mới thấp hơn.

Sau khi xác định lớp Graph , các đỉnh và cạnh phải được xác định để khởi tạo biểu đồ cụ thể và mã hoàn chỉnh cho ví dụ về thuật toán Dijkstra này trông như thế này:

Ví dụ

Trăn:

 class Graph:
    def __init__(self, size):
        self.adj_matrix = [[0] * size for _ in range(size)]
        self.size = size
        self.vertex_data = [''] * size

    def add_edge(self, u, v, weight):
        if 0 <= u < self.size and 0 <= v < self.size:
            self.adj_matrix[u][v] = weight
            self.adj_matrix[v][u] = weight  # For undirected graph

    def add_vertex_data(self, vertex, data):
        if 0 <= vertex < self.size:
            self.vertex_data[vertex] = data

    def dijkstra(self, start_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        distances = [float('inf')] * self.size
        distances[start_vertex] = 0
        visited = [False] * self.size

        for _ in range(self.size):
            min_distance = float('inf')
            u = None
            for i in range(self.size):
                if not visited[i] and distances[i] < min_distance:
                    min_distance = distances[i]
                    u = i

            if u is None:
                break

            visited[u] = True

            for v in range(self.size):
                if self.adj_matrix[u][v] != 0 and not visited[v]:
                    alt = distances[u] + self.adj_matrix[u][v]
                    if alt < distances[v]:
                        distances[v] = alt

        return distances

g = Graph(7)

g.add_vertex_data(0, 'A')
g.add_vertex_data(1, 'B')
g.add_vertex_data(2, 'C')
g.add_vertex_data(3, 'D')
g.add_vertex_data(4, 'E')
g.add_vertex_data(5, 'F')
g.add_vertex_data(6, 'G')

g.add_edge(3, 0, 4)  # D - A, weight 5
g.add_edge(3, 4, 2)  # D - E, weight 2
g.add_edge(0, 2, 3)  # A - C, weight 3
g.add_edge(0, 4, 4)  # A - E, weight 4
g.add_edge(4, 2, 4)  # E - C, weight 4
g.add_edge(4, 6, 5)  # E - G, weight 5
g.add_edge(2, 5, 5)  # C - F, weight 5
g.add_edge(2, 1, 2)  # C - B, weight 2
g.add_edge(1, 5, 2)  # B - F, weight 2
g.add_edge(6, 5, 5)  # G - F, weight 5

# Dijkstra's algorithm from D to all vertices
print("\nDijkstra's Algorithm starting from vertex D:")
distances = g.dijkstra('D')
for i, d in enumerate(distances):
    print(f"Distance from D to {g.vertex_data[i]}: {d}")
Chạy ví dụ »

Thuật toán Dijkstra trên đồ thị có hướng

Để chạy thuật toán Dijkstra trên đồ thị có hướng, cần rất ít thay đổi.

Tương tự như thay đổi chúng ta cần để phát hiện chu trình cho đồ thị có hướng , chúng ta chỉ cần xóa một dòng mã để ma trận kề không còn đối xứng nữa.

Hãy triển khai đồ thị có hướng này và chạy thuật toán Dijkstra từ đỉnh D.

thông tin F 2 5 3 4 5 2 thông tin B thông tin C 5 5 2 thông tin MỘT 4 4 thông tin E 0 D thông tin G

Đây là cách triển khai thuật toán Dijkstra trên đồ thị có hướng, với D là đỉnh nguồn:

Ví dụ

Trăn:

 class Graph:
    def __init__(self, size):
        self.adj_matrix = [[0] * size for _ in range(size)]
        self.size = size
        self.vertex_data = [''] * size

    def add_edge(self, u, v, weight):
        if 0 <= u < self.size and 0 <= v < self.size:
            self.adj_matrix[u][v] = weight
            #self.adj_matrix[v][u] = weight   For undirected graph

    def add_vertex_data(self, vertex, data):
        if 0 <= vertex < self.size:
            self.vertex_data[vertex] = data

    def dijkstra(self, start_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        distances = [float('inf')] * self.size
        distances[start_vertex] = 0
        visited = [False] * self.size

        for _ in range(self.size):
            min_distance = float('inf')
            u = None
            for i in range(self.size):
                if not visited[i] and distances[i] < min_distance:
                    min_distance = distances[i]
                    u = i

            if u is None:
                break

            visited[u] = True

            for v in range(self.size):
                if self.adj_matrix[u][v] != 0 and not visited[v]:
                    alt = distances[u] + self.adj_matrix[u][v]
                    if alt < distances[v]:
                        distances[v] = alt

        return distances

g = Graph(7)

g.add_vertex_data(0, 'A')
g.add_vertex_data(1, 'B')
g.add_vertex_data(2, 'C')
g.add_vertex_data(3, 'D')
g.add_vertex_data(4, 'E')
g.add_vertex_data(5, 'F')
g.add_vertex_data(6, 'G')

g.add_edge(3, 0, 4)  # D -> A, weight 5
g.add_edge(3, 4, 2)  # D -> E, weight 2
g.add_edge(0, 2, 3)  # A -> C, weight 3
g.add_edge(0, 4, 4)  # A -> E, weight 4
g.add_edge(4, 2, 4)  # E -> C, weight 4
g.add_edge(4, 6, 5)  # E -> G, weight 5
g.add_edge(2, 5, 5)  # C -> F, weight 5
g.add_edge(1, 2, 2)  # B -> C, weight 2
g.add_edge(1, 5, 2)  # B -> F, weight 2
g.add_edge(6, 5, 5)  # G -> F, weight 5

# Dijkstra's algorithm from D to all vertices
print("Dijkstra's Algorithm starting from vertex D:\n")
distances = g.dijkstra('D')
for i, d in enumerate(distances):
    print(f"Shortest distance from D to {g.vertex_data[i]}: {d}")
Chạy ví dụ »

Hình ảnh bên dưới cho chúng ta thấy khoảng cách ngắn nhất từ ​​đỉnh D được tính bằng thuật toán Dijkstra.

11 F 2 5 3 4 5 2 thông tin B 6 C 5 5 2 4 MỘT 4 4 2 E 0 D 7 G

Kết quả này tương tự với ví dụ trước sử dụng thuật toán Dijkstra trên đồ thị vô hướng. Tuy nhiên, có một điểm khác biệt chính: trong trường hợp này, không thể truy cập đỉnh B từ D và điều này có nghĩa là khoảng cách ngắn nhất từ ​​D đến F bây giờ là 11 chứ không phải 10, vì đường đi không thể đi qua đỉnh B nữa.


Trả về đường dẫn từ thuật toán Dijkstra

Với một vài điều chỉnh, thuật toán Dijkstra cũng có thể trả về các đường dẫn ngắn nhất thực tế, bên cạnh các giá trị đường dẫn ngắn nhất. Vì vậy, ví dụ, thay vì chỉ trả về giá trị đường đi ngắn nhất là 10 từ đỉnh D đến F, thuật toán cũng có thể trả về giá trị đường đi ngắn nhất đó là "D->E->C->B->F".

10 F 2 5 3 4 5 2 số 8 B 6 C 5 5 2 4 MỘT 4 4 2 E 0 D 7 G

Để trả về đường dẫn, chúng ta tạo một mảng predecessors để giữ đỉnh trước đó trong đường đi ngắn nhất cho mỗi đỉnh. Mảng predecessors có thể được sử dụng để quay lui để tìm đường đi ngắn nhất cho mọi đỉnh.

Ví dụ

Trăn:

 class Graph:
    # ... (rest of the Graph class)

    def dijkstra(self, start_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        distances = [float('inf')] * self.size
        predecessors = [None] * self.size
        distances[start_vertex] = 0
        visited = [False] * self.size

        for _ in range(self.size):
            min_distance = float('inf')
            u = None
            for i in range(self.size):
                if not visited[i] and distances[i] < min_distance:
                    min_distance = distances[i]
                    u = i

            if u is None:
                break

            visited[u] = True

            for v in range(self.size):
                if self.adj_matrix[u][v] != 0 and not visited[v]:
                    alt = distances[u] + self.adj_matrix[u][v]
                    if alt < distances[v]:
                        distances[v] = alt
                        predecessors[v] = u

        return distances, predecessors

    def get_path(self, predecessors, start_vertex, end_vertex):
        path = []
        current = self.vertex_data.index(end_vertex)
        while current is not None:
            path.insert(0, self.vertex_data[current])
            current = predecessors[current]
            if current == self.vertex_data.index(start_vertex):
                path.insert(0, start_vertex)
                break
        return '->'.join(path)  # Join the vertices with '->'

g = Graph(7)

# ... (rest of the graph setup)

# Dijkstra's algorithm from D to all vertices
print("Dijkstra's Algorithm starting from vertex D:\n")
distances, predecessors = g.dijkstra('D')
for i, d in enumerate(distances):
    path = g.get_path(predecessors, 'D', g.vertex_data[i])
    print(f"{path}, Distance: {d}")
Chạy ví dụ »

Dòng 7 và 29: Mảng predecessors trước tiên được khởi tạo với các giá trị None , sau đó nó được cập nhật với mảng tiền thân chính xác cho mỗi đỉnh khi các giá trị đường dẫn ngắn nhất được cập nhật.

Dòng 33-42: Phương thức get_path sử dụng mảng predecessors và trả về một chuỗi có đường đi ngắn nhất từ ​​đỉnh đầu đến đỉnh cuối.


Thuật toán Dijkstra với một đỉnh đích duy nhất

Giả sử chúng ta chỉ quan tâm đến việc tìm đường đi ngắn nhất giữa hai đỉnh, giống như tìm khoảng cách ngắn nhất giữa đỉnh D và đỉnh F trong biểu đồ bên dưới.

thông tin F 2 5 3 4 5 2 thông tin B thông tin C 5 5 2 thông tin MỘT 4 4 thông tin E 0 D thông tin G 5 thông tin H 4 thông tin TÔI 2 thông tin J

Thuật toán Dijkstra thường được sử dụng để tìm đường đi ngắn nhất từ ​​một đỉnh nguồn tới tất cả các đỉnh khác trong đồ thị, nhưng nó cũng có thể được sửa đổi để chỉ tìm đường đi ngắn nhất từ ​​nguồn đến một đỉnh đích duy nhất, bằng cách dừng thuật toán khi đã đến đích (đã ghé thăm).

Điều này có nghĩa là đối với đồ thị cụ thể trong hình trên, thuật toán Dijkstra sẽ dừng sau khi truy cập F (đỉnh đích), trước khi truy cập các đỉnh H, I và J vì chúng ở xa D hơn F.

Dưới đây chúng ta có thể thấy trạng thái các khoảng cách được tính toán khi thuật toán Dijkstra đã tìm được khoảng cách ngắn nhất từ ​​D đến F và dừng chạy.

10 F 2 5 3 4 5 2 số 8 B 6 C 5 5 2 4 MỘT 4 4 2 E 0 D 7 G 5 12 H 4 11 TÔI 2 thông tin J

Trong hình trên, đỉnh F vừa được cập nhật với khoảng cách 10 tính từ đỉnh B. Vì F là đỉnh chưa được thăm có khoảng cách thấp nhất tới D nên thông thường nó sẽ là đỉnh hiện tại tiếp theo, nhưng vì nó là đích nên thuật toán dừng . Nếu thuật toán không dừng lại, J sẽ là đỉnh tiếp theo có khoảng cách cập nhật 11+2=13, tính từ đỉnh I.

Đoạn mã dưới đây là thuật toán Dijkstra được triển khai để tìm đường đi ngắn nhất tới một đỉnh đích duy nhất:

Ví dụ

Trăn:

 class Graph:
    # ... (existing methods)

    def dijkstra(self, start_vertex_data, end_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        end_vertex = self.vertex_data.index(end_vertex_data)
        distances = [float('inf')] * self.size
        predecessors = [None] * self.size
        distances[start_vertex] = 0
        visited = [False] * self.size

        for _ in range(self.size):
            min_distance = float('inf')
            u = None
            for i in range(self.size):
                if not visited[i] and distances[i] < min_distance:
                    min_distance = distances[i]
                    u = i

            if u is None or u == end_vertex:
                print(f"Breaking out of loop. Current vertex: {self.vertex_data[u]}")
                print(f"Distances: {distances}")
                break

            visited[u] = True
            print(f"Visited vertex: {self.vertex_data[u]}")

            for v in range(self.size):
                if self.adj_matrix[u][v] != 0 and not visited[v]:
                    alt = distances[u] + self.adj_matrix[u][v]
                    if alt < distances[v]:
                        distances[v] = alt
                        predecessors[v] = u

        return distances[end_vertex], self.get_path(predecessors, start_vertex_data, end_vertex_data)

# Example usage
g = Graph(7)
# ... (rest of the graph setup)
distance, path = g.dijkstra('D', 'F')
print(f"Path: {path}, Distance: {distance}")
Chạy ví dụ »

Dòng 20-23: Nếu chúng ta chuẩn bị chọn đỉnh đích làm đỉnh hiện tại và đánh dấu nó là đã ghé thăm, điều đó có nghĩa là chúng ta đã tính khoảng cách ngắn nhất đến đỉnh đích và thuật toán Dijkstra có thể dừng trong trường hợp đích duy nhất này.


Độ phức tạp thời gian của thuật toán Dijkstra

Với \(V\) là số đỉnh trong biểu đồ của chúng ta, độ phức tạp về thời gian của thuật toán Dijkstra là

\[ O( V^2 ) \]

Lý do tại sao chúng ta có độ phức tạp về thời gian này là vì đỉnh có khoảng cách thấp nhất phải được tìm kiếm để chọn đỉnh hiện tại tiếp theo và việc đó cần có thời gian \(O(V)\). Và vì điều này phải được thực hiện cho mọi đỉnh được kết nối với nguồn, nên chúng ta cần tính đến yếu tố đó và do đó chúng ta nhận được độ phức tạp về thời gian \(O(V^2)\) cho thuật toán Dijkstra.

Thay vào đó, bằng cách sử dụng cấu trúc dữ liệu Min-heap hoặc Fibonacci-heap cho khoảng cách (chưa được giải thích trong hướng dẫn này), thời gian cần thiết để tìm kiếm đỉnh khoảng cách tối thiểu sẽ giảm từ \(O(V)\) xuống \(O ( \log{V})\), dẫn đến độ phức tạp về thời gian được cải thiện cho thuật toán Dijkstra

\[ O( V \cdot \log{V} + E ) \]

Trong đó \(V\) là số đỉnh trong đồ thị và \(E\) là số cạnh.

Sự cải thiện mà chúng tôi nhận được từ việc sử dụng cấu trúc dữ liệu Min-heap cho thuật toán Dijkstra đặc biệt tốt nếu chúng tôi có một biểu đồ lớn và thưa thớt, nghĩa là một biểu đồ có số lượng đỉnh lớn nhưng không có nhiều cạnh.

Việc triển khai thuật toán Dijkstra với cấu trúc dữ liệu Fibonacci-heap sẽ tốt hơn đối với các đồ thị dày đặc, trong đó mỗi đỉnh có một cạnh với hầu hết mọi đỉnh khác.


Bài tập DSA

Kiểm tra bản thân bằng các bài tập

Bài tập:

Sử dụng thuật toán Dijkstra để tìm đường đi ngắn nhất từ ​​đỉnh C trong biểu đồ này:

Đỉnh tiếp theo cần được thăm sau khi thăm C là gì?

Sử dụng thuật toán Dijkstra,
đỉnh tiếp theo cần được thăm 
sau đỉnh C là đỉnh .

Bắt đầu bài tập



×

Liên hệ bán hàng

Nếu bạn muốn sử dụng dịch vụ của Example.com.vn với tư cách là một tổ chức giáo dục, nhóm hoặc doanh nghiệp, hãy gửi email cho chúng tôi:
[email được bảo vệ]

Báo cáo lỗi

Nếu bạn muốn báo cáo lỗi hoặc nếu bạn muốn đưa ra đề xuất, hãy gửi email cho chúng tôi:
[email được bảo vệ]

Example.com.vn được tối ưu hóa cho việc học tập và đào tạo. Các ví dụ có thể được đơn giản hóa để cải thiện khả năng đọc và học. Các hướng dẫn, tài liệu tham khảo và ví dụ liên tục được xem xét để tránh sai sót, nhưng chúng tôi không thể đảm bảo tính chính xác hoàn toàn của mọi nội dung. Khi sử dụng W3Schools, bạn đồng ý đã đọc và chấp nhận các điều khoản sử dụng , chính sách cookie và quyền riêng tư của chúng tôi.

Bản quyền 1999-2024 của Refsnes Data. Đã đăng ký Bản quyền. Example.com.vn được cung cấp bởi W3.CSS .