Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 36

Tiết 3

CÁC THUẬT TOÁN


TÌM KIẾM TRÊN ĐỒ
THỊ
Bài toán
 Cho đồ thị G = (V, E). u và v là hai đỉnh của G. Một
đường đi (path) độ dài p từ đỉnh s đến đỉnh f là dãy
x[0..p] thoả mãn x[0] = s, x[p] = f và (x[i], x[i+1])
∈ E với ∀i: 0 ≤ i < p.
 Đường đi nói trên còn có thể biểu diễn bởi dãy các
cạnh: (s = x[0], x[1]), (x[1], x[2]), …,(x[p-1], x[p] = f)
 Đỉnh u được gọi là đỉnh đầu, đỉnh v được gọi là đỉnh
cuối của đường đi. Đường đi có đỉnh đầu trùng với
đỉnh cuối gọi là chu trình (Circuit), đường đi không
có cạnh nào đi qua hơn 1 lần gọi là đường đi đơn,
tương tự ta có khái niệm chu trình đơn.
Bài toán
 Xét 2 đồ thị trong hình dưới đây

(1, 2, 3, 4) là đường đi đơn độ dài 3 từ đỉnh 1


tới đỉnh 4.
(1, 6, 5, 4) không phải đường đi vì không có
cạnh (cung) nối từ đỉnh 6 tới đỉnh 5.
3. Duyệt đồ thị
Giới thiệu
Với một đồ thị có nhiều nút, việc kiểm tra tính liên
thông của đồ thị là bài toán lớn, cần có cách thức
để thực hiện nhanh, chính xác.
Hai cách duyệt đồ thị phổ biến được áp dụng:
1. Duyệt đồ thị theo chiều sâu (Depth First Search -
DFS)
2. Duyệt đồ thị theo chiều rộng (Breadth First Search
- BFS)

DFS BFS
Phần 3.1

TÌM KIẾM THEO CHIỀU


SÂU (Depth First Search –
DFS)
3. 1 Duyệt đồ thị theo chiều
sâu
Một đỉnh v bất kỳ của đồ thị G, chúng ta
thực hiện như sau:

Đánh dấu v đã được duyệt.

Thực hiện đánh dấu


đã duyệt với mỗi đỉnh
w chưa duyệt kề với
v,
Làm lại bước 2 cho đến
khi tất cả các đỉnh được
duyệt. DFS
3. Duyệt đồ thị
3.2 Duyệt đồ thị theo chiều sâu
Ví dụ:
Thực hiện duyệt đồ thị theo chiều sâu trên đồ thị G
dưới đây:

v1 v2 v3 v4

v5 v6 v7 v8

Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8
Bảng duyệt v1 v2 v6 v3 v4 v8 v7 v5
Ví dụ về DFS

 Áp dụng DFS, hãy thể hiện thứ tự duyệt các


đỉnh trong đồ thị sau:
u

0 t v

s x

Đáp án: 0 1 2 3 4 9 5 6 7 8 10 Đáp án: t u s v


Đỉnh x không được duyệt
Lý thuyết đồ thị 05/18/20 8
Code pascal DFS
Const    maxn = 101;
Var    a : array [1..maxn,1..maxn] of boolean;
    free : array [1..maxn] of boolean;
    Q : array [1..maxn] of integer;
    n, m, s: integer;
    dau, cuoi : integer;

Procedure    init;
Begin
    fillchar(a,sizeof(a),false);
    fillchar(Free,sizeof(Free),true);
end;
Lý thuyết đồ thị 05/18/20 9
Code pascal DFS
Procedure    readf;
Var        i, u, v : integer;
Begin
    readln(n,m,s);
    for i := 1 to m do
        begin
            readln(u,v);   A[u,v] := true;   A[v,u] := true;
        end;
end;

Lý thuyết đồ thị 05/18/20 10


Code pascal DFS

Procedure    DFS(u : integer);
Var     v : integer;
Begin
writeln(u);
    Free[u] := false;
    
   For v := 1 to n do
If A[u,v] and Free[v] then
dfs(v); Trace[v] := u;
end;
{Lưu vết đường đi cũng là đánh dấu v đã thăm}

Lý thuyết đồ thị 05/18/20 11


Code pascal DFS
In ra những đỉnh đến được
từ s
Procedure    main; for v := 1 to n do
Var        i : integer; if Trace[v] <> 0 then
Write(fo, v, ', ');
Begin WriteLn(fo, ‘duong di tu', s, ' to ', f, ': ');
    init; if Trace[f] = 0 then
    readf; WriteLn(fo,’k co')
    DFS(s); else {s tới được f} begin
end; while f <> s do {Truy vết đường đi}
begin
BEGIN Write(fo, f, '<-’); f := Trace[f];
    main; end;
END. WriteLn(fo, s);
Lý thuyết đồ thị 05/18/20 12
Code C++ DFS: Danh sách kề
#include <stdio.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

#define N 100005
int n, m, cnt;
vector<int> a[N];
int Visited[N], Parent[N];

Lý thuyết đồ thị 05/18/20 13


Code C++ DFS: Danh sách kề
void visit(int u) {
    cout << "Visiting " << u << endl;
    Visited[u] = ++cnt; // travesal time
    for (int i = 0; i < a[u].size(); i++) {
        int v = a[u][i];
        if (v != Parent[u]) {
            if (!Visited[v]) {
                Parent[v] = u;
                visit(v);
            }
        }
    }
} Lý thuyết đồ thị 05/18/20 14
Code C++ DFS: Danh sách kề
int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int p, q;
        cin >> p >> q;
        /*do {
            p=rand()%n+1;
            q=rand()%n+1;
        } while (p==q);
        cout << p << " " << q << endl;*/
        a[p].push_back(q);
        a[q].push_back(p);
    }
    for (int i = 1; i <= n; i++) if (!Visited[i]) visit(i);
    cout << "Travesal time: " << endl;
    for (int i = 1; i <= n; i++) cout << Visited[i] << " ";
    cout << endl;
    cin.ignore(2);
} Lý thuyết đồ thị 05/18/20 15
Phần 3.2

TÌM KIẾM THEO CHIỀU


RỘNG (Breadth First
Search - BFS)
3. 2 Duyệt đồ thị theo chiều
rộng
Một đỉnh v bất kỳ của đồ thị G, chúng ta thực hiện như sau

Đánh dấu đã duyệt cho một đỉnh v


bất kỳ.
Chọn đỉnh v đã được duyệt nhưng
có đỉnh kề chưa được duyệt. Việc
chọn đỉnh v được xét ưu tiên cho
các đỉnh được đánh dấu duyệt sớm.

Thực hiện đánh dấu đã duyệt với


tất cả các đỉnh w kề với v, .
BFS
Làm lại bước 2 cho đến khi tất cả
các đỉnh được duyệt
3. Duyệt đồ thị
3.2 Duyệt đồ thị theo chiều rộng
3. Duyệt đồ thị
3.2 Duyệt đồ thị theo chiều rộng
Ví dụ:
Thực hiện duyệt đồ thị theo chiều rộng trên đồ thị G
dưới đây:

v1 v2 v3 v4

v5 v6 v7 v8

Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8
Bảng duyệt v1 v2 v5 v6 v3 v7 v4 v8
Cài đặt BFS (tt)
1 2 3
 Đưa 1 vào Queue
 Lấy 1 ra xử lý, đưa 5, 4, 2 vào
Queue
 Lấy 2 ra xử lý, đưa 5, 3 vào Queue
 Lấy 4 ra xử lý, đưa 5 vào Queue
4 5 6
6
 Lấy 5 ra xử lý, đưa 3 vào Queue 3
 Lấy 3 ra xử lý. Đưa 6 vào Queue 5
 Lấy 5 ra. Không xử lý (vì đã xử lý 5 Queue
rồi)
3
 Lấy 5 ra. Không xử lý
5
 Lấy 3 ra. Không xử lý
4
 Lấy 6 ra xử lý. Không đưa gì vào
1
2
Queue
Thứ tự duyệt: 1 2 4 5 3 6
Lý thuyết đồ thị 05/18/20 21
Ví dụ về BFS

 Áp dụng BFS, hãy thể hiện thứ tự duyệt các


đỉnh trong đồ thị sau:
u

0 t v

s x

Đáp án: 0 1 3 9 2 4 5 6 8 10 7 Đáp án: t u s v


Đỉnh x không được duyệt
Lý thuyết đồ thị 05/18/20 22
Code Pascal BFS
Const    maxn = 101;
Var    a : array [1..maxn,1..maxn] of boolean;
    free : array [1..maxn] of boolean;
    Q : array [1..maxn] of integer;
    n, m, s: integer;
    dau, cuoi : integer;
Procedure    readf;
Var        i, u, v : integer;
Begin
    readln(n,m,s);
    for i := 1 to m do        begin
            readln(u,v);  A[u,v] := true; A[v,u] := true;
        end;
Lý thuyết đồ thị 05/18/20 23
end;
Code Pascal BFS
Procedure    init;
Begin
    fillchar(a,sizeof(a),false);
    fillchar(Free,sizeof(Free),true);
    dau:=1; cuoi:=0;
end;

Procedure Push(u:integer); Function  Pop : integer;


begin Begin
    inc(cuoi);     Pop := Q[dau];
    Q[cuoi] := u;     inc(dau);
end; end;
Lý thuyết đồ thị 05/18/20 24
Code Pascal BFS
Procedure    BFS(i : integer);
Var     u, v : integer;
Begin
    Push(i);
    Free[i] := false;
    While dau<=cuoi do begin
            u := Pop;           writeln(u);
            For v := 1 to n do
                If A[u,v] and Free[v] then begin
                        Push(v);         Free[v] := false;
                 end;
     end;
end;
Lý thuyết đồ thị 05/18/20 25
Code Pascal BFS

Procedure    main;
Var        i : integer;
Begin
    init;
    readf;
    BFS(s);
end;
BEGIN
    main;
END.

Lý thuyết đồ thị 05/18/20 26


Code C++ BFS

#include <bits/stdc++.h>
using namespace std;
 
int a[101][101];
queue <int> q;
 
int n,m,Free[101], u,v,s;

Lý thuyết đồ thị 05/18/20 27


Code C++ BFS
void BFS(int s)
{    q.push(s);
     Free[s]=0;
     while (!q.empty())     {
        int u = q.front();
        q.pop();
         cout << u << endl;
         for (int v=1; v<=n; v++)
            if (Free[v] && a[u][v]==1) {                
Free[v] = 0;
                q.push(v);
            }
     }
} Lý thuyết đồ thị 05/18/20 28
Code C++ BFS
int main(){
cin >> n >> m>> s;
for (int i=1; i<=n; i++)
        for (int j=1; j<=n; j++)
            a[i][j]=0;
 for (int i=1; i<=m; i++){
        cin >> u>> v;
        a[u][v]=1;        a[v][u]=1;
    }
for (int i=1; i<=n; i++)
        Free[i]=1;
      BFS(s);
 return 0;
Lý thuyết đồ thị 05/18/20 29
Code C++ BFS: Danh sách kề

#include <stdio.h>
#include <vector>
#include <queue>
using namespace std;

vector<int> a[2309];
int d[2309]; //one-based
int n, m;

Lý thuyết đồ thị 05/18/20 30


Code C++ BFS: Danh sách kề
void bfs() {
    int u, i, v;
    queue<int> qu;
    qu.push(1);
    d[1] = 1;

    while (qu.size()) {
        u = qu.front();
        qu.pop();

        for (int i = 0; i < a[u].size(); i++) {
            int v = a[u][i];
            if (d[v] == 0) {
                d[v] = d[u] + 1;
                qu.push(v);
            }
        }
    }
}thuyết đồ thị
Lý 05/18/20 31
Code C++ BFS: Danh sách kề
int main() {
    scanf("%d%d", &n, &m);
    while (m--) {
        int p, q;
        scanf("%d%d", &p, &q);
        a[p].push_back(q);
        a[q].push_back(p); // remove it in one-
directional graph
    }
    bfs();
    printf("   i : ");
    for (int i = 1; i <= n; i++)
        printf("%3d,", i);
    printf("\n");
    printf("d[i] : ");
    for (int i = 1; i <= n; i++)
        printf("%3d,", d[i]);
    printf("\n");
} Lý thuyết đồ thị 05/18/20 32
Phần 3.3

ỨNG DỤNG CÁC THUẬT


TOÁN TÌM KIẾM TRÊN ĐỒ
THỊ
Tìm đường đi

2 4 GRAPH.INP GRAPH.OUT
6
8715 1, 2, 3, 5, 4, 6
12 5<- 3<-2<-1
1 13
7 23
24
35
8 46
78
3 5

Lý thuyết đồ thị 05/18/20 34


Kiểm tra liên thông (tt)

GRAPH.INP GRAPH.OUT
2 4
6 8715 1, 2, 3, 5, 4, 6
12 5<- 3<-2<-1
13
1 23
7 24
35
8 46
3 5 78

Cho đồ thị như hình bên, đếm số


thành phần liên thông của đồ thị
Ứng dụng của BFS và DFS

 Xây dựng cây khung


 Tìm tập các chu trình cơ sở của đồ
thị
 Bài toán định chiều đồ thị
 Liệt kê các khớp và cầu của đồ thị
 Các thành phần song liên thông

Lý thuyết đồ thị 05/18/20 36


Áp dụng DFS để tìm đường đi
#include <stdio.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

#define N 100005
int n, m, cnt;
vector<int> a[N];
int Visited[N], Parent[N];

void visit(int u) {
cout << "Visiting " << u << endl;
Visited[u] = ++cnt; // travesal time
for (int i = 0; i < a[u].size(); i++) {
int v = a[u][i];
if (v != Parent[u]) {
if (!Visited[v]) {
Parent[v] = u;
visit(v);
}
}
}
}

int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int p, q;
cin >> p >> q;
/*do {
p=rand()%n+1;
q=rand()%n+1;
} while (p==q);
cout << p << " " << q << endl;*/
a[p].push_back(q);
a[q].push_back(p);
}
for (int i = 1; i <= n; i++) if (!Visited[i]) visit(i);
cout << "Travesal time: " << endl;
for (int i = 1; i <= n; i++) cout << Visited[i] << " ";
cout << endl;
cin.ignore(2);
}

You might also like