Toán tin vuotlen.com

Thuật toán Dijkstra

Phân loại Thông tin
Tên thuật toán Dijkstra
Phân loại thuật toán Thuật toán tham lam, thuật toán tìm đường đi ngắn nhất
Cấu trúc dữ liệu Ma trận kề hoặc danh sách kề
Độ phức tạp thời gian (Worst Case) O(V²) với ma trận kề, O((V + E) log V) với danh sách kề và hàng đợi ưu tiên
Độ phức tạp dung lượng O(V²) với ma trận kề, O(V + E) với danh sách kề
Độ phức tạp bộ nhớ O(V²) với ma trận kề, O(V + E) với danh sách kề
Phạm vi sử dụng Tìm đường đi ngắn nhất từ một đỉnh đến tất cả các đỉnh khác trong đồ thị có trọng số không âm
Khả năng xử lý trọng số âm Không
Khả năng xử lý chu trình âm Không
Phù hợp với đồ thị có hướng
Phù hợp với đồ thị vô hướng
Người phát hiện đầu tiên Edsger W. Dijkstra (1956)
Ứng dụng ban đầu Tìm đường đi ngắn nhất trong mạng lưới và hệ thống giao thông
Ứng dụng hiện đại Hệ thống định vị GPS, mạng máy tính, định tuyến mạng, tối ưu hóa
Một số ứng dụng và hệ thống nổi tiếng sử dụng thuật toán Google Maps, các hệ thống định vị vệ tinh, hệ thống quản lý giao thông, mạng viễn thông, hệ thống lập lịch trình và tối ưu hóa, các công cụ phân tích mạng và hệ thống điều khiển lưu lượng mạng.

 

Phân loại Thông tin
Ưu điểm - Tìm đường đi ngắn nhất hiệu quả trong đồ thị có trọng số không âm.
  - Phù hợp với nhiều ứng dụng thực tế như GPS, mạng máy tính.
  - Có thể sử dụng với cả ma trận kề và danh sách kề, linh hoạt với các loại đồ thị.
  - Hiệu quả với đồ thị thưa khi dùng danh sách kề và hàng đợi ưu tiên.
Nhược điểm - Không thể xử lý đồ thị có trọng số âm.
  - Độ phức tạp cao với đồ thị dày đặc khi dùng ma trận kề.
  - Không hiệu quả bằng thuật toán Bellman-Ford cho đồ thị có cạnh trọng số âm.

 

Chi Tiết Kỹ Thuật:

Thuật toán Dijkstra hoạt động bằng cách sử dụng một tập hợp các đỉnh đã biết khoảng cách ngắn nhất từ đỉnh nguồn và mở rộng dần tập hợp này cho đến khi tất cả các đỉnh đều đã được thăm. Các bước cơ bản của thuật toán bao gồm:

  1. Khởi tạo khoảng cách từ đỉnh nguồn đến chính nó bằng 0 và đến tất cả các đỉnh khác bằng vô cùng.
  2. Đặt tất cả các đỉnh vào một tập hợp chưa được thăm.
  3. Chọn đỉnh có khoảng cách nhỏ nhất từ tập hợp chưa được thăm và cập nhật khoảng cách của các đỉnh lân cận.
  4. Lặp lại bước 3 cho đến khi tất cả các đỉnh đã được thăm.

Pseudo-code của Thuật toán Dijkstra:

function Dijkstra(Graph, source):

    dist[source] ← 0  // Khởi tạo khoảng cách từ nguồn đến chính nó là 0

    for each vertex v in Graph:

        if v ≠ source:

            dist[v] ← infinity  // Khởi tạo khoảng cách đến tất cả các đỉnh khác là vô cực

        add v to Q  // Thêm tất cả các đỉnh vào hàng đợi ưu tiên Q

    while Q is not empty:

        u ← vertex in Q with min dist[u]  // Lấy đỉnh u trong Q có khoảng cách nhỏ nhất

        remove u from Q  // Loại bỏ u khỏi hàng đợi ưu tiên

        for each neighbor v of u:  // Duyệt qua từng đỉnh kề v của u

            alt ← dist[u] + length(u, v)  // Tính toán khoảng cách thay thế đến v thông qua u

            if alt < dist[v]:  // Nếu khoảng cách thay thế nhỏ hơn khoảng cách đã biết đến v

                dist[v] ← alt  // Cập nhật khoảng cách đến v với khoảng cách thay thế nhỏ hơn này

    return dist  // Trả về bảng khoảng cách từ nguồn đến từng đỉnh

Giải thích:

Code C++:

// https://graphicmaths.com/computer-science/graph-theory/dijkstras-algorithm/

#include <bits/stdc++.h>

 

using namespace std;

#define V 5
#define INF INT_MAX

typedef pair<int, int> iPair;

// Mảng ánh xạ chỉ số đỉnh thành ký hiệu
char dinhKyHieu[V] = {'A', 'B', 'C', 'D', 'E'};

// Hàm in đường đi từ đỉnh nguồn đến một đỉnh
void inDuongDi(const int truoc[], int v) {
    vector<int> path;
    for (int i = v; i != -1; i = truoc[i]) {
        path.push_back(i);
    }
    reverse(path.begin(), path.end());
    for (int i = 0; i < path.size(); i++) {
        cout << dinhKyHieu[path[i]];
        if (i < path.size() - 1) {
            cout << " -> ";
        }
    }
}

// Hàm in đường đi ngắn nhất và khoảng cách
void inKetQua(const int khoangCach[], const int truoc[], int src) {
    cout << "Dinh \t Khoang cach \t Duong di\n";
    for (int i = 0; i < V; i++) {
        cout << dinhKyHieu[src] << " -> " << dinhKyHieu[i] << "\t" << khoangCach[i] << "\t\t";
        if (khoangCach[i] != INF) {
            inDuongDi(truoc, i);
        }
        cout << "\n";
    }
}

// Hàm khởi tạo khoảng cách và đỉnh trước đó
void khoiTao(int khoangCach[], int truoc[], int src) {
    fill(khoangCach, khoangCach + V, INF);
    fill(truoc, truoc + V, -1);
    khoangCach[src] = 0;
}

// Hàm cập nhật khoảng cách và hàng đợi ưu tiên
void capNhatKhoangCach(int u, int v, int khoangCach[], int truoc[], priority_queue<iPair, vector<iPair>, greater<iPair>>& pq, int graph[V][V]) {
    khoangCach[v] = khoangCach[u] + graph[u][v];
    truoc[v] = u;
    pq.push({khoangCach[v], v});
}

// Hàm cập nhật hàng đợi ưu tiên
void capNhatHangDoi(priority_queue<iPair, vector<iPair>, greater<iPair>>& pq, int u, int v, int khoangCach[], int truoc[], int graph[V][V]) {
    if (graph[u][v] && khoangCach[u] + graph[u][v] < khoangCach[v]) {
        capNhatKhoangCach(u, v, khoangCach, truoc, pq, graph);
    }
}

// Hàm Dijkstra sử dụng priority_queue và pair
void dijkstra(int graph[V][V], int src) {
    int khoangCach[V]; // Mảng lưu khoảng cách ngắn nhất từ nguồn đến các đỉnh
    int truoc[V]; // Mảng lưu đỉnh trước đó trên đường đi ngắn nhất
    priority_queue<iPair, vector<iPair>, greater<iPair>> pq; // Priority queue lưu cặp (khoảng cách, đỉnh)

    khoiTao(khoangCach, truoc, src); // Khởi tạo khoảng cách và đỉnh trước đó
    pq.push({0, src});

    while (!pq.empty()) {
        int u = pq.top().second;
        pq.pop();

        for (int v = 0; v < V; v++) {
            capNhatHangDoi(pq, u, v, khoangCach, truoc, graph);
        }
    }

    inKetQua(khoangCach, truoc, src); // In kết quả
}

int main() {
    int graph[V][V] = {
        // A  B  C  D  E
        {0, 7, 0, 0, 1},  // A
        {7, 0, 3, 0, 8},  // B
        {0, 3, 0, 6, 2},  // C
        {0, 0, 6, 0, 7},  // D
        {1, 8, 2, 7, 0},  // E
    };

    int nguon = 0; // Đỉnh nguồn

    dijkstra(graph, nguon); // Chạy thuật toán Dijkstra

    return 0;
}