Đồ thị có thể được sử dụng để mô hình hóa nhiều loại quan hệ và cấu trúc trong khoa học tự nhiên và xã hội, như mạng lưới giao thông, mạng xã hội, mạng máy tính, mạng thông tin, v.v.Môn
Trang 1TRƯỜNG ĐẠI HỌC TÀI NGUYÊN VÀ MÔI TRƯỜNG
THÀNH PHỐ HỒ CHÍ MINH
KHOA HỆ THỐNG THÔNG TIN VÀ VIỄN THÁM
TIỂU LUẬN
LÝ THUẬT ĐỒ THỊ
DUYỆT ĐỒ THỊ THEO CHIỀU RỘNG BFS
Lớp : 11_ĐH_HTTT
Khoá : 2022– 2026
Giảng viên hướng dẫn : ThS Lê Tuấn Thu
TP Hồ Chí Minh, tháng 3 n ăm 2024
Trang 2DACH SÁCH TÊN CÁC THÀNH VIÊN NHÓM 5
1 Nguyễn Phước Thịnh (aka Thịnh Suy) 1150070040
Trang 3MỤC LỤC
MỤC LỤC 3
MỞ ĐẦU 1
Chương 1: TỔNG QUAN 2
1.1 Lý do chọn đề tài 2
1.2 Đối tượng và phạm vi nghiên cứu 2
Chương 2: DUYỆT ĐỒ THỊ THEO CHIỀU RỘNG BFS 3
2.1 Khái niệm và cấu trúc dữ liệu 3
2.2 Soucre code của duyệt đồ thị theo chiều rộng sử dụng danh sách kề 3
2.3 Kết quả 3
TÀI LIỆU THAM KHẢO 7
Trang 4MỞ ĐẦU
Lý thuyết đồ thị là một nhánh của toán học nghiên cứu các đối tượng gọi là đồ thị, bao gồm các đỉnh (hay còn gọi là nút) và các cạnh (hay còn gọi là đường nối) giữa chúng Đồ thị có thể được sử dụng để mô hình hóa nhiều loại quan hệ và cấu trúc trong khoa học tự nhiên và xã hội, như mạng lưới giao thông, mạng xã hội, mạng máy tính, mạng thông tin, v.v
Môn học Lý Thuyết Đồ Thị giúp sinh viên nắm vững các khái niệm cơ bản, các định lý và các thuật toán quan trọng trong lý thuyết đồ thị, như đồ thị liên thông, đồ thị Euler, đồ thị Hamilton, đồ thị phẳng, đồ thị hai phía, đồ thị phân chia, đồ thị cây, mã hoá Huffman, thuật toán Dijkstra, thuật toán Prim, thuật toán Kruskal, v.v Môn học cũng giúp sinh viên phát triển kỹ năng phân tích và giải quyết các bài toán thực tế có liên quan đến lý thuyết đồ thị, như bài toán du lịch, bài toán phân công công việc, bài toán tô màu bản đồ, v.v
Môn học Lý Thuyết Đồ Thị yêu cầu sinh viên có kiến thức nền tảng về toán rời rạc và lập trình Môn học bao gồm các bài giảng lý thuyết, các bài tập lớn và các bài kiểm tra Sinh viên sẽ được đánh giá dựa trên điểm số của các bài tập lớn và các bài kiểm tra
Trang 1
Trang 5Chương 1: TỔNG QUAN 1.1 Lý do chọn đề tài
Nhóm chúng em quyết định chọn đề tài này vì nhận thấy được các mặt lợi ích của duyệt đồ thị theo chiều rộng BFS bao gồm các tiêu chí sau: “tầm quan trọng” , “tính ứng dụng cao”, “dễ hiểu và dễ triển khai”, “hiểu quả và linh hoạt”…
-Về tầm quan trọng, duyệt đồ thị theo chiều rộng (BFS) là một thuật toán cơ bản và thiết yếu trong khoa học máy tính, được sử dụng để giải quyết nhiều bài toán liên quan đến đồ thị BFS có nhiều ứng dụng thực tế trong nhiều lĩnh vực như mạng lưới máy tính, mạng xã hội, bản đồ, v.v và việc hiểu rõ về BFS giúp nâng cao tư duy logic và khả năng giải quyết vấn đề của người học
-Về tính ứng dụng cao, thì BFS có thể được sử dụng để giải quyết nhiều bài toán khác nhau như: tìm kiếm đường đi ngắn nhất giữa hai điểm trong một mạng lưới, kiểm tra tính liên thông của một đồ thị, tìm chu trình Euler trong một đồ thị, tìm tất cả các đỉnh
có thể đi đến từ một đỉnh cho trước, xác định thành phần liên thông của một đồ thị -Về việc dễ hiểu và dễ triển khai, thì so với các thuật toán duyệt đồ thị khác, BFS có cách thức hoạt động đơn giản và dễ hiểu, BFS có thể được triển khai dễ dàng bằng nhiều ngôn ngữ lập trình khác nhau như C/C++ , C#
-Về sự hiểu quả và linh hoạt, BFS có hiệu quả cao trong nhiều trường hợp và có thể được mở rộng để giải quyết các bài toán phức tạp hơn
1.2 Đối tượng và phạm vi nghiên cứu
*Đối tượng nghiên cứu:
- Thuật toán duyệt đồ thị theo chiều rộng bằng danh sách kề
*Phạm vi nghiên cứu:
- Các khái niệm cơ bản, các thuật toán nằm trong vùng kiến thức của môn Lý Thuyết
Đồ Thị và ứng dụng
- Mô tả thuật toán bằng ngôn ngữ lập trình C++
Trang 2
Trang 6Chương 2: DUYỆT ĐỒ THỊ THEO CHIỀU RỘNG BFS 2.1 Khái niệm
- Đồ thị: Đồ thị được định nghĩa là một tập hợp các đỉnh (vertex) và các cạnh (edge) nối các đỉnh với nhau, các loại đồ thị bao gồm đồ thị vô hướng (undirected graph) và
đồ thị có hướng (directed graph), phụ thuộc vào cách các cạnh kết nối Các khái niệm
cơ bản trong đồ thị bao gồm đỉnh kề (adjacent vertex), bậc của một đỉnh (degree of a vertex), đồ thị con (subgraph), đường đi (path), chu trình (cycle), và tính liên thông (connectivity)
-Danh sách kề: Danh sách kề chứa một danh sách các đỉnh kề với mỗi đỉnh trong đồ thị Quá trình hiện thực bao gồm khởi tạo một danh sách có kích thước bằng số lượng đỉnh Duyệt qua danh sách cạnh và thêm các đỉnh kề vào danh sách tương ứng với đỉnh xuất phát của cạnh
-Duyệt theo chiều rộng (Breadth-First Search - BFS) là một trong những thuật toán cơ bản và quan trọng trong lý thuyết đồ thị BFS bắt đầu từ một đỉnh nguồn và duyệt qua tất cả các đỉnh có thể đạt được từ nguồn theo chiều rộng của đồ thị Đây là một phương pháp tìm kiếm không đệ quy, sử dụng cấu trúc dữ liệu hàng đợi để lưu trữ các đỉnh cần duyệt
- Ý tưởng chính của BFS: BFS duyệt các đỉnh theo cấp độ từ đỉnh nguồn Đầu tiên, nó thăm đỉnh nguồn, sau đó là tất cả các đỉnh kề với nguồn, tiếp theo là các đỉnh kề với các đỉnh đã thăm, và cứ tiếp tục như vậy cho đến khi tất cả các đỉnh trong thành phần liên thông với đỉnh nguồn được thăm
-Cách thức hoạt động của duyệt đồ thị theo chiều rộng BFS:
Khởi tạo: Đánh dấu tất cả các đỉnh là chưa được thăm và tạo một hàng đợi Q Bắt đầu từ đỉnh nguồn: Chọn một đỉnh nguồn, đánh dấu là đã thăm và đưa vào hàng đợi Q
Duyệt đồ thị:
o Lấy đỉnh đầu tiên trong Q ra và xem xét nó
o Duyệt qua tất cả các đỉnh kề với đỉnh đang xét
o Nếu một đỉnh kề chưa được thăm, đánh dấu nó là đã thăm và đưa vào Q
o Lặp lại quá trình trên cho đến khi hàng đợi Q rỗng
Cấu trúc dữ liệu
1 Cấu trúc Graph biểu diễn đồ thị: Cấu trúc Graph được sử dụng để biểu diễn đồ thị trong chương trình Cấu trúc này bao gồm hai thành phần chính:
adj Mảng hai chiều lưu trữ danh sách các đỉnh kề với mỗi đỉnh
n: Biến lưu trữ số lượng đỉnh của đồ thị
Ví dụ:
C++
struct Graph{
vector<vector<int>> adj;
int n; // lưu đỉnh
Graph(int n) {
this->n = n;
adj.resize(n + 1);
Trang 3
Trang 7void addEdge(int u, int v){
adj[u].push_back(v);
adj[v].push_back(u);
}
};
2.Cấu trúc vector: Cấu trúc vector là một mảng động có thể thay đổi kích thước Cấu trúc này được sử dụng để lưu trữ danh sách các đỉnh kề với mỗi đỉnh trong mảng adj của cấu trúc Graph
-Đặc điểm:
Kích thước động: Kích thước của vector có thể được thay đổi khi cần thiết, không cần khai báo kích thước ban đầu
Truy cập hiệu quả: Truy cập các phần tử trong vector rất hiệu quả, có thể truy cập trực tiếp bằng chỉ số
Hỗ trợ nhiều kiểu dữ liệu: Vector có thể lưu trữ bất kỳ kiểu dữ liệu nào, bao gồm kiểu dữ liệu cơ bản và kiểu dữ liệu do người dùng định nghĩa
Ví dụ: Mã code minh họa sử dụng vector trong cấu trúc Graph:
C++
#include <iostream>
#include <vector>
using namespace std;
// Khai báo cấu trúc Graph
struct Graph {
vector<vector<int>> adj;
int n; // lưu đỉnh
Graph(int n) {
this->n = n;
adj.resize(n + 1);
}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
};
// Hàm duyệt đồ thị theo chiều rộng (BFS)
void BFS(Graph& g, int x) {
// Khai báo hàng đợi
queue<int> q;
// Đánh dấu đỉnh xuất phát đã được duyệt
vector<bool> visited(g.n + 1, false);
visited[x] = true;
Trang 4
Trang 8// Thêm đỉnh xuất phát vào hàng đợi
q.push(x);
//Duyệt cho đến khi hàng đợi rỗng
while (!q.empty()) {
// Lấy đỉnh đầu tiên ra khỏi hàng đợi
int u = q.front();
q.pop();
// In ra đỉnh u
cout << u << " ";
// Duyệt qua các đỉnh kề với u
for (int v : g.adj[u]) {
// Nếu đỉnh v chưa được duyệt và v hợp lệ
if (!visited[v] && v <= g.n) {
// Đánh dấu đỉnh v đã được duyệt
visited[v] = true;
// Thêm đỉnh v vào hàng đợi
q.push(v);
}
};
3.Cấu trúc Queue: Queue là một cấu trúc dữ liệu theo nguyên tắc "first-in-first-out" (FIFO) trong C++ Nó hoạt động như một hàng đợi, nơi các phần tử được thêm vào một đầu và lấy ra từ đầu kia
-Đặc điểm:
FIFO: Các phần tử được thêm vào đầu tiên sẽ được lấy ra đầu tiên Hiệu quả: Thêm và xóa phần tử rất hiệu quả
Hỗ trợ nhiều kiểu dữ liệu: Queue có thể lưu trữ bất kỳ kiểu dữ liệu nào
Ví dụ: Mã code minh họa sử dụng queue trong thuật toán BFS:
C++
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// Khai báo cấu trúc Graph
struct Graph {
vector<vector<int>> adj;
int n; // lưu đỉnh
Graph(int n) {
this->n = n;
adj.resize(n + 1);
Trang 5
Trang 9}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
};
// Hàm duyệt đồ thị theo chiều rộng (BFS) void BFS(Graph& g, int x) {
// Khai báo hàng đợi
queue<int> q;
// Đánh dấu đỉnh xuất phát đã được duyệt vector<bool> visited(g.n + 1, false);
visited[x] = true;
// Thêm đỉnh xuất phát vào hàng đợi q.push(x);
// Duyệt cho đến khi hàng đợi rỗng
while (!q.empty()) {
// Lấy đỉnh đầu tiên ra khỏi hàng đợi int u = q.front();
q.pop();
// In ra đỉnh u
cout << u << " ";
// Duyệt qua các đỉnh kề với u
for (int v : g.adj[u]) {
// Nếu đỉnh v chưa được duyệt và v hợp lệ
if (!visited[v] && v <= g.n) {
// Đánh dấu đỉnh v đã được duyệt
visited[v] = true;
// Thêm đỉnh v vào hàng đợi
q.push(v); }
}
}
};
int main() {
// Nhập số lượng đỉnh và cạnh
int n, m;
cout << "nhập số lượng đỉnh và cạnh: "; cin >> n >> m;
// Tạo đồ thị Graph g(n);
cout << "nhập các cạnh: "; // Nhập các cạnh for (int i = 0; i < m; i++)
Trang 6
Trang 10{
int u, v;
cin >> u >> v;
g.addEdge(u, v);
}
// Nhập đỉnh xuất phát int x;
cout << "nhập đỉnh xuất phát: ";
cin >> x;
Trang 7