Các Khái Niệm Cơ Bản Về Đồ Thị và Tìm Kiếm Theo Chiều Sâu

Lý Thuyết Đồ Thị

Phân Loại Đồ Thị

  • Đồ thị vô hướng: Cạnh không có hướng xác định
  • Đồ thị có hướng: Cạnh xác định chiều đi giữa các đỉnh
  • Đồ thị có trọng số: Cạnh mang giá trị trọng lượng

Bậc Đỉnh

  • Vô hướng: Số cạnh nối với đỉnh
  • Có hướng: Bao gồm bậc vào (số cạnh hướng tới) và bậc ra (số cạnh đi ra)

Tính Liên Thông

  • Đồ thị liên thông (vô hướng): Mọi cặp đỉnh có đường nối
  • Đồ thị liên thông mạnh (có hướng): Mọi cặp đỉnh có đường đi hai chiều
  • Thành phần liên thông: Tập con liên thông cực đại

Biểu Diễn Đồ Thị

Ma trận kề: Dùng mảng 2 chiều kích thước V×V

  • Ưu điểm: Kiểm tra cạnh nhanh, phù hợp đồ thị dày
  • Nhược điểm: Tốn bộ nhớ với đồ thị thưa

Danh sách kề: Mảng kết hợp danh sách liên kết

  • Ưu điểm: Tiết kiệm bộ nhớ, duyệt láng giềng hiệu quả
  • Nhược điểm: Kiểm tra cạnh chậm hơn

Phương Pháp Duyệt

  • Tìm kiếm theo chiều sâu (DFS)
  • Tìm kiếm theo chiều rộng (BFS)

Tìm Kiếm Theo Chiều Sâu (DFS)

Khác Biệt với BFS

  • DFS: Duyệt sâu theo nhánh, sử dụng backtracking khi gặp ngõ cụt
  • BFS: Duyệt theo tầng, xử lý toàn bộ đỉnh cùng mức

Khung Thuật Toán DFS

void dfs(Tham số) {
  if (Điều kiện dừng) {
    Lưu kết quả;
    return;
  }
  for (Đỉnh kề : danh sách kề) {
    Xử lý đỉnh;
    dfs(Đồ thị, đỉnh kề);
    Hoàn tác xử lý; // Backtrack
  }
}

Bài Toán: Tìm Tất Cả Đường Đi

Khởi Tạo Đồ Thị

Ma trận kề:

int[][] maTran = new int[soDinh + 1][soDinh + 1];
for (int i = 0; i < soCanh; i++) {
  int diemDau = scanner.nextInt();
  int diemCuoi = scanner.nextInt();
  maTran[diemDau][diemCuoi] = 1;
}

Danh sách kề:

List<List<Integer>> dsKe = new ArrayList<>(soDinh + 1);
for (int i = 0; i <= soDinh; i++) {
  dsKe.add(new ArrayList<>());
}
while (soCanh-- > 0) {
  int diemDau = scanner.nextInt();
  int diemCuoi = scanner.nextInt();
  dsKe.get(diemDau).add(diemCuoi);
}

Thuật Toán DFS

Với ma trận kề:

void timDuong(int[][] maTran, int dinhHienTai, int dich) {
  if (dinhHienTai == dich) {
    ketQua.add(new ArrayList<>(duongDi));
    return;
  }
  for (int i = 1; i <= tongDinh; i++) {
    if (maTran[dinhHienTai][i] == 1) {
      duongDi.add(i);
      timDuong(maTran, i, dich);
      duongDi.remove(duongDi.size() - 1);
    }
  }
}

Với danh sách kề:

void timDuong(List<List<Integer>> dsKe, int dinhHienTai, int dich) {
  if (dinhHienTai == dich) {
    ketQua.add(new ArrayList<>(duongDi));
    return;
  }
  for (int ke : dsKe.get(dinhHienTai)) {
    duongDi.add(ke);
    timDuong(dsKe, ke, dich);
    duongDi.remove(duongDi.size() - 1);
  }
}

Thực Thi Chương Trình

public class TimDuong {
  static List<List<Integer>> ketQua = new ArrayList<>();
  static List<Integer> duongDi = new ArrayList<>();
  
  public static void main(String[] args) {
    // Khởi tạo đồ thị (chọn 1 trong 2 cách)
    duongDi.add(1); // Bắt đầu từ đỉnh 1
    timDuong(doThi, 1, dich); // Gọi DFS
    
    if (ketQua.isEmpty()) System.out.println(-1);
    for (List<Integer> p : ketQua) {
      // In kết quả
    }
  }
}

Thẻ: đồ thị DFS thuật toán Java ma trận kề

Đăng vào ngày 24 tháng 6 lúc 04:20