M ột phương pháp đơn giản để có thể kiểm soát được bộ nhớ c ủa những lời gọi chương trình con, sử dụng stack riêng để lưu các bi ến, địa chỉ cần thiết.. V ề mặt bản chất, gần giống[r]
Trang 1Gi ảng viên: TS Ngo Huu Phuc
Tel: 0438 326 077 Mob: 098 5696 580 Email: ngohuuphuc76@gmail.com
C ấu trúc dữ liệu và giải thuật
Trang 2Bài 12 Kh ử đệ quy
N ội dung bài học:
12.1 Khái ni ệm chung
12.2 Kh ử đệ quy cho bài toán tính giai thừa.
12.3 Kh ử đệ quy cho bài toán Fibonacci.
12.4 Kh ử đệ quy cho bài toán tháp Hanoi.
12.5 Kh ử đệ quy cho bài toán QuickSort.
Tham kh ảo:
Trang 312.1 Khái ni ệm chung (1/7)
Đối với hệ điều hành, thông thường, khi một chương trình con được gọi, nó sẽ thực hiện:
1. Trước hết, hệ điều hành lưu tất cả các thông tin cần thiết của
chương trình con (thông tin cần thiết)
2. Tiếp theo, chuẩn bị gọi chương trình và chuyển quyền điều
khiển cho chương trình con
3. Sau đó, khi kết thúc chương trình con, hệ điều hành lấy lại
các thông tin đã lưu ở bước 1 và chuyển quyền điều khiển đến nơi gọi chương trình con
Lưu ý: các thông tin được lưu tại Stack của chương trình.
Trang 412.1 Khái ni ệm chung (2/7)
Stack chương trình:
Đối với các ngôn ngữ bậc cao, sẽ sử dụng Stack chương trình cho mỗi lời gọi chương trình con
Như vậy, khi chương trình con được gọi, mọi thông tin của môi trường (tại nơi gọi chương trình con) sẽ được đưa vào stack chương trình
Khi quay trở lại nơi gọi chương trình con, các thông tin được
lấy lại từ stack chương trình
V ới cách này, bộ nhớ cần thiết sẽ rất lớn khi áp dụng cho bài toán đệ quy.
Trang 512.1 Khái ni ệm chung (3/7)
S ử dụng bản ghi dạng stack riêng:
Một phương pháp đơn giản để có thể kiểm soát được bộ nhớ
của những lời gọi chương trình con, sử dụng stack riêng để lưu các biến, địa chỉ cần thiết
Về mặt bản chất, gần giống với hệ thống, tuy nhiên chỉ lưu trữ thông tin cần thiết (không phải toàn bộ thông tin trước lời gọi chương trình con)
Quá trình lưu và lấy lại tương tự như thao tác trên stack
Trang 612.1 Khái ni ệm chung (4/7)
V ới cách sử dụng Stack riêng, lưu ý:
S ử dụng biến để lưu địa chỉ nơi gọi chương trình con.
V ới các tham số dạng giá trị, tạo bản copy của nó khi
lưu.
V ới các biến dạng tham chiếu, lưu trữ địa chỉ của chúng.
V ới các biến cục bộ (khai báo trong đoạn chương trình),
t ạo bản copy và lưu chúng.
Trang 712.1 Khái ni ệm chung (5/7)
Các bước gợi ý trong việc khử đệ quy tổng quát:
Có thể tạo một stack riêng để chứa các bản ghi Lệnh gọi đệ quy
và lệnh trả về từ hàm đệ quy có thể được thay thế như sau:
Đưa vào stack một bản ghi chứa các biến cục bộ, các tham số và
v ị trí dòng lệnh ngay sau lệnh gọi đệ quy.
Gán m ọi tham số về các trị mới thích hợp.
Tr ở về thực hiện dòng lệnh đầu tiên trong giải thuật đệ quy.
M ỗi lệnh trả về của hàm đệ quy được thay đổi:
L ấy lại từ stack để phục hồi mọi biến, tham số.
B ắt đầu thực hiện dòng lệnh tại vị trí mà trước đó đã được cất trong stack.
Trang 812.1 Khái ni ệm chung (6/7)
Các bước gợi ý trong việc khử đệ quy đuôi:
1 S ử dụng một biến để thay thế cho việc gọi đệ quy.
2 S ử dụng một vòng lặp với điều kiện kết thúc giống như điều
ki ện dừng của đệ quy.
3 Đặt tất cả các lệnh vốn cần thực hiện trong lần gọi đệ quy đuôi
vào trong vòng l ặp.
4 Thay l ệnh gọi đệ quy bằng phép gán.
5 Dùng các l ệnh gán để gán các trị như các tham số mà hàm đệ
quy l ẽ ra nhận được.
6 Tr ả về trị cho biến đã định nghĩa ở bước 1.
Trang 912.1 Khái ni ệm chung (7/7)
M ột số lưu ý khi sử dụng:
N ếu tất cả các tham số có cùng kiểu:
S ử dụng nhiều thao tác push vào stack.
Sau đó, sử dụng nhiều thao tác pop để phục hồi thông tin.
N ếu các tham số có kiểu khác nhau:
S ử dụng cấu trúc hoặc,
S ử dụng nhiều stack cho mỗi loại tham số.
Trang 1012.2 Kh ử đệ quy cho bài toán tính giai thừa(1/4)
Xét l ại đoạn chương trình tính n!
long factorial( int n)
{
if ((n==0)||(n==1)) return 1;
else return n*factorial(n-1);
}