TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT HƯNG YÊN KHOA CÔNG NGHỆ THÔNG TIN BÀI TẬP THỰC HÀNH HỌC PHẦN: CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT Trình độ đào tạo : Đại học chính quy Ngành đào tạo : CNTT, KTPM, KHMT Hưng Yên, năm 2021 MỤC LỤC Bài 9: Thực hành cài đặt giải thuật đệ quy. 3 Bài 10: Thực hành cài đặt danh sách bằng mảng. 10 Bài 11: Thực hành cài đặt danh sách bằng danh sách liên kết đơn. 17 Bài 12: Thực hành cài đặt danh sách Stack, Queue.
23 Bài 13: Thực hành cài đặt cây nhị phân. 33 Bài 14: Thực hành cài đặt các giải thuật sắp xếp - tìm kiếm. 35 Bài tập thực hành môn CTDL> Trang 2 Bài 9: Thực hành cài đặt giải thuật đệ quy Mục tiêu: Sau khi học xong bài này người học có khả năng: - Cài đặt được giải thuật đệ qui theo yêu cầu của các bài toán. - Giải thích được trình tự máy tính thực hiện khi thực thi các chương trình đệ qui.
- Rèn luyện kỹ năng lập trình, tư duy logic Bài tập mẫu Bài 1: Hãy viết giải thuật đệ quy tính số fibonaci thứ n; biết rằng f(n)=1 nếu n=1, f(n)=0 nếu n=0, f(n) = f(n-1)+ f(n-2) nếu n>1 Phân tích Từ công thức tính số fibonaci thứ n mà bài toán đặt rat a nhận thấy. - Trường hợp suy biến của bài toán xảy ra khi n<2 ta có f(n) =n - Trường hợp đệ quy xảy ra khi n>1: F(n) = F(n-1) + F(n-2) Giải thuật đệ quy để tính số fibonaci thứ n: int F(n){ if(n<2) return n; else return F(n-1) + F(n-2); } Lời giải mẫu using System; class Exam{ // Hàm đệ qui tính số fibonaci thứ n public static int f(int n){ if(n<2) return n; else return f(n-1)+f(n-2); } // Chương trình chính public static void main() { Console.ReadKey(); } Nhận xét Khi viết giải thuật đệ quy cần xác định được lời giải của giải thuật ứng với 2 trường hợp: suy biến và trường hợp đệ qui Kết quả thực hiện chương trình Bài tập thực hành môn CTDL> Trang 3 Bài 2: Viết chương trình thực hiện in ra tất cả các xâu nhị phân có độ dài N. Phân tích Ta có thuật toán tổng quát cho bài toán quay lui đệ quy. PROCEDURE TÌM(K:INTEGER); KHAI BÁO CÁC BIẾN NẾU CẦN; BEGIN NẾU LÀ BƯỚC SAU BƯỚC CUỐI CÙNG THÌ HIỆN NGHIỆM NGƯỢC LẠI VÒNG LẶP CHẠY CÁC TRƯỜNG HỢP CÓ THỂ LẤY LÀM PHẦN TỬ CỦA NGHIỆM BEGIN THỬ CHỌN MỘT ĐỀ CỬ TRONG CÁC BƯỚC TÌM PHẦN TỬ CỦA NGHIỆM; NẾU ĐỀ CỬ THỎA MÃN THÌ BEGIN LƯU PHẦN TỬ VÀO NGHIỆM; GHI NHẬN TRẠNG THÁI MỚI CỦA BÀI TOÁN; TÌM(K+1); TRẢ LẠI TRẠNG THÁI CỦ CỦA BÀI TOÁN TRƯỚC LÚC CHỌN ĐỀ CỬ; END; END; END; Theo yêu cầu của bài toán ta có thuật giải cho bài toán : - Mảng luu để lưu giá trị của chuỗi nhị phân - Đầu tiên ta sẽ tìm giá trị gán cho bít k=1 của chuỗi (là 0 or 1), sau đó gọi đệ quy tìm giá trị gán cho bít k=2, ….
- Lời gọi đệ quy kết thúc khi k== n+1 (nghĩa là ta đã thực hiện gán được giá trị cho n bít) khi đó sẽ hiển thị ra kết quả của chuỗi nhị phân Bài tập thực hành môn CTDL> Trang 4 Lời giải mẫu using System; namespace VDDeQui { class DeQui { static int n, d = 0; static int[] luu; // Hien thi ket qua ra man hinh static void Xuat(int[] luu) { for (int i = 0; i < n; i++) Console.0/n Bài 4: Tính S(x, n) = xn Gợi ý: Ta xây dựng hàm LuyThua(X, N) để tính XN với N dương. - Ta nhận thấy trường hợp suy biến: N=0 khi đó hàm nhận giá trị là 1 - Trường hợp đệ qui: N > 0 khi đó ta có LuyThua(X, N) = LuyThua(X, N- 1)*X Hàm S(x, n) sẽ được định nghĩa như sau: double S(x, n){ Bài tập thực hành môn CTDL> Trang 6 if(n<0) return 1.0/LuyThua(x, -n); else return LuyThua(x, n); } Bài 5: Tìm ước số lẻ lớn nhất của số nguyên dương n. Ví dụ : n = 100 ước lẻ lớn nhất của 100 là 25 Gợi ý: - Ta nhận thấy trường hợp suy biến: Nếu n%2==1 khi đó UocLeMax(n)= n - Trường hợp đệ qui: Ngược lại ta có UocLeMax(n) = UocLeMax(n/2) Bài 6: Tìm ước chung lớn nhất của 2 số nguyên dương a,b Gợi ý: Theo thuật toán Euclid: - Ta nhận thấy trường hợp suy biến: Nếu a%b ==0 thì UCLN(a, b) =b - Trường hợp đệ qui: ngược lại thì UCLN(a, b)= UCLN(b, a%b) Bài 7: Đếm số chữ số của số nguyên dương n Gợi ý: - Ta nhận thấy trường hợp suy biến: Nếu n==0 thì SoChuSo(n) =0 - Trường hợp đệ qui: ngược lại thì SoChuSo(n) = SoChuSo(n/10) +1 √ Bài 8: Tính S(n) = 1 + √2 + √3 + √4 + ⋯ … + √𝑛 − 1 + √𝑛 Gợi ý: Ta có giải thuật tính tổng căn như sau: float Can(int i, int n){ if(i==n) return sqrt(n) ; else return sqrt(i + Can(i+1, n)); } float S(int n){ return Can(1, n); } Bài tập thực hành môn CTDL> Trang 7 Bài 9: Hãy viết giải thuật cho bài toán: phân tích số N thành tổng các số tự nhiên. Viết chương trình hiển thị ra tất cả các cách có thể để phân tích số N thành tổng các số tự nhiên.
Gợi ý 5 = 1+4; =2+3=0+5 Theo yêu cầu của bài toán ta có thuật giải cho bài toán : - Mảng luu để lưu giá trị của các các số hạng được phân tích; tối đa có Số N được phân tích thành tổng có tối đa N số hạng, và ta cần 1 phần tử đầu tiên của mảng làm cờ; do đó Ta cần tạo luu là mảng có N+1 phần tử và được khởi gán giá trị ban đầu là 0. - Giả sử t là giá trị số được phân tích tính đến bước hiện tại, t ban đầu được khởi gán bằng 0. - Đầu tiên ta sẽ tìm giá trị gán cho số hạng đầu tiên (k=1), rồi gọi đệ quy tìm giá trị cho số hạng k = 2,. quá trình đệ qui kết thúc khi t == N(nghĩa là ta đã thực hiện tìm được lời giải) khi đó sẽ hiển thị ra kết quả của phép phân tích.
Giải thuật để phân tích số n thành tổng các số tự nhiên // k là số lượng số hạng được phân tích của tổng void Tao(int k) { int i,j; if (t == n) { d = d+1; // Tăng số nghiệm tìm thấy lên 1 // Hiển thị cách phân tích số N ra màn hình Console.WriteLine(luu[k-1]); } else for (j=1; j<=n; j++) if ((t+j<=n) && (j>= luu[k-1]) ) { luu[k]=j; t=t+j; Tao(k+1); t =t-j; luu[k]=0; } } Bài tập thực hành môn CTDL> Trang 8 Bài 10: Cho dãy số nguyên a1, a2, a3,. an; và một số nguyên. Phân tích số m thành tổng các phần tử của dãy không dùng lặp lại và đưa ra cách phân tích có ít số hạng nhất. Gợi ý - Mảng Kt để đánh dấu phần tử của mảng a đa được sử dụng, kt[i] = false nghĩa là a[i] đã được sử dụng, không thể dùng được nữa.
- Mảng luu để lưu nghiệm hiện tại - Mảng mluu để lưu nghiệm tối ưu thỏa mãn yêu cầu bài toán - Min để lưu số lượng phần tử ít nhất dùng để phân tích số m, min được khởi gán bởi giá trị min = 0 Thuật toán đệ qui tìm nghiệm tối ưu Thuật toán hiển thị kết quả nghiệm tối ưu của bài toán static void Tao(int k) { static void Xuat(){ int i,j; if (t == m) if(min==0) { if (min > k-1|| min==0) { Console.Write("Khong co cach phan tich thoa min=k-1; man yeu cau bai toan"); for (i=0 ;i< k-1; i=i+1) { mluu[i]=luu[i]; else{ } Console.WriteLine(mluu[min]); kt[j] = false; Tao(k+1); } t = t - a[j]; kt[j] = true; } luu[k]=0; } } Bài 11: Viết giải thuật đệ qui tìm USCLN của 2 số nguyên, áp dụng viết chương trình thực hiện nhập 2 số nguyên từ bàn phím, hiển thị ra màn hình USCLN, BSCNN của 2 số nguyên đó. Bài tập thực hành môn CTDL> Trang 9 Bài 10: Thực hành cài đặt danh sách bằng mảng Mục tiêu: - Hệ thống lại các kiến thức về mảng như: cách khai báo, cách thức tổ chức dữ liệu trên mảng, các thao tác trên mảng - Áp dụng các kiến thức về mảng để cài đặt được bài toán lưu trữ danh sách và thực hiện các thao tác trên mảng - Có khả năng nhận xét các ưu, nhược điểm khi cài đặt danh sách bằng mảng. Bài tập mẫu: Bài 1: Cho n số nguyên dương a0,a1,a2,.Chèn phần tử x vào vị trí k của dãy.Xóa tất cả các số nguyên tố trong dãy.Khởi tạo một mảng trung gian tmp có độ dài là (a.Length+1) sau đó gán các giá trị : tmp[i]=a[i] với i từ 0 ->k-1 (k-1 là vị trí trước vị trí cần chèn) tmp[k]=giá trị cần chèn tmp[i+1]=a[i] với i từ k->a.Length-1 Cuối cùng gán mảng a=tmp.Thực hiện duyệt các phần tử trong mảng kiểm tra xem phần tử đó có phải là số nguyên tố hay không (số nguyến tố là số chỉ chia hết cho 1 và chính nó) : + Nếu là số nguyên tố: Duyệt mảng từ trái sang phải.Từ vị trí số nguyên tố đó tiến hành dời các phần tử về phía trước cho đến khi kết thúc mảng, sau đó giảm kích thước mảng +Ngược lại, chuyển sang kiểm tra phần tử kế tiếp Lời giải mẫu using System; class VD { static int[] a; //Mảng a chứa các phần tử số nguyên static void Nhap() { int n; Bài tập thực hành môn CTDL> Trang 10 Console.ReadLine()); a = new int[n]; //Cấp phát cho mảng a số phần tử là n for (int i = 0; i < a.ReadLine()); } } static void Chen(int x, int k) //Chèn phần tử có giá trị x vào vị trí k trong mảng a { if (k >= 0 && k <= a.Length - 1) //Kiểm tra xem vị trí k có trong mảng không { int[] tmp = new int[a.Length + 1]; //Sau khi chèn mảng mới tmp sẽ thêm 1 phần tử for (int i = 0; i < k; ++i) tmp[i] = a[i]; tmp[k] = x; for (int i = k; i < a.Length; ++i) tmp[i + 1] = a[i]; a = tmp; } else Console.Length; for (int i = 0; i < n; ) if (ngto(a[i])) // Xóa số là nguyên tố { for (int j = i; j < a.Length - 1; ++j) //dồn các phần tử từ vị trí I đến cuối a[j] = a[j + 1]; n = n - 1; //sau khi xóa số phần tử trong mảng giảm đi 1 đơn vị } else ++i; int[] tmp = new int[n]; //mảng sau khi xóa có kích thước bằng n mới for (int i = 0; i < n; ++i) tmp[i] = a[i]; a = tmp; } static void Hien() { for (int i = 0; i < a.