Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.18 MB, 125 trang )
<span class="text_page_counter">Trang 1</span><div class="page_container" data-page="1">
TRƯỜNG ĐẠI HỌC DUY TÂN KHOA CƠNG NGHỆ THƠNG TIN
Khái niệm: Nếu 1 lời giải của bài toán P được thực hiện bằng lời giải của 1 hay nhiều của bài tốn P’nhỏ hơn có dạng giống như P thì đó là một lời giải đệ quy. Giải thuật tương ứng với lời giải đệ quy gọi là giải thuật đệ quy.
Cấu trúc của giải thuật đệ quy
Một giải thuật đệ quy bao giờ cũng gồm 2 phần
Phần neo: Xác định điểm kết thúc của một giải thuật đệ quy. Trường hợp này còn được gọi là trường hợp suy biến. Nếu một giải thuật đệ quy khơng có trường hợp suy biến thì sẽ dẫn đến lặp vô hạn và sinh lỗi khi thi hành.
Phần đệ quy: Phân tích và xây dựng trường hợp chung của bài tốn (có nghĩa là đưa bài toán về bài toán cùng loại nhưng với dữ liệu nhỏ hơn).
Xây dựng giải thuật đệ quy
Khi cài đặt thuật toán đệ quy ta tiến hành những bước sau:
Bước 1: Xác định mục đích, đầu vào và đầu ra để từ đó xác định tên tiêu đề hàm (tên hàm và tham số hình thức của nó)
Bước 2: Xác định trường hợp suy biến (neo)
Nguyễễn Minh Nh tậ Trang 1
</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">Bước 3: Phân tích và xây dựng trường hợp chung của bài toán (phần đệ quy). Có nghĩa là đưa bài tốn về bài tốn cùng loại nhưng với dữ liệu nhỏ hơn.
Từ 3 bước trên, áp dụng nguyên tắc viết ngôn ngữ ta xây dựng được hàm đệ qui (thơng thường sử dụng tốn tử điều kiện (if…else) để viết hàm đệ quy.
Ví dụ 1: Tính x với x được định nghĩa như sau:<small>yy</small>
Bước 1: Xác định tham số đầu vào là x và y, tham số đầu ra là x . Ta luôn<small>y</small> nhận được giá trị x duy nhất, do đó ta sử dụng hàm có kiểu trả về.<small>y </small>
Bước 2: Phần neo: y=0 thì tổng x = 1<small>0</small>
Bước 3: Phần đệ quy: là trường hợp thực hiện lại bài toán với giá trị nhỏ hơn y = y - 1. Tức là ứng với trường hợp thực hiện lại lời gọi cũng đều có chung tham số đầu vào là y nhưng với giá trị nhỏ hơn y = y – 1 và đều có khuynh hướng đến trường hợp suy biến y = 0.
Ví dụ 2: Đếm số chữ số của số nguyên dương N. Ví dụ N = 152 có 3 chữ số Bước 1: Xác đinh tham số đầu vào là n, tham số đầu ra là số chữ số của n. Sử dụng hàm có kiểu trả về vì bài tốn này trả về một giá trị duy nhất là số chữ
</div><span class="text_page_counter">Trang 4</span><div class="page_container" data-page="4">Hàm đệ quy như sau: int dem(long n) {
if(n==0) return 0;
else return (1+ dem(n/10));
1 Ví dụ 3: Xuất đảo ngược một số nguyên dương ra màn hình
</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">1. (0.2 point)
Theo cách tiếp cận của lập trình có cấu trúc, NiklausWirth đưa ra cơng thức thể hiện được mối liên hệ giữa cấu trúc dữ liệu và giải thuật như sau:
*A. Thuật toán + cấu trúc dữ liệu = chương trình B. Thuật tốn + dữ liệu đầu vào = chương trình C. Kỹ thuật lập trình + cấu trúc dữ liệu = Kết quả đầu ra. D. Tất cả đều đúng
2. (0.2 point)
Chọn phát biểu đúng nhất
*A. Một đối tượng được gọi là đệ quy nếu nó hoặc một phần của nó được định nghĩa thơng qua khái niệm về chính nó.
B. Một chương trình gọi là đệ quy nếu trong chương trình có lời gọi đến một chương trình đệ quy khác.
C. Một chương trình đệ quy là chương trình lặp đi lặp lại với số lần lặp không biết trước. D. Một chương trình đề quy là chương trình có chưa hàm main.
3. (0.2 point)
Chọn phát biểu đúng nhất
D. Nếu một lời giải của bài toán P được thực hiện bằng lời giải của bài tốn P’, có dạng giống như P thì đó là một lời giải đệ quy
B. Giải thuật tương ứng với lời giải đệ quy gọi là giải thuật đệ quy
C. Nếu giải thuật đệ quy được viết dưới dạng một thủ tục thì thủ tục ấy được gọi là thủ tục đệ quy.
*D. Tất cả đều đúng 4. (0.2 point)
Trong giải thuật đệ quy thành phần dừng là: A. Thành phần neo của thuật toán. B. Xác định điểm dừng của thuật toán
C. Thành phần không chứa khái niệm trong định nghĩa. *D. Tất cả đều đúng
5. (0.2 point)
Nguyễễn Minh Nh tậ Trang 4
</div><span class="text_page_counter">Trang 6</span><div class="page_container" data-page="6">Giải thuật là … câu lênh chặt chẽ, rõ ràng và xác định các thao tác trên các đối
Đánh giá độ phức tạp của giải thuật là việc xác định ……và…… mà giải thuật cần để thưc hiện giải một bài toán
A, Khoảng thời gian, độ khó B. Khoảng thời gian, độ phức tạp
*C. Khoảng thời gian, dung lượng bộ nhớ máy tính D. Độ khó, dung lượng bộ nhớ máy tính
8. (0.2 point)
Các kiểu dữ liệu cơ bản là……
A. Các kiểu dữ liệu mà người lập trình được cung cấp sẵn từ máy tính B. Các kiểu dữ liệu mà người lập trình được cung cấp sẵn từ ngơn ngữ tự nhiên *C. Các kiểu dữ liệu mà người lập trình được cung cấp sẵn từ ngơn ngữ lập trình D. Các kiểu dữ liệu mà người lập trìn được cung cấp sẵn từ ngôn ngữ này
</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7">Kiểu dữ liệu trừu tượng là….
A. Kiểu dữ liệu mà người lập trình tự xây dựng khơng dựa trên kiểu dữ liệu cơ bản được cung cấp từ ngôn ngữ lập trình
B. Kiểu dữ liệu mà người lập trình phải tự xây dựng dựa trên kiểu dữ liệu không cơ bản được cung cấp từ ngơn ngữ lập trình
C. Kiểu dữ liệu mà người lập trình phải tự xây dựng dựa trên các kiểu dữ liệu cơ bản được cung cấp từ ngôn ngữ này.
*D Kiểu dữ liệu mà người lập trình phải tự xây dựng trên các kiểu dữ liệu cơ bản được
</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">14. (0.2 point)
Chọn đáp án đúng nhất
A. Câu trúc dữ liệu cách lưu trữ dữ liệu trong bộ nhớ máy tình (ROM), sao cho nó có thể được sử dụng một cách hiệu quả.
*B. Câu trúc dữ liệu cách lưu trữ dữ liệu trong bộ nhớ máy tình (RAM), sao cho nó có thể được sử dụng một cách hiệu quả.
C. Câu trúc dữ liệu cách lưu trữ dữ liệu trong bộ nhớ máy tình (HDD), sao cho nó có thể được sử dụng một cách hiệu quả.
D. Câu trúc dữ liệu cách lưu trữ dữ liệu trong bộ nhớ máy tình (USB), sao cho nó có thể được sử dụng một cách hiệu quả.
15. (0.2 point)
Cho hàm sau: int A(int n){if(n==0) return 0; return n+A(n-1);}. Với n=3 hàm trả về giá trị nào sau đây:
Cho hàm sau: int A(int n){if(n==0) return 0; return n*A(n-1);}. Với n=3 hàm trả về giá trị nào sau đây:
Cho hàm sau: int A(int n,int m=0){if(n==0) return m; return A(n/10,m*10+n %10);}. Với lời gọi cout<<A(1200) nhận được kết quả nào sau đây: *A. 21
B. 0021
Nguyễễn Minh Nh tậ Trang 7
</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9">C. 1200 D. Lỗi cú pháp 18. (0.2 point)
Cho hàm sau: int A(int n,int m=0){if(n==0) return m; return A(n/10,m*10+n %10)}. Với lời gọi cout<<A(1200) nhận được kết quả nào sau đây:
Cho hàm sau: int B(int n){if(n>0){B(n/10);cout<<n%10<<"\t";}} Với lời gọi cout<<B(102) nhận được kết quả nào sau đây:
Cho hàm sau: int B(int n){if(n>0){ cout<<n%10<<"\t";B(n/10); }} Với lời gọi cout<<B(102) nhận được kết quả nào sau đây:
A. 201
Nguyễễn Minh Nh tậ Trang 8
</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">B. 1 0 2 *C. 2 0 1 D. Lỗi cú pháp 22. (0.2 point)
Cho hàm sau: int B(int n){if(n>0){B(n/2);cout<<n%2<<"\t";}}Với lời gọi cout<<B(10) nhận được kết quả nào sau đây:
Cho hàm sau: int B(int n){if(n>0){ cout<<n%2<<"\t";B(n/2); }}Với lời gọi cout<<B(10) nhận được kết quả nào sau đây:
Cho hàm sau: int B(int A[],int n){if(n>0){cout<<A[n]<<"\t";B(A,n/2);}}. Cho A[]={1,2,3,4,5} Với lời gọi B(A,4) nhận được kết quả nào sau đây:
Cho hàm sau: int B(int A[],int n){if(n==0) return 0; return (A[n]+B(A,n-1));} Cho A[]={1,2,3,4,5} Với lời gọi cout<<B(A,4) nhận được kết quả nào sau đây:
</div><span class="text_page_counter">Trang 11</span><div class="page_container" data-page="11">D. 12
TRẢ LỜI NGẮN
1: Cấu trúc dữ liệu là gì? Cấu trúc dữ liệu khác cấu trúc lưu trữ ở những điểm nào? 2: Nêu khái niệm giải thuật là gì? Mối quan hệ giữa giải thuật và cấu trúc dữ liệu là gì? Nêu các tính chất của giải thuật
3: Một ngơn ngữ lập trình có nên cho phép người sử dụng tự định nghĩa thêm các kiểu dữ liệu có cấu trúc? Giải thích và cho ví dụ.
B2. BÀI TẬP THỰC HÀNH
1: Hãy viết mỗi yêu cầu sau bằng giải thuật đệ qui: a. Tính tổng S = 1 – 1/2 + 1/3 -1/4 + ...+(-1) 1/n<small>n+1</small>
b. Tính S= 1 + 3 + 5 + ... + (2k+1) với (2k+1) ≤ n c. Đổi số n hệ 10 sang hệ nhị phân (chỉ sử dụng đệ qui) d. Tìm số đảo ngược 1 số nguyên dương.
e. Tìm ước số chung lớn nhất và bội số chung nhỏ nhất của 2 số nguyên A và B
f. Tính tổng S= 1/2 +2/3+ 3/4 + ...+ n/ n+1 g. Tính tổng S= 1/2+ 1/2.3 +1/3.4 + ... + 1/n(n+1) h. Tìm chữ số lớn nhất của số nguyên dương n. 2: Tính tổng các ước số của số nguyên dương n.
3: Viết giải thuật đệ quy cho bài tốn tìm số Fibonaci F(n)=F(n-1)+ F(n-2)
4: Viết giải thuật đệ quy cho Bài toán tháp Hà Nội chuyển n đĩa từ cột A sang cột C với
Hãy viết chương trình đệ quy cho bài toán trên với n được nhập vào từ bàn phím. 6: Dãy số Fibonacci được định nghĩa như sau:(Không dùng đệ quy)
Nguyễễn Minh Nh tậ Trang 10
</div><span class="text_page_counter">Trang 12</span><div class="page_container" data-page="12">A(0) = A(1) =1
A(n) = A(n-2) + A(n-1) với n >1 a. Nhập một số n và in ra n số Fibonacci đầu tiên b. Nhập một số n và in ra các số Fibonacci <=n c. Nhập một số m, kiểm tra xem m có phải số Fibonacci? Hướng dẫn: Không dùng mảng mà dùng 3 biến để tính các số
A.Lý thuyết
1. Định nghĩa danh sách
Danh sách là một tập hợp gồm nhiều phần tử (element) a<small>1</small>a<small>2</small>…a<small>n</small> mà tính chất cấu trúc của nó là mối liên hệ tương đối giữa các phần tử với nhau: nếu biết được phần tử a thì<small>i</small>
ta sẽ biết được vị trí của các phần tử a<small>i</small>+1.Số phần tử của danh sách được gọi là chiều dài của danh sách. Một danh sách có chiều dài bằng 0 là một danh sách rỗng. Một tính chất quan trọng của danh sách là các phần tử có thể được sắp xếp tuyến tính theo vị trí của chúng trong danh sách.
2. Định nghĩa danh sách đặc
Danh sách đặc là một danh sách mà các phần tử được sắp xếp kế tiếp nhau trong bộ nhớ, đứng ngay sau vị trí phần tử ai là vị trí phần tử a . <small>i+1</small>
3. Mảng
Mảng là một tập hợp có thứ tự gồm một số xác định n phần tử cùng kiểu dữ liệu liên tục trong bộ nhớ và có cùng một tên (với n được gọi là độ dài hay kích thước của mảng). Ngồi giá trị, mỗi phần tử của mảng còn được đặc trưng bởi chỉ số(index), thể hiện thứ tự của phần tử đó trong mảng.
Các phần tử của mảng nằm trong các ô nhớ liên tục nhau, địa chỉ thấp nhất của ô nhớ tương ứng với phần tử thứ nhất và địa chỉ cao nhất của ô nhớ tương ứng với phần tử cuối cùng. Số phần tử của mảng sau khi được cấp phát là cố định. Mỗi phần tử của mảng được truy nhập trực tiếp thông qua tên mảng cùng với chỉ số của nó. Đối với mảng thường có các phép tốn:
+ Tạo lập một mảng
Nguyễễn Minh Nh tậ Trang 11
</div><span class="text_page_counter">Trang 13</span><div class="page_container" data-page="13">+ Duyệt qua các phần tử của mảng. + Tìm kiếm một phần tử trong mảng.
+ Sắp xếp các phần tử trong mảng theo thứ tự ấn định trước, v.v…
Vì số phần tử của mảng là cố định nên phép bổ sung phần tử mới vào mảng hoặc loại bỏ một phần tử ra khỏi mảng thực hiện phải mất nhiều thao tác.
Mảng một chiều hay còn gọi là vectơ là mảng mà mỗi phần tử của nó ứng với một chỉ số. Ví dụ như mảng a chứa n phần tử, phần tử thứ i của mảng a kí hiệu là a[i] trong đó i được gọi là chỉ số của mảng a.
Ta có thể hình dung mảng một chiều a như hình vẽ sau:
Hình 2.1 Biểu diễn mảng một chiều
Ma trận là mảng hai chiều, mỗi phần tử của nó ứng với hai chỉ số hàng và cột. Ví dụ mảng B là mảng hai chiều hay còn gọi là ma trận B ký hiệu là B[i,j] với i là chỉ số hàng, j gọi là chỉ số cột.
4. Cấu trúc lưu trữ trên mảng một chiều
Thông thường một số từ máy kế tiếp sẽ được giành ra để lưu trữ các phần tử của mảng (vì vậy người ta gọi là cách lưu trữ kế tiếp – sequential storage allocation)
Nếu mỗi phần tử của vector V[i] chiếm m từ máy mới đủ bộ nhớ để chứa phần tử a[i] thì lúc đó vector V chiếm n*m từ máy kế tiếp (hay nói là vector V được lưu trữ trong n*m từ máy kế tiếp). Địa chỉ của mỗi ô nhớ tức là địa chỉ của mỗi phần tử V[i] chính là địa chỉ của từ máy đầu tiên của ơ nhớ đó.
Hình 2.2 Mơ tả Vector lưu trữ Địa chỉ V[1] được gọi là địa chỉ gốc(base address) ký hiệu là L0.
Như vậy việc xác định địa chỉ của V[i] hay nói cách khác là địa chỉ của a[i] sẽ được tính theo cơng thức sau:
Nguyễễn Minh Nh tậ Trang 12
</div><span class="text_page_counter">Trang 14</span><div class="page_container" data-page="14">LOC(a[i]) = L0 + m * (i-1) Trong đó LOC là từ viết tắt của chữ LOCATION (vị trí) Hàm f(i) = m* (i-1) được gọi là hàm địa chỉ.
5. Một số thuật toán trên danh sách đặc 5.1. Duyệt mảng
Khai báo mảng một chiều trong C:
Kiểu_dữ _liệu Tên_mảng[số phần tử của mảng]; Truy cập vào một phần tử của mảng: Tên_mảng[chỉ số];
Duyệt mảng là việc mà chúng ta cần phải thăm tất cả các phần tử của mảng và duyệt qua tất cả các phần tử mà khơng bỏ sót phần tử nào của mảng. Thơng thường dùng vịng lặp để duyệt qua tất cả các phần tử của mảng.
for (int i=1; i<n; i++)
cout << ” ”<<a[ i]; getch();
Duyệt mảng để tìm kiếm
Duyệt mảng để tìm kiếm một phần tử có khóa k. Có 2 giải thuật thường được áp dụng để tìm kiếm dữ liệu là tìm tuyến tính và tìm nhị phân. Ðể đơn giản trong việc trình bày giải thuật, bài tốn được đặc tả như sau:
Tập dữ liệu được lưu trữ là dãy số a , a , ... ,a .<small>12n</small>
Giả sử chọn cấu trúc dữ liệu mảng để lưu trữ dãy số này trong bộ nhớ chính, có khai báo: int a[n];
Lưu ý các bản cài đặt trong tập bài giảng này sử dụng ngôn ngữ C, do đó chỉ số của mảng mặc định bắt đầu từ 0, nên các giá trị của các chỉ số có chênh lệch so với thuật tốn, nhưng ý nghĩa không đổi.
Nguyễễn Minh Nh tậ Trang 13
</div><span class="text_page_counter">Trang 82</span><div class="page_container" data-page="82">{ int A[N]; int f,r; }; Khởi tạo hàng đợi
Việc khởi tạo tức là cho f và r nhận giá trị 0, cho toàn bộ các phần tử của mảng A
Kiểm tra xem hàng đợi có rỗng khơng
Để kiểm tra hàng đợi có rỗng hay không ta chỉ cần kiểm tra giá trị r của hàng đợi Q là biết được. Bởi vì khi thêm một phần tử vào thì giá trị r tăng lên 1, nếu r =0 chứng tỏ else return false;
Nguyễễn Minh Nh tậ Trang 81
</div><span class="text_page_counter">Trang 83</span><div class="page_container" data-page="83">Kiểm tra hàng đợi đã đầy chưa
Để kiểm tra hàng đợi đã đầy ta chỉ cần kiểm tra giá trị r –f của hàng đợi Q là biết được. Bởi vì khi thêm một phần tử vào thì giá trị r tăng lên 1, khi loại bỏ một phần tử ra khỏi hàng thì f tăng lên 1, như vậy nếu số phần tử có chứa dữ liệu là r-f=N (là số phần tử cực đại của mảng) thì chứng tỏ mảng bị đầy. Ngược lại thì cịn các phần tử mảng chưa có chứa giá trị(là các phần tử đứng trước f: có thể đã bị loại bỏ đi). Hàm kiểm tra được mô tả
Thêm một phần tử vào cuối hàng đợi
Khi thêm một phần tử vào hàng đợi ta xem xét các trường hợp sau:
Kiểm tra hàng có rỗng hay khơng: Nếu hàng rỗng ta thêm giá trị X vào phần tử có chỉ số r, tăng r lên một đơn vị.
Ngược lại hàng đã có dữ liệu thì tiến hành kiểm tra xem hàng có bị tràn hay khơng, nếu bị tràn nhưng dữ liệu chưa đầy thì tiến hành giải quyết hàng tràn theo một trong hai cách:
Cách 1: Tịnh tiến hàng lên f-1 phần tử(được sử dụng trong bài giảng này) Cách 2: Xem như hàng là vòng tròn, thêm dữ liệu vào phần tử đầu tiên của mảng, trước f, nếu r = N thì cho r= 0 ngược lại tăng r lên 1 đơn vị.(sinh viên tự cài đặt tại nhà theo cách này)
Nếu hàng đã đầy thì thơng báo hàng đầy khơng thể thêm dữ liệu. Hàm mô tả bằng ngôn ngữ C như sau:
Nguyễễn Minh Nh tậ Trang 82
</div><span class="text_page_counter">Trang 84</span><div class="page_container" data-page="84">// Them mot phan tu vao hang Queue AddEndQueue(Queue Q, int X)
// di chuyen tinh tien hang len f-1 phan tu for(int i=Q.f+1; i<=Q.r; i++)
</div><span class="text_page_counter">Trang 85</span><div class="page_container" data-page="85">b. Cài đặt hàng đợi bằng danh sách liên kết
Ta có thể tạo hàng đợi bằng cách sử dụng một danh sách liên kết đơn.
Hình 4.5 Cài đặt hàng đợi bằng DSLK
Phần tử đầu DSKL (Dau) sẽ là phần tử đầu hàng đợi, phần tử cuối DSKL (Cuoi) sẽ là phần tử cuối hàng đợi.
Khởi tạo hàng đợi:
Ta cho hai biến con trỏ Dau và Cuoi trỏ vào giá trị NULL. Trả về giá trị là con trỏ Dau trỏ đến đầu hàng đợi:
// Khoi tao hang doi
</div><span class="text_page_counter">Trang 86</span><div class="page_container" data-page="86">return Dau; }
Kiểm tra xem hàng đợi có rỗng khơng
Hàng đợi rỗng khi phần tử Dau chỉ vào đầu hàng đợi có giá trị NULL. // Kiem tra hang doi rong
bool IsEmpty_Queue(Queue Q) {
Dau = Q;
if (Dau == NULL) return true; else return false;
Thêm một phần tử vào hàng đợi
Khi thêm một phần tử mới vào hàng ta thêm nó vào cuối hàng đợi. Thủ tục thêm vào cuối được mô tả bằng ngôn ngữ C như sau:
Queue AddEndQueue(Queue Q, int x)
Hàm InsertTail(Q,x)là hàm thêm vào cuối danh sách liên kết một phần tử, đã được trình bày trong chương 3.
Loại một phần tử ra khỏi hàng đợi
Khi muốn loại một phần tử ra khỏi hàng ta loại phần tử đầu tiên của hàng đợi. Thuật toán tương tự thuật toán loại Node đầu tiên của danh sách liên kết đơn đã trình bày ở chương 3.Thủ tục loại phần tử ra khỏi hàng đợi được mô tả bằng ngôn ngữ C như sau:
//Xoa node dau tien cau hang doi
</div>