1. Trang chủ
  2. » Luận Văn - Báo Cáo

Nghiên cứu và cài đặt chương trình thực hiện các giải thuật tìm kiếm theo chiều sâuvà theo chiều rộng trên đồ thị (đồ thị được lưu trữ bằng ma trận lân cận và bằngdanh sách lân cận)

37 1 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Nghiên Cứu Và Cài Đặt Chương Trình Thực Hiện Các Giải Thuật Tìm Kiếm Theo Chiều Sâu Và Theo Chiều Rộng Trên Đồ Thị (Đồ Thị Được Lưu Trữ Bằng Ma Trận Lân Cận Và Bằng Danh Sách Lân Cận)
Tác giả Nguyễn Huyền Trâm, Đỗ Hương Trà, Lương Xuân Đại, Nguyễn Thị Như Quỳnh, Nguyễn Xuân Sơn, Vũ Lê Thành Vinh
Người hướng dẫn ThS. Lưu Minh Tuấn
Trường học Trường Đại Học Kinh Tế Quốc Dân
Chuyên ngành Khoa Học Máy Tính
Thể loại tiểu luận
Năm xuất bản 2022
Thành phố Hà Nội
Định dạng
Số trang 37
Dung lượng 1,79 MB

Cấu trúc

  • CHƯƠNG I. GIỚI THIỆU ĐỀ TÀI (6)
    • 1.1. Phát biểu đề tài (6)
    • 1.2. Đối tượng và phạm vi nghiên cứu (6)
    • 1.3. Phương pháp nghiên cứu (7)
    • 1.4. Mục đích nghiên cứu (7)
  • CHƯƠNG II. TRÌNH BÀY NỘI DUNG VỀ ĐỀ TÀI NGHIÊN CỨU (8)
    • 2.1. Định nghĩa và các khái kiệm (8)
      • 2.1.1. Đồ thị và phân loại (8)
      • 2.1.2. Các khái niệm trên đồ thị (9)
    • 2.2. Các phương pháp lưu trữ (biểu diễn) đồ thị (10)
      • 2.2.1. Ma trận kề (10)
      • 2.2.2. Danh sách kề (13)
    • 2.3. Phát biểu bài toán tìm kiếm trên đồ thị (16)
    • 2.4. Các phép tìm kiếm trên đồ thị (17)
      • 2.4.1. Tìm kiếm theo chiều sâu (17)
      • 2.4.2. Tìm kiếm theo chiều rộng (22)
  • CHƯƠNG III. ĐÁNH GIÁ KẾT QUẢ NGHIÊN CỨU VÀ KẾT LUẬN (35)
    • 1.1. So sánh tìm kiếm theo chiều rộng và tìm kiếm theo chiều sâu (35)
    • 1.2. Kết luận (36)
  • TÀI LIỆU THAM KHẢO (37)

Nội dung

GIỚI THIỆU ĐỀ TÀI

Phát biểu đề tài

Vấn đề tìm kiếm tổng quan liên quan đến việc xác định một đối tượng đáp ứng các yêu cầu nhất định Trong một tập hợp đa dạng các đối tượng, nhiều vấn đề có thể được giải quyết thông qua quá trình tìm kiếm Các trò chơi như cờ vua và cờ caro minh họa cho khái niệm này, khi người chơi phải lựa chọn những nước đi chiến thắng từ vô số lựa chọn Tương tự, việc chứng minh định lý cũng là một dạng tìm kiếm; với một tập hợp các tiên đề và luật suy diễn, mục tiêu là tìm ra chuỗi luật chứng minh để đạt được công thức mong muốn.

Để đáp ứng nhu cầu thực tiễn, các nhà khoa học máy tính đã phát triển các thuật toán tìm kiếm Trong lý thuyết đồ thị, một vấn đề quan trọng là duyệt tất cả các đỉnh có thể tiếp cận từ một đỉnh xuất phát mà không lặp lại hoặc bỏ sót Do đó, cần xây dựng các phương pháp duyệt đỉnh theo một hệ thống nhất định, được gọi là thuật toán tìm kiếm trên đồ thị Đồ thị có thể được lưu trữ dưới dạng ma trận hoặc danh sách kề.

Hai giải thuật tìm kiếm cơ bản trên đồ thị là Tìm kiếm theo chiều sâu (DFS) và Tìm kiếm theo chiều rộng (BFS) Mặc dù độ phức tạp thuật toán của chúng tương đương, nhưng ứng dụng và cách cài đặt của từng giải thuật lại khác nhau.

Đối tượng và phạm vi nghiên cứu

Bài tiểu luận này dành cho những ai muốn khám phá môn Cấu trúc dữ liệu và giải thuật, đặc biệt là các phương pháp lưu trữ đồ thị và các kỹ thuật tìm kiếm trên đồ thị.

Lý thuyết môn học Cấu trúc dữ liệu và giải thuật

Lý thuyết đồ thị bao gồm hai phương pháp lưu trữ chính là ma trận lân cận và danh sách lân cận, mỗi phương pháp có những ưu điểm riêng Ngoài ra, trong việc tìm kiếm trên đồ thị, có hai thuật toán phổ biến là tìm kiếm theo chiều sâu (DFS) và tìm kiếm theo chiều rộng (BFS), giúp tối ưu hóa quá trình duyệt và khám phá các đỉnh trong đồ thị.

Phương pháp nghiên cứu

 Nêu ý tưởng của thuật toán

 Phân tích, đánh giá độ phức tạp thuật toán

Mục đích nghiên cứu

Với sự phát triển của khoa học kỹ thuật, công nghệ thông tin và bộ môn Cấu trúc dữ liệu và giải thuật đang được ứng dụng rộng rãi, việc tìm kiếm dữ liệu hiệu quả trong cơ sở dữ liệu khổng lồ ngày càng trở nên quan trọng Dữ liệu thường được biểu diễn dưới dạng danh sách liên kết, nhưng việc truy cập vẫn chưa đạt hiệu suất cao Sử dụng cấu trúc dữ liệu dạng đồ thị là giải pháp tối ưu để nâng cao hiệu suất xử lý Nghiên cứu về đồ thị và các phương pháp tìm kiếm trên đồ thị sẽ giúp lưu trữ thông tin một cách logic và cải thiện tốc độ tìm kiếm trong kho dữ liệu lớn.

TRÌNH BÀY NỘI DUNG VỀ ĐỀ TÀI NGHIÊN CỨU

Định nghĩa và các khái kiệm

2.1.1 Đồ thị và phân loại Đồ thị là một cấu trúc rời rạc bao gồm các đỉnh và các cạnh nối các đỉnh này.

G = (V,E) tức là đồ thị G có tập các đỉnh là V, tập các cạnh là E Có thể hiểu E là tập hợp các cặp (u, v) với u và v là hai đỉnh thuộc V.

Có thể phân loại đồ thị G theo tính chất của tập cạnh như sau:

 G được gọi là đơn đồ thị nếu như giữa hai đỉnh (u, v) của V có nhiều nhất một cạnh trong E nối từ u tới v.

Đồ thị G được gọi là đa đồ thị khi giữa hai đỉnh (u, v) có thể tồn tại nhiều hơn một cạnh nối trong tập E Trong khi đơn đồ thị cũng thuộc loại đa đồ thị, không phải mọi đa đồ thị đều là đơn đồ thị, vì đa đồ thị cho phép có hai hoặc nhiều cạnh nối giữa cùng một cặp đỉnh.

 G được gọi là đồ thị vô hướng (undirected graph) nếu như các cạnh trong E là không định hướng, tức là cạnh (u,v) là cạnh hai chiều.

Đồ thị có hướng (directed graph) là một loại đồ thị trong đó các cạnh (E) có định hướng, cho phép tồn tại cạnh nối từ đỉnh u đến đỉnh v mà không nhất thiết có cạnh nối ngược lại từ v đến u Trong đồ thị có hướng, các cạnh được gọi là các cung Đồng thời, đồ thị vô hướng cũng có thể được xem là một dạng đồ thị có hướng, khi mà mỗi cạnh (u, v) tương ứng với hai cung (u→v) và (v→u).

Trong bài nghiên cứu này, chúng ta sẽ chỉ tập trung xét đến đơn đồ thị.

Hình 2.1 Phân loại đồ thị

2.1.2 Các khái niệm trên đồ thị

 Hai đỉnh lân cận của nhau (hay hai đỉnh kề nhau) (adjacent): Nếu (vi,v j) là 1 cung thuộc E thì 2 đỉnh vi và v j gọi là lân cận của nhau (kề nhau).

Đường đi trong đồ thị G từ đỉnh va đến đỉnh vb là dãy các đỉnh va, vi1, vi2, , vin, trong đó các cặp (va, vi1), (vi1, vi2), (vi2, vi3), , (vin, vb) tạo thành các cung thuộc tập E(G) Độ dài của đường đi được xác định bằng số lượng cung có trong đường đi.

 Đường đi đơn: không có cạnh nào trên đường đi đó đi qua hơn một lần.

 Chu trình (cycle): Đường đi đơn mà đỉnh đầu và đỉnh cuối trùng nhau

 Hai đỉnh liên thông (connected): Nếu tồn tại ít nhất 1 đường đi từ đỉnh vi tới v j

(trong đồ thị vô hướng thì đồng thời cũng tồn tại đường đi từ đỉnh v j tới vi).

Lưu ý: Hai đỉnh kề nhau là hai đỉnh liên thông nhưng ngược lại hai đỉnh liên thông chưa chắc là 2 đỉnh kề nhau.

Đồ thị G được gọi là đồ thị liên thông khi với mọi cặp đỉnh phân biệt (vi, vj) luôn tồn tại ít nhất một đường đi từ đỉnh vi đến đỉnh vj Điều này có nghĩa là trong đồ thị liên thông, mọi đỉnh đều có thể truy cập được từ bất kỳ đỉnh nào khác thông qua các đường đi.

Hình 2.1.a Đồ thị có hướng, liên thông Hình 2.1.b Đồ thị vô hướng, liên thông

Hình 2.1.c Đồ thị có hướng, không liên thông Hình 2.1.d Đồ thị vô hướng, không liên thông

Đồ thị có trọng số là loại đồ thị mà mỗi cung được gán một giá trị, gọi là trọng số, để thể hiện thông tin cụ thể Khi có trọng số gắn liền với các cung, đồ thị này được xác định là đồ thị có trọng số.

Hình 2.1.e Đồ thị có hướng, có trọng số Hình 2.1.f Đồ thị vô hướng, có trọng số

Các phương pháp lưu trữ (biểu diễn) đồ thị

2.2.1 Ma trận kề a Khái niệm

Xét đồ thị G = {V, E} (có hướng hoặc vô hướng).

Giả sử tập V gồm n đỉnh và được sắp xếp theo thứ tự V = {x1, x2, …, xn}

Ma trận kề của đồ thị G, kí hiệu A(G), là một ma trận nhị phân cấp n x n được định nghĩa như sau: A = (Aij) với:

- Bij = 1 nếu có cạnh nối xi tới x j

- Bij = 0 nếu{ không có cạnhnối x i tớix j đỉnhi≡đỉnh j

Ví dụ 1: Đồ thị và ma trận kề tương đương của nó

 Ma trận kề phụ thuộc vào cách đánh số thứ tự cho các đỉnh.

 Ma trận kề biểu diễn đồ thị vô hướng là ma trận có các phần tử đối xứng nhau qua đường chéo chính.

Ma trận kề biểu diễn đồ thị có trọng số, được gọi là ma trận trọng số, được tạo ra bằng cách thay thế phần tử có giá trị 1 trong ma trận lân cận bằng giá trị của cung tương ứng Nếu không có cung nối đỉnh i với đỉnh j, giá trị 0 sẽ được thay bằng ∞ để thể hiện sự không tồn tại của cung đó Trong trường hợp đỉnh i bằng đỉnh j, giá trị 0 vẫn được giữ nguyên.

E ∞ 20 40 ∞ 0 b Thuật toán void ma_tran_ke(){

Đồ thị ban đầu có n đỉnh (từ 1 đến n) và m cạnh, được lưu trữ trong mảng 2 chiều [1000x1000] để biểu diễn ma trận kề Nếu có cạnh nối giữa hai đỉnh (i, j), phần tử aij và aji sẽ được gán giá trị 1, trong khi các phần tử khác sẽ có giá trị 0.

1 Khởi tạo vector lưu trữ danh sách các đỉnh kề đỉnh i int a[1000][1000];

2 Nhập vào số lượng đỉnh, cạnh và danh sách cạnh coutn>>m; cout>y;

3 Nếu có cạnh nối giữa 2 đỉnh thì đặt là 1 a[x][y]=a[y][x]=1;

Để cài đặt ma trận kề trong C++, đầu tiên, chúng ta cần xác định số lượng đỉnh (n) và cạnh (m) của đồ thị Khai báo một mảng 2 chiều a[1000][1000] để lưu trữ ma trận kề và một mảng bool tham[1001] để kiểm tra trạng thái thăm đỉnh Hàm ma_tran_ke sẽ yêu cầu người dùng nhập số lượng đỉnh và cạnh, sau đó nhập danh sách các cạnh bằng cách nhập hai đỉnh tương ứng Cuối cùng, cập nhật ma trận kề bằng cách gán giá trị 1 cho a[x][y] và a[y][x] để thể hiện mối quan hệ giữa các đỉnh trong đồ thị vô hướng.

} d Độ phức tạp tính toán:

- Độ phức tạp thời gian: O(n) với n là số cạnh đồ thị

- Độ phức tạp không gian: O(n 2 ) với n là số đỉnh của đồ thị e Ưu nhược điểm Ưu điểm

Các phép toán cơ bản bao gồm thêm cạnh, xóa cạnh và kiểm tra sự tồn tại của cạnh từ đỉnh i đến đỉnh j đều có hiệu suất thời gian tối ưu và thời gian thực hiện là không đổi.

Khi đồ thị có độ dày cao và số lượng cạnh lớn, ma trận kề là lựa chọn ưu tiên Trong trường hợp đồ thị và ma trận kề thưa, chúng ta có thể sử dụng cấu trúc dữ liệu phù hợp để biểu diễn ma trận thưa.

Thực hiện các thao tác trên ma trận kề giúp chúng ta hiểu rõ hơn về bản chất của đồ thị và các mối quan hệ giữa các đỉnh trong đồ thị đó.

Nhược điểm chính của phương pháp này là không phụ thuộc vào số cạnh của đồ thị, dẫn đến việc luôn cần sử dụng n^2 đơn vị bộ nhớ để lưu trữ ma trận kề, gây tốn kém một lượng lớn bộ nhớ Ma trận kề có nhiều ứng dụng trong việc biểu diễn và phân tích các đồ thị.

 Tạo bảng định tuyến trong mạng.

 Các bài toán về tìm hướng đi hoặc định vị.

2.2.2 Danh sách kề a Khái niệm

Danh sách kề (Adjacency list) là một phương pháp biểu diễn đồ thị (graph) thông qua một mảng các danh sách liên kết Trong cấu trúc này, chỉ số của mảng tương ứng với các đỉnh của đồ thị, trong khi các phần tử trong danh sách liên kết của mỗi đỉnh thể hiện các đỉnh có kết nối với đỉnh đó.

Phương pháp: Để lưu trữ đồ thị G có n đỉnh, sử dụng n danh sách móc nối đơn.

 Đánh số thứ tự cho các đỉnh của đồ thị G theo một trình tự nào đó (từ 1 → n).

Mỗi đỉnh trong đồ thị G đều có một danh sách móc nối tương ứng, trong đó các nút của danh sách thứ i thể hiện các đỉnh kề của nút i Mỗi danh sách bao gồm một nút "đầu danh sách", và các nút này được lưu trữ liên tiếp trong một véc tơ nhằm tăng tốc độ truy cập Ngoài ra, mỗi nút còn lại trong danh sách i có hai trường thông tin.

+ TrườngCHISO: chứa chỉ số (số thứ tự) của các đỉnh lân cận của đỉnh i. + TrườngCONTRO: là trường con trỏ, trỏ tới nút tiếp theo trong danh sách

Ví dụ: Đồ thị và danh sách kề tương đương của nó b Thuật toán void danh_sach_ke(){

Đồ thị ban đầu có n đỉnh (đánh số từ 1 đến n) và m cạnh Giải thuật sử dụng vecto dsk (vector dsk[1001]) với kích thước 1001 để lưu danh sách các đỉnh kề cho mỗi đỉnh i Khi có cạnh nối giữa hai đỉnh (u, v), chương trình sẽ thêm v vào danh sách kề của đỉnh u và thêm u vào danh sách kề của đỉnh v.

1 Khởi tạo vector lưu trữ danh sách các đỉnh kề đỉnh i vector dsk[1001];

2 Nhập vào số lượng đỉnh, cạnh và danh sách cạnh coutn>>m; cout>y; //nhap vao 2 dinh tuong ung voi canh can nhap

3 Đẩy y vào vector chứa danh sách kề đỉnh x và ngược lại dsk[x].push_back(y);//them y vao danh sach ke dinh x dsk[y].push_back(x);// them x vao danh sach ke dinh y }

} c Cài đặt danh sách kề bằng C++ int n, m; // Số lượng đỉnh(n), cạnh(m) của đồ thị vector dsk[1001]; // Vecto lưu trữ danh sách đỉnh kề nhau

Để xây dựng danh sách kề cho đồ thị, đầu tiên, người dùng cần nhập số lượng đỉnh và cạnh Sau đó, nhập danh sách các cạnh bằng cách cung cấp hai đỉnh tương ứng Mỗi cạnh được thêm vào danh sách kề của cả hai đỉnh, đảm bảo rằng đồ thị được biểu diễn đầy đủ và chính xác.

} d Độ phức tạp tính toán:

- Độ phức tạp thời gian: O(m) với m là số cạnh của đồ thị

- Độ phức tạp không gian: O(n) với n là số đỉnh của đồ thị e Ưu điểm, nhược điểm Ưu điểm

So với ma trận kề, việc biểu diễn đồ thị dưới dạng danh sách kề tiết kiệm bộ nhớ hơn Điều này đặc biệt rõ ràng khi áp dụng cho các đồ thị có nhiều đỉnh nhưng chỉ có ít cạnh kết nối.

 Việc duyệt các đỉnh kề với một đỉnh nào đó cũng cực kỳ nhanh chóng do mỗi đỉnh chỉ kết nối tới các đỉnh kề với nó.

 Cài đặt bài toán bằng danh sách kề tương đối dài hơn so với ma trận kề.

Việc kiểm tra kết nối giữa hai đỉnh trong danh sách liên kết cần phải duyệt tuần tự từ node đầu, điều này dẫn đến tốc độ chậm hơn so với phương pháp kiểm tra sử dụng ma trận kề.

Phát biểu bài toán tìm kiếm trên đồ thị

Trong bài viết này, chúng ta sẽ khám phá cách duyệt tất cả các đỉnh trong một đồ thị bắt đầu từ đỉnh s đã cho, đảm bảo rằng mỗi đỉnh được truy cập đúng một lần Quá trình này được gọi là tìm kiếm các đỉnh trên đồ thị từ đỉnh s.

Trong các ứng dụng liên quan đến đồ thị, thao tác tìm kiếm đóng vai trò cơ bản và quan trọng, tương tự như các phép toán cộng, trừ trong tính toán số học.

Trong phát biểu trên, hai điều kiện cơ bản của thao tác tìm kiếm là không bỏ sót và không trùng lặp, trong khi thứ tự tìm kiếm không được xem xét Những điều kiện này có thể thay đổi tùy thuộc vào các chiến lược tìm kiếm khác nhau và mục tiêu ứng dụng cụ thể.

Bài toán tìm kiếm trên đồ thị áp dụng cho cả đồ thị có hướng và vô hướng, với cạnh vô hướng cho phép di chuyển theo cả hai chiều Đồng thời, nhiều cạnh nối giữa cùng một cặp đỉnh chỉ được coi là một cạnh duy nhất trong ngữ cảnh của bài toán tìm kiếm, do đó, đa đồ thị và đơn đồ thị có thể coi là tương đương.

Thao tác tìm kiếm các đỉnh trên đồ thị bắt đầu từ đỉnh s được gọi là duyệt các đỉnh hoặc đi thăm các đỉnh từ đỉnh s Các đỉnh được tìm kiếm trong quá trình này được gọi là các đỉnh đã được duyệt hoặc thăm từ đỉnh s.

Có hai thuật toán cơ bản để tìm kiếm các đỉnh trên đồ thị từ một đỉnh khởi đầu, đó là tìm kiếm theo chiều rộng (BFS) và tìm kiếm theo chiều sâu (DFS).

Các phép tìm kiếm trên đồ thị

2.4.1 Tìm kiếm theo chiều sâu a Khái niệm

Tìm kiếm theo chiều sâu (Depth First Search - DFS) là một thuật toán quan trọng trong việc duyệt hoặc tìm kiếm trên cây và đồ thị Thuật toán này bắt đầu từ một đỉnh gốc và mở rộng theo từng nhánh một cách tối đa, giúp khám phá toàn bộ cấu trúc của cây hoặc đồ thị.

Tìm kiếm theo chiều sâu trong không gian bài toán bắt đầu từ một nút và tiếp tục cho đến khi gặp ngõ cụt hoặc đạt được đích Khi không thể tiếp tục, hệ thống sẽ quay lại một mức trên đồ thị và tìm kiếm theo hướng khác, ví dụ như đến nút "sát nút cực trái".

Trong thuật toán tìm kiếm theo chiều sâu, các đỉnh được thăm muộn sẽ được duyệt xong sớm hơn, áp dụng cơ chế Last-In First-Out (LIFO) Cơ chế này cho phép danh sách chỉ có một đầu, vừa là nơi lấy ra vừa là nơi nạp vào các phần tử Danh sách này được gọi là ngăn xếp (stack).

Thuật toán tìm kiếm theo chiều sâu có thể được biểu diễn một cách đệ quy, tương tự như thuật toán quay lui, giúp việc cài đặt trở nên tiện lợi hơn khi sử dụng ngôn ngữ đệ quy Phương pháp này cho phép tận dụng cơ chế ngăn xếp của lời gọi đệ quy mà không cần tổ chức ngăn xếp một cách tường minh.

Quá trình tìm kiếm theo chiều sâu (DFS) bắt đầu từ đỉnh xuất phát v Sau đó, một đỉnh lân cận chưa được thăm là w sẽ được chọn, và tìm kiếm từ w sẽ được thực hiện Quá trình này lặp lại cho đến khi một đỉnh u được “với tới” mà mọi đỉnh lân cận của nó đã được thăm Khi đó, tìm kiếm sẽ quay ngược lên đỉnh cuối cùng vừa được thăm, nơi vẫn còn đỉnh p chưa được thăm, và thực hiện tìm kiếm từ p Tìm kiếm kết thúc khi tất cả các đỉnh của đồ thị đã được thăm.

Đồ thị G bao gồm n đỉnh, trong đó từ đỉnh u có thể liên hệ với các đỉnh khác Thuật toán sử dụng mảng tham(n) với n phần tử, mỗi phần tử tương ứng với một đỉnh để đánh dấu trạng thái đã thăm hay chưa; nếu đã thăm, đánh dấu là 1, còn chưa thăm là 0 Thuật toán thực hiện duyệt đồ thị theo chiều sâu bắt đầu từ đỉnh u.

1 Khởi tạo mảngtham ban đầu là rỗng memset(tham,false,sizeof(tham));

2 Đánh dấu đỉnh u đã được thăm tham[u]=1;

3 Với các đỉnh x lân cận với đỉnh u if tham[x]=0 gọi dfs(x)

Đồ thị G bao gồm n đỉnh, trong đó đỉnh u có thể liên hệ với các đỉnh khác Giải thuật sử dụng mảng tham(n) với n phần tử, mỗi phần tử tương ứng với một đỉnh của đồ thị, nhằm đánh dấu trạng thái đã thăm hay chưa Nếu đỉnh đã được thăm, đánh dấu là 1, còn chưa thăm đánh dấu là 0 Giải thuật duyệt đồ thị theo chiều sâu bắt đầu từ đỉnh u, và sử dụng một stack s được cài đặt sẵn trong C++.

1 Khởi tạo mảng tham ban đầu là rỗng memset(tham,false,sizeof(tham));

2 Đánh dấu đỉnh u đã được thăm tham[u]=1;

3 Tạo một stack rỗng và nạp đỉnh u vào stack s; s.push(u);

4 while srỗng gắn đỉnh v là đỉnh đầu danh sách và xóa v ra khỏi stack s v=s.top(); s.pop(); cout

Ngày đăng: 14/11/2023, 05:24

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w