Dùng stack để khử thuật tóan đệ quy nhánh doc

14 2.9K 30
Dùng stack để khử thuật tóan đệ quy nhánh doc

Đ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

Phần 2: dùng stack để khử thuật tóan đệ quy nhánh Như chúng ta đã biết, sự thực hiện của đệ quy chính là sự thực hiện lại chính bản thân nó với dữ liệu nhỏ hơn, nên khi gọi hàm đệ quy thì máy sẽ cấp phát một vùng nhớ có cơ chế hoạt động như Stack (ngăn xếp) cho hàm đệ quy đó. Cơ chế này hoạt động như kiểu LIFO( last in first out – vào sau ra trước), tức là việc thêm vào hay lấy ra đều thực hiện thông qua một đầu ra duy nhất Nếu một chương trình con đệ qui P(x) được gọi từ chương trình chính ta nói chương trình con được thực hiện ở mức 1. Chương trình con này gọi chính nó, ta nói nó đi sâu vào mức 2 và cho đến một mức k. Rõ ràng mức k phải thực hiện xong thì mức k-1 mới được thực hiện tiếp tục, hay ta còn nói là chương trình con quay về mức k-1. Trong khi một chương trình con từ mức i đi vào mức i+1 thì các biến cục bộ của mức i và địa chỉ của mã lệnh còn dang dở phải được lưu trữ, địa chỉ này gọi là địa chỉ trở về. Khi từ mức i+1 quay về mức i các giá trị đó được sử dụng. Như vậy những biến cục bộ và địa chỉ lưu sau được dùng trước. Tính chất này gợi ý cho ta dùng một stack để lưu giữ các giá trị cần thiết của mỗi lần gọi tới chương trình con như biến cục bộ, các tham số. Mỗi khi lùi về một mức thì các giá trị này được lấy ra để tiếp tục thực hiện mức này. Ta tóm tắt quá trình: •  Bưóc 1: Tạo 1 stack rỗng •  Bưóc 2: Lưu các tham số hình thức ở mức 1 vào stack. •  Bước 3: Lặp cho đến khi stack rỗng 1. Lấy ra 1 phần tử X khỏi stack Vào sau Vào trước vào ra Đỉnh Stack(Ngăn xếp) 2. Nếu X thoả điều kiện dừng của đệ qui (NEO) thì xử lý dữ liệu Ngược lại (phần đệ qui) Xử lý theo thuật tóan Xác định lời gọi đệ quy với các tham số tương ứng và nạp các tham số đó vào stack  Bước 4: Kết thúc thuật toán Một số ví dụ về dùng Stack để khử đệ qui Ví dụ 1: Bài toán Tháp Hà Nội Có ba cọc A,B,C. Khởi đầu cọc A có một số đĩa xếp theo thứ tự nhỏ dần lên trên đỉnh. Bài toán đặt ra là phải chuyển toàn bộ chồng đĩa từ A sang C. Mỗi lần thực hiện chuyển một đĩa từ một cọc sang một cọc khác và không được đặt đĩa lớn nằm trên đĩa nhỏ Phân tích bài toán: Trường hợp 1 đĩa: Chuyển thẳng từ A sang C. Đây là trường hợp suy biến Trường hợp 2 đĩa: Chuyển 1 đĩa từ A sang B Chuyển 1 đĩa từ A sang C Chuyển 1 đĩa từ B sang C Trường hợp chung n>1 đĩa; Ta coi n-1 đĩa trên như là 1 đĩa và ta áp dụng trong trường hợp 2 đĩa  Chuyển n-1 đĩa từ A sang B ( dùng cọc C làm trung gian)  Chuyển 1 đĩa từ A sang C  Chuyển n-1 đĩa từ B sang C (dùng cọc A làm trung gian) thuật toán đệ qui mô phỏng như sau: Proc Move(n,A,B,C) // Chuyển n đĩa từ cọc A sang cọc C If n=1 Then chuyển (A,’’,C) Else { Call Move(n-1, A, C, B) Call Move(1, A, B, C) Call Move(n-1, B, A, C) } Return Quá trình thực hiện chương trình con được minh hoạ với 3 đĩa (n=3) như sau: Move(1,A,B,C) A->B Move(2,A,C,B) Move(1,A,C,B) A->C Move(1,B,C,A) B->C move(3,A,B,C) Move(1,A,B,C) A->B Move(1,C,A,B) C->A Move(2,C,B,A) Move(1,C,B,A) C->B Move(1,A,B,C) A->B Mức 1 mức 2 mức 3 Cây đệ quy trên được mô phỏng qua việc dùng stack để giải quyết như sau: (3,A,B,C) (2,A,C,B) Quá trình chuyển đĩa (2,B,A,C) Từ đây, ta xây dựng cấu trúc của mỗi phần tử được lưu trong stack để giải quyết bài toán tháp Hà nội như sau: struct Item_Tower{ int num; // số đĩa char sour, midl, dest; void assign(int n, char a, char b, char c){ // hàm gán từng giá trị cho các thuộc tính num = n; sour = a; midl = b, dest = c; } }; 3 A B C A->C A->B C->B A->C B->A B->C A->C ACBABCBAC ABCACBCABA BCBAC BAC BCABACABC Thuật toán khử đệ qui void haNoi_Tower(int n, char a, char b, char c){ Stack<Item_Tower> sTower; // Khai báo stack kiểu Item_Tower Item_Tower t; int d = 0; t.assign(n, a, b, c); sTower.push(t); while (sTower.isEmpty() == false){ Item_Tower temp = sTower.topOfStack(); sTower.pop(); if(temp.num == 1) cout<<" \n "<<++d<<" ."<<temp.sour <<" >"<<temp.dest; else{ t.assign(temp.num-1, temp.midl, temp.sour, temp.dest); sTower.push(t); t.assign(1, temp.sour, temp.midl, temp.dest); sTower.push(t); t.assign(temp.num - 1, temp.sour, temp.dest, temp.midl); sTower.push(t); } } } Ví dụ 2: Phủ bàn cờ kích thước 2 n * 2 n bằng các con tromino Yêu cầu: có bàn cờ kích thước 2 n * 2 n (n nguyên >0). Đánh dấu 1 ô bất kỳ trên bàn cờ, sau đó dùng các con tromino phủ đầy bàn cờ. Với bàn cờ cỡ n= 8 Thuật tóan: Từ bàn cờ kích thước cỡ 2 n * 2 n với n>0 và 1 ô có tọa độ (x,y) bị đánh dấu, ta chia bàn cờ thành 4 bàn cờ con, với tâm bàn cờ là 1 con tromino có phần khuyết hướng về phía ô bị đánh dấu Như vậy ta được 4 bàn cờ con, mỗi bàn cờ con có kích thước 2 n-1 * 2 n-1 với 1 ô bị đánh dấu, vì mỗi góc của con tromino vừa đặt vào tâm trở thành ô bị đánh dấu trong mỗi bàn cờ con mới. X X X X Với mỗi bàn cờ con, ta lại tiếp tục thực hiện cho đến khi mọi ô trong bàn cờ bị đánh dấu. Như vậy, bài toán này trở thành 4 bài toán con tương tự với 4 lần gọi đệ quy Thuật toán Proc Tromino( {a, b}, {x, y}, n) /* trong do {a, b} là tọa độ góc trên bến trái của bàn cờ {x, y} là tọa độ ô bị đánh dấu n là kích thước của bàn cờ */ if(n>1){ s = n/2 ; // slà kích thước của 4 bàn cờ con xác định các bộ giá trị của 4 bàn cờ con ({a i , b i }, {x i , y i }) với i = 1 4 call Tromino({a 1 ,b 1 }, {x 1 , y 1 }, s); call Tromino({a 2 ,b 2 }, {x 2 , y 2 }, s); call Tromino({a 3 ,b 3 }, {x 3 , y 3 }, s); call Tromino({a 4 ,b 4 }, {x 4 , y 4 }, s); , } Return Từ đây, ta xây dựng cấu trúc của mỗi phần tử được lưu trong stack để giải quyết bài Tromino như sau: struct Item_Tromino{ int a, b, x, y, n; void assign(int a1, int b1, int x1, int y1, int n1){ a = a1, b = b1, x = x1, y = y1, n = n1; } }; void Tromino(){ Stack <Item_Tromino> sTromino; Item_Tromino t; // đưa dữ liệu của bàn cờ đầu tiên vào stack t.assign(1,1,x,y,n); sTromino.push(t); d = 0; //dùng để đánh dấu while (sTromino.isEmpty()==false){ Item_Tromino temp = sTromino.topOfStack(); sTromino.pop(); if(temp.n>1){ int a, b, x, y, d, a1, b1, a2, b2, a3, b3, a4, b4, x1, y1, x2, y2, x3, y3, x4, y4, s; s = temp.n/2; // s là kích thước của mỗi bàn cờ con a = temp.a; b = temp.b; x = temp.x; y = temp.y; //Xác định tọa độ trên bên trái của 4 bàn cờ con a1 = a; b1 = b; a2 = a; b2 = b + s ; a3 = a1 + s; b3 = b1 + s; a4 = a + s ; b4 = b; //xác định vị trí đánh dấu của các bàn cờ con x1 = a + s -1; y1 = b + s -1; x2 = x1, y2 = y1 + 1; x3 = x1 + 1; y3 = y2; x4 = x1 + 1; y4 = y1; // đánh dấu vào các ô bị đánh dấu ở mỗi bàn cờ con (trừ ô ban đầu) d++; if( x< a + s && y< b+ s ) {x1 = x, y1 =y ;M[x2][y2] = M[x3][y3] = M[x4][y4] = d;} if( x< a + s && y>= b+ s ) {x2 = x, y2 =y ;M[x1][y1] = M[x3][y3] = M[x4][y4] = d;} if( x>= a + s && y< b+ s ) {x4= x, y4 =y ;M[x1][y1] = M[x2][y2] = M[x3][y3] = d;} if( x>= a + s && y>= b+ s ) {x3 = x, y3 =y ;M[x1][y1] = M[x2][y2] = M[x4][y4] = d;} // Lần lượt đưa các tham số ({a i , b i }{x i , y i }, s) vào stack t.assign(a4,b4,x4,y4,s); // góc phần tư thứ 4 sTromino.push(t); t.assign(a3,b3,x3,y3,s); // góc phần tư thứ 3 sTromino.push(t); t.assign(a2,b2,x2,y2,s); // góc phần tư thứ 2 sTromino.push(t); t.assign(a1,b1,x1,y1,s); // góc phần tư thứ 1 sTromino.push(t); } } } Một kết quả khi chạy chương trình [...]... SX If Left . Phần 2: dùng stack để khử thuật tóan đệ quy nhánh Như chúng ta đã biết, sự thực hiện của đệ quy chính là sự thực hiện lại chính bản thân nó với dữ liệu nhỏ hơn, nên khi gọi hàm đệ quy thì máy. khỏi stack Vào sau Vào trước vào ra Đỉnh Stack( Ngăn xếp) 2. Nếu X thoả điều kiện dừng của đệ qui (NEO) thì xử lý dữ liệu Ngược lại (phần đệ qui) Xử lý theo thuật tóan Xác định lời gọi đệ quy. 3 Cây đệ quy trên được mô phỏng qua việc dùng stack để giải quy t như sau: (3,A,B,C) (2,A,C,B) Quá trình chuyển đĩa (2,B,A,C) Từ đây, ta xây dựng cấu trúc của mỗi phần tử được lưu trong stack để

Ngày đăng: 12/07/2014, 21:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan