Thuật toán tìm một đường đi Euler trên đồ thị có hướng

Một phần của tài liệu Bài giảng Cấu trúc dữ liệu và giải thuật (2016): Phần 2 (Trang 73 - 76)

d) Cài đặt thuật toán

6.4.4. Thuật toán tìm một đường đi Euler trên đồ thị có hướng

Tìm một đường đi Euler cũng giống như tìm một chu trình Euler trên đồ thị có hướng. Điểm khác biệt duy nhất giữa hai thuật toán này là đỉnh xuất phát để tìm đường đi hay chu trình Euler. Đối với đồ thị Euler ta có thể xây dựng chu trình Euler tại bất kỳ đỉnh nào thuộc tập đỉnh. Đối với đồ thị là nửa Euler nhưng không là Euler, đỉnh để xây dựng đường đi Euler là m đỉnh u có deg+(u)-deg-(u) =1. Thuật toán kiểm tra một đồ thị có hướng liên thông yếu là nửa Euler nhưng không là Euler được thể hiện như Hình 5.13.

a) Biểu diễn thuật toán

Hình 5.13. Thuật toán kiểm tra đồ thị có hướng là nửa Euler.

Thuật toán Check_Semi_Euler(G=<V, E>): Begin

int s, t, dem1 = 0, dem2 = 0;

for each uV do //duyệt trên tập các đỉnh trong V

if ( deg+(u)- deg-(u)==1){ //tìm hiệu bán bậc của đỉnh u

dem1++; s =u; //ghi nhận đỉnh u có deg+(u)- deg-(u)==1

endif;

else if (deg-(u)- deg+(u)==1){

dem2++; t =u; //ghi nhận đỉnh u có deg-(u)- deg+(u)==1

endesle; endfor;

if (dem1==1 && dem2==1) //nếu điều này xảy ra

return true;//kết luận đồ thị là nửa euler

endif;

return false;//kết luận đồ thị không là nửa Euler

NGUYỄN DUY PHƯƠNG 198

b) Độ phức tạp thuật toán

Bạn đọc tự tìm hiểu và chứng minh độ phức tạp thuật toán trong các tài liệu tham khảo liên quan.

c) Kiểm nghiệm thuật toán

Bạn đọc tự tìm hiểu và kiểm nghiệm thuật toán trong các tài liệu tham khảo liên quan.

d) Cài đặt thuật toán

Thuật toán được cài đặt cho đồ thị vô hướng liên thông yếu được biểu diễn dưới dạng danh sách cạnh sau đó chuyển đổi biểu diễn thành dánh sách kề như dưới đây.

#include<iostream> #include <list> #include <fstream> #include <iomanip> #include <stack> using namespace std;

class Graph{ //định nghĩa lớp Graph

private:

int V; // tập đỉnh của đồ thị

list <int> *adj; // con trỏ đến mảng các danh sách kề

public:

Graph(int V); // constructor của lớp

void addEdge(int v, int w); // thêm một cạnh của đồ thị vào danh sách kề

int NegativeDeg(int u); //tìm bán đỉnh bậc vào của đỉnh u

void SemiEuler(void);//thuật toán Hình 5.13

void EulerCycle(int u); //thuật toán Hình 5.10

};

Graph::Graph(int V){//Constructor: khởi tạo đồ thị ban đầu

this->V = V;//thiết lập tập đỉnh

adj = new list<int>[V];//thiết lập V danh sách kề

}

void Graph::addEdge(int v, int w){

adj[v].push_back(w); //thêm đỉnh w vào danh sách kề đỉnh v. }

int Graph::NegativeDeg(int u){

int dem =0; list <int>::iterator v; for(int s=1; s<V; s++){

NGUYỄN DUY PHƯƠNG 199 for(v=adj[s].begin(); v != adj[s].end(); ++v){ for(v=adj[s].begin(); v != adj[s].end(); ++v){ if(u==(*v)) dem++; } } return dem; } void Graph::SemiEuler(){

int deg_u, deg_u1; //bán bậc ra và bán bậc vào của đỉnh

int dem1=0, dem2=0;//số lượng định thỏa mãn đồ thị semi-Euler

int s, t; //ghi nhận đỉnh có deg+(u)-deg-(u) =1 hoặc deg-(u)-deg+(u) =1 for(int u=1; u<V; u++){ //duyệt trên tập đỉnh V

deg_u = adj[u].size(); //tìm bán bậc ra của đỉnh

deg_u1 = NegativeDeg(u);//tìm bán bậc vào của đỉnh

if (deg_u - deg_u1 ==1) { //nếu deg+(u)-deg-(u) =1 dem1 ++; s = u; //ghi nhận số đỉnh và s = u

}

else if (deg_u1 - deg_u ==1){ //nếu deg-(u)-deg+(u) =1 dem2++; t = u; //ghi nhận số đỉnh và t =u

} }

if (dem1==1 && dem2==1) //nếu điều này xảy ra

EulerCycle(s); //ta làm giống như chu trình Euler

else cout<<"\n Đồ thị không là nửa Euler"; }

void Graph::EulerCycle(int u){//tìm chu trình Euler bắt đầu tại đỉnh u

//Bước 1 (Khởi tạo):

stack <int> stack; //tạo stack rỗng

int *CE = new int[V], k=0;//tạo mảng CE rỗng

stack.push(u); //đưa u vào ngăn xếp

//Bước 2 (Lặp):

while(!stack.empty()){//lặp đến khi stack rỗng

int s = stack.top(); //lấy s là đỉnh đầu ngăn xếp if(!adj[s].empty()){ //nếu ke(s) khác rỗng

int t= adj[s].front();//t là đỉnh đầu tiên trong ke(s) stack.push(t);//đưa t vào ngăn xếp

adj[s].remove(t);//loại t khỏi ke(s) adj[t].remove(s); //loại s khỏi ke(t)

NGUYỄN DUY PHƯƠNG 200 } }

else {//nếu ke(s) là rỗng

stack.pop();//loại s khỏi stack

CE[k]= s; k++;//đưa s sang CE

} }

//Bước 3 (Trả lại kết quả): cout<<"\n Kết quả:";

for(int t=k-1;t>=0; t--){//lật ngược lại CE

cout<<EC[t]<<"-"; }

cout<<endl; }

6.5. Bài toán xây dựng cây khung của đồ thị

Trong mục này ta đề cập đến một loại đồ thị đơn giản nhất đó là cây. Cây được ứng dụng rộng rãi trong nhiều lĩnh vực khác nhau của tin học như tổ chức các thư mục, lưu trữ dữ liệu, biểu diễn tính toán, biểu diễn quyết định và tổ chức truyền tin. Ta có thể tiếp cận cây bằng lý thuyết đồ thị như dưới đây.

Định nghĩa 1. Ta gọi cây là đồ thị vô hướng liên thông không có chu trình. Đồ thị không liên thông được gọi là rừng. Như vậy, rừng là đồ thị mà mỗi thành phần liên thông của nó là một cây.

Định lý 1. Giả sử T= <V, E> là đồ thị vô hướng n đỉnh. Khi đó những khẳng định sau là tương đương

a) T là một cây.

b) T không có chu trình và có n-1 cạnh. c) T liên thông và có đúng n-1 cạnh.

d) T liên thông và mỗi cạnh của nó đều là cầu.

e) Giữa hai đỉnh bất kỳ của T được nối với nhau bởi đúng một đường đi đơn. f) T không chứa chu trình nhưng hễ cứ thêm vào nó một cạnh ta thu được đúng

một chu trình.

Định nghĩa 2. Cho G =<V, E> là đồ thị vô hướng liên thông. Ta gọi đồ thị con H=<V,T> là một cây khung của G nếu H là một cây và TE.

Tiếp cận cây bằng lý thuyết đồ thị, người ta qua tâm đến hai bài toán cơ bản về cây:

Bài toán 1. Cho đồ thị vô hướng G =<V,E>. Hãy xây dựng một cây khung của đồ thị bắt đầu tại đỉnh uV.

Bài toán 2. Cho đồ thị vô hướng G =<V,E> có trọng số. Hãy xây dựng cây khung có độ dài nhỏ nhất.

Một phần của tài liệu Bài giảng Cấu trúc dữ liệu và giải thuật (2016): Phần 2 (Trang 73 - 76)

Tải bản đầy đủ (PDF)

(101 trang)