Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Duyệt tìm kiếm theo chiều sâu (DFS) trong JavaScript


DFS ghé thăm các đỉnh con trước đỉnh anh em; tức là nó duyệt sâu theo một đường đi cụ thể trước khi khám phá độ rộng. Một stack (thường là call stack của chương trình qua đệ quy) thường được dùng khi triển khai thuật toán.

Cách DFS hoạt động:

  • Ghé thăm đỉnh kề chưa thăm. Đánh dấu đã thăm. Hiển thị nó. Đẩy vào stack.
  • Nếu không tìm thấy đỉnh kề, lấy một đỉnh từ stack. (Sẽ lấy tất cả đỉnh từ stack, những đỉnh không có đỉnh kề.)
  • Lặp lại Quy tắc 1 và 2 cho đến khi stack rỗng.

Hãy xem minh họa cách DFS traversal hoạt động.

BướcDuyệtMô tả
1Duyệt tìm kiếm theo chiều sâu (DFS) trong JavaScriptKhởi tạo stack.
2Duyệt tìm kiếm theo chiều sâu (DFS) trong JavaScriptĐánh dấu S đã thăm và đẩy vào stack. Khám phá nút kề chưa thăm từ S. Có 3 nút và có thể chọn bất kỳ. Ví dụ này chọn theo thứ tự bảng chữ cái.
3Duyệt tìm kiếm theo chiều sâu (DFS) trong JavaScriptĐánh dấu A đã thăm và đẩy vào stack. Khám phá nút kề chưa thăm từ A. Cả S và D kề với A nhưng chỉ quan tâm nút chưa thăm.
4Duyệt tìm kiếm theo chiều sâu (DFS) trong JavaScriptGhé thăm D, đánh dấu đã thăm và đẩy vào stack. Ở đây có B và C kề với D và cả hai chưa thăm. Tuy nhiên, lại chọn theo thứ tự bảng chữ cái.
5Duyệt tìm kiếm theo chiều sâu (DFS) trong JavaScriptChọn B, đánh dấu đã thăm và đẩy vào stack. B không có nút kề chưa thăm. Vậy lấy B ra khỏi stack.
6Duyệt tìm kiếm theo chiều sâu (DFS) trong JavaScriptKiểm tra đỉnh đỉnh stack để quay lại nút trước và xem có nút kề chưa thăm không. Ở đây, tìm thấy D ở đỉnh stack.
7Duyệt tìm kiếm theo chiều sâu (DFS) trong JavaScriptChỉ nút kề chưa thăm từ D là C bây giờ. Vậy ghé thăm C, đánh dấu đã thăm và đẩy vào stack.

Vì C không có nút kề chưa thăm, ta tiếp tục lấy stack cho đến khi tìm thấy nút có nút kề chưa thăm. Trong trường hợp này, không có và ta lấy cho đến khi stack rỗng. Hãy xem cách triển khai trong JavaScript.

Ví dụ

DFS(node) {
  // Tạo Stack và thêm nút khởi tạo vào
  let s = new Stack(this.nodes.length);
  let explored = new Set();
  s.push(node);
  // Đánh dấu nút đầu tiên đã khám phá
  explored.add(node);
  // Tiếp tục cho đến khi Stack rỗng
  while (!s.isEmpty()) {
    let t = s.pop();
    // Log mọi phần tử ra khỏi Stack
    console.log(t);
    // 1. Trong object edges, tìm nút mà nút này kết nối trực tiếp.
    // 2. Lọc bỏ nút đã được khám phá.
    // 3. Đánh dấu mỗi nút chưa khám phá là đã khám phá và đẩy vào Stack.
    this.edges[t]
      .filter(n => !explored.has(n))
      .forEach(n => {
        explored.add(n);
        s.push(n);
      });
  }
}

Thật dễ dàng. Chúng ta chỉ đổi queue thành stack và đã có DFS. Đó chính là sự khác biệt duy nhất giữa hai thuật toán. DFS cũng có thể triển khai bằng đệ quy. Nhưng tốt nhất tránh trong trường hợp này vì đồ thị lớn nghĩa là cần bộ nhớ thêm để theo dõi call stack.

Bạn có thể kiểm tra bằng cách sau:

let g = new Graph();
g.addNode("A");
g.addNode("B");
g.addNode("C");
g.addNode("D");
g.addNode("E");
g.addNode("F");
g.addNode("G");
g.addEdge("A", "C");
g.addEdge("A", "B");
g.addEdge("A", "D");
g.addEdge("D", "E");
g.addEdge("E", "F");
g.addEdge("B", "G");
g.DFS("A");

Kết quả

Điều này sẽ cho ra kết quả.

A D E F B G C