Bài giảng Kỹ thuật lập trình - Chương 4: Một số cấu trúc dữ liệu và giải thuật căn bản - Lương Mạnh Bá
Một mô tả/định nghĩa về một đối tượng gọi là đệ qui nếu trong mô tả/định nghĩa đó ta lại sử dụng chính đối tượng này.
Tức là mô tả đối tượng qua chính nó.
Mô tả đệ qui tập sốtựnhiên N :
Số1 là sốtựnhiên ( 1 -N)
Sốtựnhiên bằng sốtựnhiên cộng 1.
Mô tả đệ qui cấu trúc ds(list) kiểu T :
Cấu trúc rỗng là một ds kiểu T.
Ghép nối một thành phần kiểu T(nút kiểu T ) với một ds kiểu T ta có một ds kiểu T.
Mô tả đệ qui cây gia phả: Gia phả của một người bao gồm người đó và gia phả của cha và gia phả của mẹ
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Chương 4: Một số cấu trúc dữ liệu và giải thuật căn bản - Lương Mạnh Bá", để tải tài liệu gốc về máy hãy click vào nút Download ở trên.
File đính kèm:
- bai_giang_ky_thuat_lap_trinh_chuong_4_mot_so_cau_truc_du_lie.ppt
Nội dung text: Bài giảng Kỹ thuật lập trình - Chương 4: Một số cấu trúc dữ liệu và giải thuật căn bản - Lương Mạnh Bá
- 1. Đệ qui 1.1 Khái niệm về đệ qui 1.2 Các loại đệ qui 1.3 Mô tả đệ qui các cấu trúc dữ liệu 1.4 Mô tả đệ qui các giải thuật 1.5 Các dạng đệ qui đơn giản thường gặp Last update 8-2010 SE-SoICT KTLT 4-1.2
- Ví dụ Định nghĩa không đệ qui n!: n! = n * (n-1) * * 1 Định nghĩa đệ qui: n! = 1 nếu n=0 n * (n-1)! nếu n>0 Mã C++: int factorial(int n) { if (n==0) return 1; else return (n * factorial(n - 1)); } Mô tả đệ qui thủ tục sắp tăng dãy a[m:n] ( dãy a[m], a[m+1], . . . , a[n] ) bằng phương pháp Sort_Merge (SM): SM (a[m:n]) ≡Merge ( SM(a[m : (n+m) div 2]) , SM (a[(n+m) div 2 +1 : n] ) Với : SM (a[x : x]) là thao tác rỗng (không làm gì cả). Merge (a[x : y] , a[(y+1) : z]) là thủ tục trộn 2 dãy tăng a [x : y] , a[(y+1) : z] để được một dãy a[x : z] tăng. Last update 8-2010 SE-SoICT KTLT 4-1.4
- Giải thuật đệ qui Nếu ta có 1 lời giải S cho bài toán P, ta lại sử dụng lời giải ấy cho bài toán P’ giống P nhưng kích cỡ nhỏ hơn thì lời giải S đó gọi là lời giải đệ qui. Biểu diễn giải thuật đệ qui P P[ S , P ] Điều kiện dừng Biểu diễn tổng quát P if B P[ S , P ] hoặc P P[ S , if B P ] Chương trình con đệ qui: Khi ta cài đặt giải thuật đệ qui, ta có chương trình đệ qui (tự nó gọi lại chính nó: P =>P’) –Hàm đệ qui –Thủ tục đệ qui Last update 8-2010 SE-SoICT KTLT 4-1.6
- Các dạng đệ qui đơn giản thường gặp Đệqui tuyến tính: là dạng đệqui trực tiếp đơn giản nhất có dạng P () { If (B) thực hiện S; else { thực hiện S* ; gọi P } } Với S , S* là các thao tác không đệqui . Vídụ:Hàm FAC(n) tính số hạng n của dãy n! Dạng hàm trong ngôn ngữ mã giả: { Nếu n = 0 thì FAC = 1 ; /* trường hợp neo*/ Ngược lại FAC = n*FAC(n-1) } Dạng hàm trong C++ : int FAC( int n ) { if ( n == 0 ) return 1 ; else return ( n * FAC(n-1 )) ; } Last update 8-2010 SE-SoICT KTLT 4-1.8
- Trạng thái hệ thống khi thi hành hàm tính giai thừa Stack hệ thống factorial(0) factorial(1) factorial(1) factorial(1) factorial(2) factorial(2) factorial(2) factorial(2) factorial(2) factorial(3) factorial(3) factorial(3) factorial(3) factorial(3) factorial(3) factorial(3) t Thời gian hệ thống Trả về từ Trả về từ Trả về từ Trả về từ Gọi hàm Gọi hàm Gọi hàm Gọi hàm hàm hàm hàm hàm factorial(3) factorial(2) factorial(1) factorial(0) factorial(0) factorial(1) factorial(2) factorial(3) t Last update 8-2010 SE-SoICT KTLT 4-1.10
- Các dạng đệ qui đơn giản thường gặp (tiếp) Đệqui phi tuyến: là đệ qui trực tiếp mà lời gọi đệ qui được thực hiện bên trong vòng lặp. P () { for ( to ) { thực hiện S ; if ( thỏa điều kiện dừng ) then thực hiện S*; else gọi P;} } Với S , S* là các thao tác không đệqui . Vídụ: Cho dãy { An } xác định theo công thức truy hồi : A0= 1 ; An = n2A0+(n-1)2A1+ . . . + 22An-2+ 12An-1 Dạng hàm đệ qui tính An trên ngôn ngữC++ là: int A( int n ) { if ( n == 0 ) return 1 ; else { int tg = 0 ; for (int i = 0 ; i<n ; i++ ) tg = tg + sqr(n-i) *A(i); return ( tg ) ; } Last update 8-2010 SE-SoICT KTLT 4-1.12
- 3 bước (tiếp) Phân rã bài toán tổng quát theo phương thức đệ qui Phân rã bài toán thành các bài toán giống BT ban đầu (ĐQ) song có kích thước nhỏ hơn và các BT khác (có thể có hoặc không). Đây là trường hợp đặc biệt của phương pháp Thiết kế Top-Down! Vídụ FAC(n) = n * FAC(n -1) . Tmax(a[1:n]) = max(Tmax(a[1:(n-1)]) , a[n] ) Last update 8-2010 SE-SoICT KTLT 4-1.14
- Bài toán Tháp Hà nội Luật: Di chuyển mỗi lần một đĩa Không được đặt đĩa lớn lên trên đĩa nhỏ Với chồng gồm n đĩa cần 2n-1 lần chuyển –Giả sử thời gian để chuyển 1 đỉa là t giây thì thời gian để chuyển xong chồng 64 đĩa sẽ là: –T = ( 264-1) * t = 1.84 1019 t –Với t = 1/100 s thì T = 5.8*109 năm = 5.8 tỷ năm . Last update 8-2010 SE-SoICT KTLT 4-1.16
- Bài toán Tháp Hà nội Giải bài toán bằng đệqui Thông số hóa bài toán Xét bài toán ở mức tổng quát nhất : chuyển n (n>=0) đĩa từ cột A sang cột B lấy cột C làm trung gian . THN(n ,A ,B,C) -> với 64 đĩa gọi THN(64,A ,B,C) n sẽ là thông số quyết định bài toán –n là tham số điều khiển Trường hợp suy biến vàcách giải Với n =1 : THN (1,A,B,C) Giải thuật giải bt THN (1,A,B,C) là thực hiện chỉ 1 thao tác cơ bản : Chuyển 1 đĩa từ A sang B (ký hiệu là Move (A , B) ) . THN(1,A,B,C) ≡{ Move( A, B ) } . THN(0, A,B,C) ≡{ φ} Last update 8-2010 SE-SoICT KTLT 4-1.18
- Bài toán Tháp Hà nội – Mã C++ void move(int count, int start, int finish, int temp) { if (count > 0) { move(count − 1, start, temp, finish); cout << "Move disk " << count << " from " << start << " to " << finish << "." << endl; move(count − 1, temp, finish, start); } } Last update 8-2010 SE-SoICT KTLT 4-1.20
- Bài toán chia phần thưởng Có 100 phần thưởng đem chia cho 12 học sinh giỏi đã được xếp hạng. Có bao nhiêu cách khác nhau để thực hiện cách chia? Tìm giải thuật giải bài toàn bằng phương pháp đệquy. Last update 8-2010 SE-SoICT KTLT 4-1.22
- Các trường hợp suy biến m = 0 : mọi học sinh đều nhận được 0 phần thưởng . PART(0 , n ) = 1 với mọi n n = 0 , m 0 . Phân rã bài toán trong trường hợp tổng quát m m thìPART(m , n ) = PART(m , m ) m>=n: là tổng Học sinh cuối cùng không có phần thưởng PART(m , n -1 ) Học sinh cuối cùng có ít nhất 1 PART(m -n , n ) Vậy: m > n => PART(m , n ) = PART(m , n -1 ) + PART(m -n , n ) Last update 8-2010 SE-SoICT KTLT 4-1.24
- Bài toán tìm tất cả hoán vị của một dãy các phần tử (giảng lướt) Thông số hóa bài toán. Gọi HV(v, m ) v : array[1 . . N ] of T m :integer ; m <= N T là một kiểu dữ liệu Vídụ: N = 4 , A[1] = 1 , A[2] = 2 , A[3] = 3 , A[4] = 4 thì lời gọi HV(A ,3 ) xuất tất cả hoán vị của A có được bằng cách hoán vị 3 phần tử đầu (có 6 h vị) : 1 2 3 4 1 3 2 4 3 2 1 4 2 1 3 4 3 1 2 4 2 3 1 4 Trường hợp neo Với m = 1 : HV(v,1): xuất v HV(v,1) ≡Display(v) //for (k= 1 to N do) Display(v[k]) Last update 8-2010 SE-SoICT KTLT 4-1.26
- Bài toán tìm tất cả hoán vị của một dãy các phần tử HV(V,m) ≡{ SWAP( V[m],V[m] ) ; HV(V,m –1) ; SWAP( V[m],v[m-1] ) ; HV(V,m –1) ; SWAP( V[m],v[m-2 ] ) ; HV(V,m –1) ; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . SWAP (V[m],v[2] ) ; HV(V,m –1) ; SWAP( V[m],v[1] ) ; HV(V,m –1) ; } SWAP(x , y ) là thủ tục hoán đổi giá trị của 2 đối tượng dữ liệu x ,y ) Vậy :HV(V , m ) ≡ for (k = m;k>0; k ) { SWAP( V[m], V[k] ) ; HV(V,m –1) ; } ; Last update 8-2010 SE-SoICT KTLT 4-1.28
- Lời giảiĐệ qui khác của bài toán Hoán vị (giảng cho SV) #include #include #include #define STOP 27 // phim esc const MAX =100; int a[MAX]; char b[MAX];// mang danh dau int shv, n; Hoanvi (int k) { int i,j; for (i=1;i 1 hoan vi shv ++; printf("\n Hoan vi %2d la : ", shv); for (j=1; j <=n;j++) printf("%d", a[j]); } else // goi tang len 1 so Hoanvi (k+1); b[i] = 1; } return 0; } Last update 8-2010 SE-SoICT KTLT 4-1.30
- KHỬ ĐỆ QUI 1 Cơ chế thực hiện đệ qui 2 Tổng quan về khử đệ qui 3 Các trường hợp khử đệ qui đơn giản Last update 8-2010 SE-SoICT KTLT 4-1.32
- Xét giải thuật giai thừa –Giải thuật FAC ( n ) ≡if(n = 0 ) retrun 1; else retrun ( n * FAC (n –1)); –Sơ đồ thực hiện Last update 8-2010 SE-SoICT KTLT 4-1.34
- Last update 8-2010 SE-SoICT KTLT 4-1.36
- Tạo ngăn xếp S –Thủ tục Creatstack(S) : Tạo S rỗng . –Thủ tục Push(x,S) : thêm x vào đỉnh stack S •( x là dữ liệu kiểu đơn giản giản hoặc có cấu trúc ) –Thủ tục Pop(x,S) : Lấy giá trị đang lưu ở đỉnh S •Lưu trữ vào x •Loại bỏ giá trị này khỏi S ( lùi đỉnh S xuống một mức ) –Hàm Empty(S): (kiểu boolean) Kiểm tra tính rỗng của S : cho giá trị đúng nếu S rỗng , sai nếu không. Last update 8-2010 SE-SoICT KTLT 4-1.38
- void Push(stack *ps , int x) { if (ps->top ==MAX -1) { printf(“\n stack full”); return; } ps->top +=1 ; ps->nodes[ps->top] =x; } int Pop(stack *ps) { if(Empty(ps) { printf(“\n stack is empty”); return (0); } return(ps->nodes[ps->top ]); Last }update 8-2010 SE-SoICT KTLT 4-1.40
- 1.Khử đệ qui bằng vòng lặp A. Hàm tính gía trị của dãy dữ liệu mô tả bằng hồi qui •Ý tưởng –Xét một vòng lặp trong đó sử dụng 1 tập hợp biến W = (V , U ) i) Tập hợp U các biến bị thay đổi ii) V là các biến còn lại. –Dạng tổng quát của vòng lặp là: W = Wo ; { Wo = ( Uo,Vo) } while C(U) do U := g(W) –Gọi Uo là trạng thái của U ngay trước vòng lặp –Uk với k >0 là trạng thái của U sau lần lặp thứ k (giả sử còn lặp đến lần k ) Uo mang các giá trị được gán ban đầu Uk= g(W) = g(Uk-1, Vo) = f(Uk-1) với k = 1 n Với n là lần lặp cuối cùng , tức C(Uk ) đúng với mọi k < n , C(Un) sai –Sau vòng lặp W mang nội dung (Un ,Vo ) . Last update 8-2010 SE-SoICT KTLT 4-1.42
- .Giải thuật đệqui tính giá trị f(n) f(n) = if(n = no) return C ; else return (g(n,f(n -1)) ; •Giải thuật lặp tính giá tri f(n) k := no ; F := C ; { F = f(no) } while( k < n ) { k := k +1 ; F := g(k,F ) ; } return F; •Trong trường hợp này : W = U = ( k ,F ) Wo = Uo = ( no,C ) C(U) = ( k < n) f(W) = f(U) = f(k,F) = (k+1,g(k,F))) Last update 8-2010 SE-SoICT KTLT 4-1.44
- 2.Các thủ tục đệ qui dạng đệ qui đuôi •Xét thủ tục P dạng : P(X) ≡ if B(X) then D(X) else { A(X) ; P(f(X)) ; } •Trong đó: X là tập biến (một hoặc một bộ nhiều biến ). •P(X) là thủ tục đệ qui phụ thuộc X •A(X) ; D(X) là các thao tác không đệ qui •f(X) là hàm biến đổi X Last update 8-2010 SE-SoICT KTLT 4-1.46
- Vídụ: Để đổi 1 số nguyên không âm Y ở cơ số10 sang dạng cơ số k ( 2 0 ) •A(X) là lệnh gán A[m] := n%k ; •f(X) là hàm f(n,m ) = ( n/k , m -1 ) ; •D(X) là lệnh rỗng –Đoan lệnh lặp tương ứng với thủ tục Convert(x,m) là: while (n != 0) { A[m] = n % k ; //A(X) n = n / k ; // X := f(X) m = m -1 ; Last update 8-2010 SE-SoICT KTLT 4-1.48 }
- Cài đặt không đệ qui unsigned USCLN1(int a, int b) {// Dùng vòng lặp theo thuật toán Ơclide while (a !=b) { if (a > b) a-=b; else b-=a; } return b; } Last update 8-2010 SE-SoICT KTLT 4-1.50
- A. Đệ qui chỉ có một lệnh gọi trực tiếp •Đệ qui có dạng sau: P(X) ≡ if C(X) D(X) else { A(X) ; P(f(X)) ; B(X) ; } X là một biến đơn hoặc biến véc tơ. C(X) là một biểu thức boolean của X . A(X) , B(X) , D(X):không đệ qui f(X) là hàm của X (hàm đơn điệu giảm) Last update 8-2010 SE-SoICT KTLT 4-1.52
- •Ví dụ:Thủ tục đệ qui chuyển biểu diễn số từ cơ số thập phân sang nhị phân có dạng : Binary(m) ≡if ( m > 0 ) { Binary( m div 2 ) ; write( m mod 2 ) ; } •Trong trường hợp này : X là m . P(X) là Binary(m) . A(X) ; D(X) là lệnh rỗng . B(X) là lệnh Write(m mod 2 ) ; C(X) là ( m <= 0 ) . f(X) = f(m) = m div 2 Last update 8-2010 SE-SoICT KTLT 4-1.54
- B. Thủ tục đệ qui với hai lần gọi đệ qui –Đệ qui có dạng sau P(X) ≡if C(X) D(X) ; else { A(X) ; P(f(X)) ; B(X) ; P(g(X)) ; } -Thuật toán khử đệ qui tương ứng với thủ tục đệquy P(X) là:{ Creat_Stack (S) : Push (S, (X,1)) ; do { while ( not C(X) ) { A(X) ; Push (S, (X,2)) ; X := f(X) ; } D(X) ; POP (S, (X,k)) ; if ( k <> 1) { B(X) ; X := g(X) ; } Last update} while 8-2010 ( k = 1 ) ; SE-SoICT } KTLT 4-1.56
- •Giải thuật không đệ qui tương đương là: THN (n, X, Y, Z) { Creat_Stack (S) ; Push (S ,(n,X,Y,Z,1)) ; do while ( n > 0 ) { Push (S ,(n,X,Y,Z,2)) ; n := n -1 ; Swap (Y,Z ) ; } POP (S,(n,X,Y,Z,k)) ; if ( k <> 1 ) { Move (X ,Z ) ; n := n -1 ; Swap (X ,Y ) ; } } while ( k = 1 ) ; } Last update 8-2010 SE-SoICT KTLT 4-1.58
- Bài toán 8 con Hậu – Giải thuật Algorithm Solve Input trạng thái bàn cờ Output 1. if trạng thái bàn cờ chứa đủ 8 con hậu 1.1. In trạng thái này ra màn hình 2. else 2.1. for mỗi ô trên bàn cờ mà còn an toàn 2.1.1. thêm một con hậu vào ô này 2.1.2. dùng lại giải thuật Solve với trạng thái mới 2.1.3. bỏ con hậu ra khỏi ô này End Solve Vét cạn Last update 8-2010 SE-SoICT KTLT 4-1.60
- Bài toán 8 con Hậu – Thiết kế dữ liệu đơn giản const int max_board = 30; class Queens { public: Queens(int size); bool is_solved( ) const; void print( ) const; bool unguarded(int col) const; void insert(int col); void remove(int col); int board_size; // dimension of board = maximum number of queens private: int count; // current number of queens = first unoccupied row bool queen_square[max_board][max_board]; }; Last update 8-2010 SE-SoICT KTLT 4-1.62
- Bài toán 8 con Hậu – Góc nhìn khác Last update 8-2010 SE-SoICT KTLT 4-1.64
- Bài toán 8 con Hậu – Mã C++ mới Queens :: Queens(int size) { board size = size; count = 0; for (int i = 0; i < board_size; i++) col_free[i] = true; for (int j = 0; j < (2 * board_size − 1); j++) upward_free[j] = true; for (int k = 0; k < (2 * board_size − 1); k++) downward_free[k] = true; } void Queens :: insert(int col) { queen_in_row[count] = col; col_free[col] = false; upward_free[count + col] = false; downward_free[count − col + board size − 1] = false; count++; } Last update 8-2010 SE-SoICT KTLT 4-1.66