cấu trúc dữ liệu sgu -Đh saigon

128
TRƯỜNG ĐẠI HC SÀI GÒN KHOA CÔNG NGHTHÔNG TIN BMÔN KHOA HC MÁY TÍNH -----------o0o---------- BÀI TP CU TRÚC DLIU VÀ GII THUT (lưu hành ni b) Năm 2010

Upload: leo-beckham

Post on 05-Jul-2015

4.322 views

Category:

Documents


8 download

TRANSCRIPT

Page 1: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 0

TRƯỜNG ĐẠI HỌC SÀI GÒN KHOA CÔNG NGHỆ THÔNG TIN

BỘ MÔN KHOA HỌC MÁY TÍNH -----------o0o----------

BÀI TẬP CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT

(lưu hành nội bộ)

Năm 2010

Page 2: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 1

Lời giới thiệu

Cấu trúc dữ liệu và giải thuật là học phần bắt buộc thuộc khối kiến thức cơ sở ngành của sinh viên các chuyên ngành công nghệ thông tin và cũng là nội dung quan trọng ở các kỳ thi tốt nghiệp, thi hoàn chỉnh đại học các chuyên ngành công nghệ thông tin.

Giáo trình này trình bày các chủ đề bài tập về:Tổng quan về cấu trúc dữ liệu và giải thuật, tìm kiếm, sắp xếp, cấu trúc danh sách liên kết và cấu trúc cây theo ngôn ngữ C/C++. Mỗi chủ đề được thiết kế gồm các phần: Tóm tắt lý thuyết, một số dạng bài tập điển hình và một số đề bài tập chọn lọc. Phần cuối của giáo trình có hướng dẫn giải cho một số bài tập tiêu biểu, đồng thời bổ sung một số đề thi mẫu để sinh viên tự rèn luyện kỹ năng phân tích vấn đề bài toán. Giáo trình chỉ trình bày vấn đề bài tập, còn các vấn đề lý thuyết liên quan thì bạn đọc có thể tham khảo chi tiết ở các quyển sách đã được chỉ ra ở phần tài liệu tham khảo.

Quyển giáo trình này được biên soạn để làm tài liệu tham khảo khi giảng các học phần cấu trúc dữ liệu và giải thuật ở hệ đại học và cao đẳng. Chúng tôi xin trân trọng giới thiệu với bạn đọc quyển giáo trình này và hy vọng rằng nó sẽ giúp cho việc giảng dạy và học tập học phần cấu trúc dữ liệu và giải thuật được thuận lợi hơn.

Thành phố Hồ Chí Minh, ngày 06 tháng 09 năm 2010

CÁC TÁC GIẢ

Page 3: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 2

Chương 1

TỔNG QUAN VỀ CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT A.Tóm tắt lý thuyết 1.Cấu trúc dữ liệu và giải thuật

Xuyên suốt trong giáo trình này, chúng tôi muốn đề cập đến hai mặt quan trọng của một vấn đề bài toán là cách thức tổ chức dữ liệu của bài toán và các phép xử lý trên các dữ liệu đó.

1.1.Cấu trúc dữ liệu Cấu trúc dữ liệu của bài toán là cách thức tổ chức dữ liệu sao cho phản ánh

chính xác dữ liệu của bài toán và có thể dùng máy tính để xử lý các dữ liệu đó một cách hiệu quả.

Một cấu trúc dữ liệu được đánh giá là tốt nếu nó thỏa mãn được các yêu cầu như: Phản ánh đúng thực tế bài toán, phù hợp với các thao tác xử lý trên đó, tiết kiệm được tài nguyên hệ thống,…

1.2.Giải thuật (trong giáo trình này chúng tôi đồng nhất khái niệm thuật toán và giải thuật) Giải thuật là một bảng liệt kê các chỉ dẫn (hay các qui tắc) cần thực hiện theo từng bước xác định nhằm giải quyết một vấn đề bài toán.

Các đặc trưng của giải thuật Tính xác định: Ở mỗi bước các chỉ dẫn phải rõ ràng. Tính kết thúc: Giải thuật phải dừng sau một số hữu hạn bước. Tính đúng đắn: Giải thuật phải cho ra kết quả đúng theo yêu cầu của

bài toán. Tính tổng quát: Giải thuật phải áp dụng được cho các bài toán cùng

loại. 1.3.Sự liên hệ giữa giải thuật và cấu trúc dữ liệu Giải thuật và cấu trúc dữ liệu có mối liên hệ chặt chẽ với nhau. Giải thuật

phản ánh các phép xử lý, còn đối tượng xử lý của giải thuật là các dữ liệu; dữ liệu chứa đựng các thông tin cần thiết để thực hiện giải thuật. Để xác định được giải thuật

Page 4: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 3

phù hợp cần phải biết nó tác động đến những loại dữ liệu nào và khi chọn lựa một cấu trúc dữ liệu cũng cần phải hiểu rõ những thao tác nào sẽ được tác động lên nó. 2.Độ phức tạp của một giải thuật

2.1.Phân tích thời gian thực hiện giải thuật Với một bài toán chỉ có một giải thuật. Việc chọn lựa giải thuật đưa đến kết

quả nhanh là một đòi hỏi quan trọng. Vấn đề là căn cứ vào những yêu tố nào để biết giải thuật này nhanh hơn giải thuật kia?

Rõ ràng thời gian thực hiện một giải thuật (hay chương trình để thực hiện giải thuật đó) phụ thuộc vào nhiều yếu tố. Một yếu tố cần chú ý đầu tiên tiên chính là kích thước của dữ liệu đưa vào. Chẳng hạn thời gian để sắp xếp một dãy số chịu ảnh hưởng bởi số lượng số của dãy số đó. Nếu gọi n là số lượng này, thì thời gian thực hiện T của một giải thuật được biểu diễn như một hàm của n: T(n).

Các kiểu lệnh và tốc độ xử lý của máy tính, ngôn ngữ viết chương trình và chương trình dịch ngôn ngữ ấy đều ảnh hưởng đến thời gian thực hiện chương trình; nhưng những yếu tố này không đồng đều với mỗi loại máy tính. Vì vậy không thể dựa vào chúng khi xác lập T(n). Điều đó cũng có nghĩa là T(n) không thể được biểu diễn thành đơn vị thời gian bằng giây, bằng phút được. Tuy nhiên không phải vì thế mà không thể so sánh được các giải thuật về mặt tốc độ. Nếu như thời gian thực hiện của một giải thuật là T1(n)=Cn2 và thời gian thực hiện giải thuật khác là T2(n)= kn (C, n, k là các hằng số nào đó), thì khi n khá lớn, thời gian thực hiện giải thuật t2 ít hơn so với giải thuật T1 , như vậy nếu nói thời gian thực hiện giải thuật T(n) tỉ lệ với với n2 hay tỉ lệ với n cũng cho ta ý niệm về tốc độ thực hiện giải thuật đó khi n khá lớn (với n nhỏ thì việc xét T(n) không có ý nghĩa). Cách đánh giá thời gian thực hiện giải thuật độc lập với máy tính và các yếu tố liên quan tới máy như vậy sẽ dẫn tới khái niệm về “cấp độ lớn của thời gian thực hiện giải thuật” hay còn gọi là “độ phức tạp tính toán của giải thuật”.

2.2.Thời gian chạy của các lệnh Lệnh gán Lệnh gán có dạng X = <biểu thức>

Page 5: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 4

Thời gian chạy của lệnh gán là thời gian thực hiện biểu thức. Trường hợp hay gặp nhất là biểu thức chỉ chứa các phép toán sơ cấp, và thời gian thực hiện nó là O(1). Nếu biểu thức chứa các lời gọi hàm thì ta phải tính đến thời gian thực hiện hàm, và do đó trong trường hợp này thời gian thực hiện biểu thức có thể không còn phải O(1).

Lệnh lựa chọn if (<điều kiện>) <lệnh 1>; else <lệnh 2>; Giả sử thời gian đánh giá điều kiện là T0(n), thời gian thực hiện <lệnh 1> là

T1(n), thời gian thực hiện <lệnh 2> là T2(n). Thời gian thực hiện lệnh lựa chọn if-else sẽ là thời gian lớn nhất trong các thời gian T0(n) + T1(n) và T0(n) + T1(n).

Trường hợp hay gặp là kiểm tra điều kiện chỉ cần O(1). Khi đó nếu T1(n) = O(f(n)), T2(n) = O(g(n)) và f(n) tăng nhanh hơn g(n) thì thời gian chạy của lệnh if-else là O(f(n)); còn nếu g(n) tăng nhanh hơn f(n) thì lệnh if-else cần thời gian O(g(n)).

Thời gian chạy của lệnh lựa chọn switch được đánh giá tương tự như lệnh if-else, chỉ cần lưu ý rằng, lệnh if-else có hai khả năng lựa chọn, còn lệnh switch có thể có nhiều hơn hai khả năng lựa chọn. Các lệnh lặp

for, while, do-while Để đánh giá thời gian thực hiện một lệnh lặp, trước hết ta cần đánh giá số tối

đa các lần lặp, giả sử đó là L(n). Sau đó đánh giá thời gian chạy của mỗi lần lặp, chú ý rằng thời gian thực hiện thân của một lệnh lặp ở các lần lặp khác nhau có thể khác nhau, giả sử thời gian thực hiện thân lệnh lặp ở lần thứ i (i=1,2,..., L(n)) là Ti(n). Mỗi lần lặp, chúng ta cần kiểm tra điều kiện lặp, giả sử thời gian kiểm tra là T0(n). Như vậy thời gian chạy của lệnh lặp là:

( ) ( )( )∑=

+)(

10

nL

ii nTnT

Page 6: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 5

Công đoạn khó nhất trong đánh giá thời gian chạy của một lệnh lặp là đánh giá số lần lặp. Trong nhiều lệnh lặp, đặc biệt là trong các lệnh lặp for, ta có thể thấy ngay số lần lặp tối đa là bao nhiêu. Nhưng cũng không ít các lệnh lặp, từ điều kiện lặp để suy ra số tối đa các lần lặp, cần phải tiến hành các suy diễn không đơn giản.

Trường hợp hay gặp là: kiểm tra điều kiện lặp (thông thường là đánh giá một biểu thức) chỉ cần thời gian O(1), thời gian thực hiện các lần lặp là như nhau và giả sử ta đánh giá được là O(f(n)); khi đó, nếu đánh giá được số lần lặp là O(g(n)), thì thời gian chạy của lệnh lặp là O(g(n)f(n)).

2.3.Độ phức tạp tính toán của giải thuật Nếu thời gian thực hiện một giải thuật là T(n)=Cn2 (với C là hằng số) thì ta

nói: độ phức tạp tính toán của giải thuật này có cấp là n2 (hay cấp độ lớn của thời gian thực hiện giải thuật là n2) và ta ký hiệu T(n) = O(n2) - ký hiệu chữ O lớn. Một cách tổng quát ta có thể định nghĩa như sau:

Một hàm f(n) được xác định là O(g(n)) f(n)=O(g(n)) và được gọi là có cấp g(n) nếu tồn tại một hằng số C và no sao

cho f(n) ≤ C.g(n) khi n ≥ no Nghĩa là f(n) bị chặn trên bởi một hằng số nhân với g(n), với mọi giá trị của n

từ một điểm nào đó. Chú ý rằng O(C(f(n))=O(f(n)) Để xác định độ phức tạp tính toán của một giải thuật bất kỳ có thể dẫn tới

những bài toán phức tạp. Tuy nhiên trong thực tế, đối với một số giải thuật ta cũng có thể phân tích được bằng một số quy tắc đơn giản.

Quy tắc cộng Giả sử T1(n) và T2(n) là thời gian thực hiện của hai đoạn chương trình P1 và

P2 mà T1(n) = O(f(n)) và T2(n)=O(g(n)), thì thời gian thực hiện P1 rồi P2 tiếp theo sẽ là T1(n)+ T2(n) = O( max(f(n), g(n))

Chẳng hạn đoạn lệnh for (int i=1;i<=n;i++)

x=x+1; có thời gian thực hiện là O(n.1) = O(n) Quy tắc nhân

Page 7: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 6

Nếu tương ứng với P1 và P2 là T1(n) và T2(n), T1(n) = O(f(n)) và T2(n)=O(g(n)), thì thời gian thực hiện P1 và P2 lồng nhau là T1(n) * T2(n) = O(f(n).g(n))

Chẳng hạn đoạn lệnh for (int i=1;i<=n;i++)

for (int j=1;j<=n;j++ ) x=x+1;

có thời gian thực hiện là O(n.n)=O(n2). Chú ý rằng có những trường hợp giải thuật không phải chỉ thuộc vào kích

thước của dữ liệu vào mà còn phụ thuộc vào chính tính trạng của dữ liệu đó nữa. Chẳng hạn việc sắp xếp một dãy số theo thứ tự tăng dần nếu gặp dãy số đưa vào đã có đúng thứ tự thì sẽ khác với trường hợp dãy số đưa vào chưa có thứ tự hoặc có thứ tự ngược lại, lúc đó khi phân tích thời gian thực hiện giải thuật ta sẽ phải xét tới: đối với mọi dữ liệu vào có kích thước n thì T(n) trong trường hợp thuật lợi nhất là thế nào? rồi T(n) trong trường hợp xấu nhất và T(n) trung bình ? Việc xác định T(n) trung bình thường khó vì sẽ phải dùng tới những công cụ toán phức tạp. Trong các trường hợp mà T(n) trung bình khó xác định người ta thường đánh giá giải thuật qua giá trị xấu nhất của T(n).

2.4.Sự phân lớp các giải thuật Thông thường các hàm thể hiện độ phức tạp tính toán của giải thuật có dạng

hằng số, log2n, n, nlogn, n2, n3, 2n, n!, nn,… Hằng số:Hầu hết các chỉ thị của các chương trình đều được thực hiện một lần

hay một số số lần nhất định không phụ thuộc vào n Các hàm như 2n, n!, nn được gọi là hàm mũ. Một giải thuật mà thời gian thực

hiện của nó có cấp là các hàm loại mũ thì tốc độ rất chậm. Các hàm log2n, n, nlogn, n2, n3 được gọi là các hàm loại đa thức. Giải thuật với thời gian thực hiện có cấp hàm đa thức thì thường là chấp nhận được.

Các cấp độ thời gian chạy của giải thuật và tên gọi của chúng được liệt kê trong bảng sau:

Ký hiệu ô lớn Tên gọi O(1) hằng

Page 8: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 7

O(logn) O(n) O(nlogn) O(n2) O(n3) O(2n)

logarit tuyến tính nlogn bình phương lập phương Mũ

B.Các dạng bài tập Dạng 1: Bài toán với cấu trúc dữ liệu là mảng một chiều

Ví dụ 1.1: Cộng hai số nguyên lớn Cho hai số nguyên lớn a và b; a có m chữ số và b có n chữ số. Hãy viết

chương trình tính tổng a+b. Giải thuật Số nguyên lớn ở đây là số có thể có đến vài nghìn chữ số. Để lưu trữ các số

nguyên lớn này ta có thể dùng chuỗi (mỗi ký tự của chuỗi là một chữ số) hoặc dùng mảng một chiều (mỗi phần tử của mảng một chiều là một chữ số). Tuy nhiên trong hai phương án này thì phương án dùng mảng một chiều để lưu trữ sẽ có giải thuật tốt hơn.

Giải thuật này có thể trình bày ngắn gọn như sau: Bước 1:Nhập hai số nguyên lớn a,b. Để có thể thực hiện được phép a+b một

cách tự nhiên thì khi nhập a,b thì a và b phải được giống hàng bên phải. Ví dụ: Giả sử a có m=5 chữ số, b có n=4 chữ số như sau: a = 97895 b = 6478 Thì việc lưu trữ hai số này là như sau: a[1] = 9, a[2]=7, a[3]=8, a[4]=9, a[5]=5

b[2] = 6, b[3]=4, b[4]=7, b[5]=8 Việc giống hàng bên phải cho a và b có thể tiến hành bằng cách đặt max là số lớn nhất trong hai số m và n. for i=max-m+1 to max cin>>a[i]; for i=max-n+1 to max

Page 9: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 8

cin>>b[i]; Bước 2: Thực hiện phép hai số a,b theo cách thông thường: Nghĩa là cộng từng cặp

chữ số a[i], b[i] bắt đầu từ phía bên phải và lưu kết quả cuối cùng lưu vào mảng c. Lưu ý các mảng a,b bắt đầu từ chỉ số 1, còn mảng c bắt đầu từ chỉ số 0 và c[0] có thể bằng 0 (khi phép cộng hai chữ số cuối cùng không có nhớ) và cũng có thể khác 0 (khi phép cộng hai chữ số cuối cùng có nhớ). sonho=0; for (i=max;i >0;i--) {

c[i]=(a[i]+b[i]+ sonho)%10; sonho =(a[i]+b[i]+ sonho)/10; } c[0]= sonho;

Bước 3: Xuất mảng c kết quả ra màn hình. Lưu ý là chỉ nên xuất c[0] khi c[0] khác 0.

Chương trình 1-1 Dạng 2: Bài toán với cấu trúc dữ liệu là mảng hai chiều

Ví dụ 1.2.Ma trận phân số Cho ma trận hai chiều m dòng, n cột; trong đó mỗi phần tử là một phân số

(giả sử tử số và mẫu số của các phân số này là các số nguyên dương). Hãy thực hiện các yêu cầu sau:

a.Tìm ma trận phân số tối giản b.Tìm phân số có giá trị lớn nhất. Giải thuật Một cấu trúc dữ liệu tốt cho bài toán này là định nghĩa một kiểu dữ liệu mới

kiểu bản ghi để lưu trữ dữ liệu là các phân số như sau: struct phanso

{ int tuso;

Page 10: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 9

int mauso; };

-Phân số tối giản là phân số mà ước số chung lớn nhất của tử số và mẫu số bằng 1. Để tìm bảng phân số tối giản ta chỉ cần tối giản từng phân số.

Lưu ý là để so sánh phân số 1 có lớn phân số 2 hay không ta dùng điều kiện: ps1.tu*ps2.mau>ps2.tu*ps1.mau

Chương trình 1-2 #include <conio.h> #include <math.h> #include <iostream.h> #include <stdio.h> #define maxm 50 #define maxn 50 struct phanso {

int tu; int mau;

}; void nhapmangps(phanso ps[maxm][maxn], int &m, int &n); void xuatmangps(phanso ps[maxm][maxn], int m, int n); int sosanhps(phanso ps1, phanso ps2); void bangphansotoigian(phanso ps[maxm][maxn], int m, int n); void timpslonnhat(phanso ps[maxm][maxn], int m, int n); void main() {

clrscr(); phanso ps[maxm][maxn]; int m,n; nhapmangps(ps,m,n); bangphansotoigian(ps,m,n); xuatmangps(ps,m,n);

Page 11: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 10

timpslonnhat(ps,m,n); getch();

} void nhapmangps(phanso ps[maxm][maxn], int &m, int &n) { cout<<"Nhap m = ";cin>>m; cout<<"Nhap n = ";cin>>n; for (int i=0; i<m;i++) for (int j=0; j<n;j++) { cin>>ps[i][j].tu; cin>>ps[i][j].mau; } } void xuatmangps(phanso ps[maxm][maxn], int m, int n) { cout<<endl; for (int i=0; i<m;i++) { for (int j=0; j<n;j++) cout<<ps[i][j].tu<<"/"<<ps[i][j].mau<<";"; cout<<endl; } } int sosanhps(phanso ps1, phanso ps2) {

return ps1.tu*ps2.mau>ps2.tu*ps1.mau>0; } int uscln(int a, int b) {

Page 12: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 11

int r=a%b; while (r!=0) { a=b; b=r; r=a%b; } return b; } void bangphansotoigian(phanso ps[maxm][maxn], int m, int n) { for (int i=0; i<m;i++) for (int j=0; j<n;j++) { int uc=uscln(ps[i][j].tu,ps[i][j].mau); ps[i][j].tu=ps[i][j].tu/uc; ps[i][j].mau=ps[i][j].mau/uc; } } void timpslonnhat(phanso ps[maxm][maxn], int m, int n) { phanso psmax; psmax.tu=ps[0][0].tu; psmax.mau=ps[0][0].mau; for (int i=0; i<m;i++) for (int j=0; j<n;j++) if (sosanhps(ps[i][j],psmax)>0) psmax=ps[i][j]; cout<<"\nPhan so lon nhat la : "<<psmax.tu<<"/"<<psmax.mau; }

Dạng 3: Cấu trúc dữ liệu kiểu bản ghi

Page 13: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 12

Ví dụ 1.2.Bài toán quản lý Cho một danh sách lưu thông tin của các nhân viên trong một công ty, thông

tin gồm : - Mã nhân viên (chuỗi, tối đa là 8 ký tự) - Họ và tên (chuỗi, tối đa là 20 ký tự) - Phòng ban (chuỗi, tối đa 10 ký tự) - Lương cơ bản (số nguyên) - Thưởng (số nguyên) - Thực lãnh (số nguyên, trong đó thực lãnh = lương cơ bản + thưởng )

Hãy thực hiện các công việc sau: a.Tính tổng thực lãnh tháng của tất cả nhân viên trong công ty. b.In danh sách những nhân viên có mức lương cơ bản thấp nhất. c.Đếm số lượng nhân viên có mức thưởng >= 1200000. d.In danh sách các nhân viên tăng dần theo phòng ban, nếu phòng ban trùng nhau thì giảm dần theo mã nhân viên. Chương trình 1-3 #include<conio.h> #include<iostream.h> #include<stdlib.h> #include<stdio.h> #include<string.h> struct nhanvien { char manv[8]; char hoten[20]; char phongban[16]; float luongcb; float thuong; float thuclanh; }; int n;

Page 14: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 13

struct nhanvien nv[100],temp; void nhap(nhanvien nv[], int &n); void xuat(nhanvien nv[], int n); void tongthuclanh(nhanvien nv[], int n); void luongcbthapnhat(nhanvien nv[], int n); void mucthuong(nhanvien nv[], int n); void sapxep(nhanvien nv[], int n); void main() { clrscr(); nhap(nv,n); tongthuclanh(nv,n); luongcbthapnhat(nv,n); mucthuong(nv,n); sapxep(nv,n); xuat(nv,n); getch(); } void nhap(nhanvien nv[], int &n) { cin>>n; for(int i=0;i< n;i++) { cout<<"\nManv:"; fflush(stdin); gets(nv[i].manv); cout<<"Ho ten:"; fflush(stdin); gets(nv[i].hoten); cout<<"Phong Ban:"; fflush(stdin); gets(nv[i].phongban); cout<<"Luong CB:"; cin>>nv[i].luongcb;

Page 15: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 14

cout<<"Thuong:"; cin>>nv[i].thuong; nv[i].thuclanh = nv[i].luongcb + nv[i].thuong; } } void xuat(nhanvien nv[], int n) { for(int i=0;i<n;i++) cout<<nv[i].manv<<nv[i].hoten<<nv[i].phongban<<nv[i].luongcb<<nv[i].thuong<<nv[i].thuclanh; } void tongthuclanh(nhanvien nv[], int n) { long tong=0; for(int i=0;i<n;i++) tong=tong +nv[i].thuclanh; cout<<"\nTong thuc lanh la "<<tong; } void luongcbthapnhat(nhanvien nv[], int n) { long min=nv[0].luongcb; for(int i=1;i<n;i++) if (nv[i].luongcb<min) min=nv[i].luongcb; cout<<"\nLuong co ban thap nhat la:"<<min; } void mucthuong(nhanvien nv[], int n) { int dem=0; for(int i=0;i<n;i++)

Page 16: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 15

if (nv[i].thuong>=1200000) dem++; cout<<"\nSo luong nhan vien co muc thuong >=1200000 la: "<<dem; } void sapxep(nhanvien nv[], int n) { for(int i=0;i<n-1;i++) for(int j=i+1;j<n;j++) if ( strcmp(nv[i].phongban,nv[j].phongban) > 0 || (strcmp(nv[i].phongban,nv[j].phongban)==0 && strcmp(nv[i].manv,nv[j].manv)<0)) { temp = nv[i]; nv[i] = nv[j]; nv[j] = temp; } }

Dạng 4: Cấu trúc dữ liệu dạng đồ thị Ví dụ 1-4.Dãy con chung dài nhất. Cho hai dãy số a, b. Hãy tìm dãy con chung dài nhất của hai dãy này. Dãy

con ở đây không nhất thiết phải là các phần tử liền nhau Chẳng hạn cho hai dãy số sau: Dãy a: 1, 5, 3, 7, 8 Dãy b: 6, 2, 5, 7, 1, 9, 8 Gợi ý: Một cấu trúc dữ liệu cho bài toán này là chuyển bài toán này về cấu trúc đồ

thị bằng cách thành lập một ma trận kề, trong đó a[i][j] =1 nếu phần tử thứ i của dãy a trùng với phần tử thứ j trong dãy b. Chẳng hạn với ví dụ trên thì ta có ma trận kề như sau:

0 0 0 0 1 0 0

Page 17: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 16

0 0 1 0 0 0 0

0 0 0 0 0 0 0

0 0 0 1 0 0 0

0 0 0 0 0 0 1

Khi đó dãy cần tìm là dãy ứng với đường đi xuống dài nhất trong ma trận kề: Là phần tử ở các vị trí 2, 4, 5 của dãy a hoặc là các vị trí 3, 4, 7 của dãy b. Dạng 5: Tối ưu hóa cấu trúc Ví dụ 1.5.Dãy con có tổng lớn nhất Cho dãy n số nguyên {a} Dãy con liên tiếp là dãy mà thành phần của nó là các thành phần liên tiếp nhau trong {a}, ta gọi tổng của dãy con là tổng tất cả các thành phần của nó. Tìm tổng lớn nhất trong tất cả các tổng của các dãy con của {a} Chẳng hạn n = 7 số sau:

4 –5 6 –4 2 3 -7 Thì kết quả tổng dãy con cần tìm là 7.

Giải thuật 1: Giải thuật đơn giản nhất có thể viết ngay là: xét tất cả các cặp số nguyên L và U thỏa mãn 1 ≤ L ≤ U ≤ n; đối với mỗi cặp như vậy ta tính tổng của dãy con a[L..U] và so sánh tổng này với giá trị lớn nhất hiện có:

for (L=1;L<=n;L++) for (U=L;U<=n;U++) {

sum=0; for (int I=L;I<=U;I++)

sum=sum+a[I]; maxsofar=max(maxsofar,sum);

} chương trình này tuy ngắn và dễ hiểu, tuy nhiên giải thuật này có độ phức tạp là O(n3)

Giải thuật 2:

Page 18: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 17

Ta có thể cải tiến giải thuật trên để có giải thuật với độ phức tạp là O(n2) bằng cách sử dụng hệ thức :

Tổng a[L..U]= Tổng a[L..U-1]+a[U] maxsofar=0; for (L=1;L<=n;L++) {

sum=0; for (U=L;U<=n;U++)

{ sum=sum+a[U];

maxsofar=max(maxsofar,sum); } }

Giải thuật 3: Tổng lớn nhất trong dãy con a[1..i] là tổng lớn nhất trong dãy con a[1..i-1](gọi là maxsofar) hoặc tổng lớn nhất trong tất cả các tổng của các dãy con kết thúc tại i (gọi là maxendinghere). Chúng ta có nhận xét rằng: Dãy con lớn nhất kết thúc tại i là dãy con lớn nhất kết thúc tại vị trí i-1 được bổ sung thêm phần tử a[i] ở cuối hoặc là dãy con rỗng trong trường hợp tổng của dãy con nhận được là số âm. Ta có giải thuật như sau:

maxsofar=0; maxendinghere=0; for (i=1; i<=n;i++) { maxendinghere=max(maxendinghere+a[i],0); maxsofar=max(maxsofar,maxendinghere); } Minh họa cho Giải thuật 3:

1 2 3 4 5 6 7 a[i] 4 -5 6 -4 2 3 -7

maxendinghere 4 0 6 2 4 7 0

Page 19: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 18

maxsofar 4 4 6 6 6 7 7 Giải thuật 3 này có độ phức tạp là O(n). #include <iostream.h> #include <conio.h> void algorithm1(int a[], int n); void algorithm2(int a[], int n); void algorithm3(int a[], int n); int max(int a,int b); void input(int a[],int &n); void main() { int a[100],n; input(a,n); algorithm1(a,n); getch(); } void input(int a[],int &n) { clrscr(); cin>>n; for (int i=1;i<=n;i++) cin>>a[i]; } void algorithm1(int a[], int n) { int maxsofar=0; for (int L=1;L<=n;L++)

Page 20: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 19

for (int U=L;U<=n;U++) { int sum=0; for (int I=L;I<=U;I++) sum=sum+a[I]; maxsofar=max(maxsofar,sum); } cout<<maxsofar; } void algorithm2(int a[], int n) { int maxsofar=0; for (int L=1;L<=n;L++) { int sum=0; for (int U=L;U<=n;U++) { sum=sum+a[U]; maxsofar=max(maxsofar,sum); } } cout<<maxsofar; } void algorithm3(int a[], int n) { int maxsofar=0; int maxendinghere=0; for (int i=1; i<=n;i++) { maxendinghere=max(maxendinghere+a[i],0);

Page 21: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 20

maxsofar=max(maxsofar,maxendinghere); } cout<<maxsofar; } int max(int a,int b) { return a>b?a:b; }

Page 22: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 21

C.Bài tập Viết chương trình hoàn chỉnh cho các bài toán sau đây BT1-1.Cho dãy n số nguyên a0,a1,...,an-1.

a.Tìm chiều dài của dãy con dài nhất chứa toàn số chẵn. b.Tìm dãy con liên tiếp tăng dài nhất. c.Tìm giá trị lớn thứ k của dãy. d.Tìm dãy con tăng chứa nhiều số nguyên tố nhất. e.Tìm tần số xuất hiện của các số. Cho biết độ phức tạp tính toán của các thuật toán trên.

BT1-2.Cho dãy n số nguyên a0,a1,...,an-1. Hãy chuyển k phần tử đầu tiên của dãy về cuối dãy. BT1-3.Cho hai số nguyên lớn a và b; trong đó a có m chữ số và số b có n chữ số. Viết chương trình thực hiện các phép cộng, trừ, nhân hai số nguyên lớn. BT1-4.Xây dựng cấu trúc dữ liệu để lưu trữ đa thức có bậc tự nhiên n (0 ≤ n ≤ 100) trên trường số nguyên (ai , x ∈ Z).

∑=

=n

i

iin xaxf

0)(

Với cấu trúc dữ liệu được xây dựng, hãy viết chương trình thực hiện các công việc sau:

a.Tính giá trị của đa thức tại giá trị x0 nào đó. b.Tính tổng, hiệu, tích, thương hai đa thức p và q.

BT1-5.Cho mảng một chiều gồm n tọa độ điểm (giả sử hoành độ và tung độ của các điểm là các số nguyên).

a.Tìm một điểm trong mảng xa gốc tọa độ nhất. b.Tìm tọa độ hai điểm gần nhau nhất. c.Xác định tọa độ của hình chữ nhật nhỏ nhất (tọa độ góc trên bên trái và tọa độ góc dưới bên phải của hình chữ nhật) bao hết cả n điểm trên. Ví dụ n = 5 và tọa độ 5 điểm là: (0,0); (0,3); (3,3); (4,1); (4,4). Thì kết quả câu a là điểm (4,4), kết quả câu b là (3,3) và (4,4), kết quả câu c

là (0,4); 4(,0).

Page 23: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 22

BT1-6.Cho ma trận hai chiều m dòng n cột; trong đó mỗi phần tử là một phân số (giả sử các tử số và mẫu số của các phân số là các số nguyên).

a.Tính tổng giá trị của các phân số trong mảng (kết quả là phân số ở dạng tối giản).

b.Tính tích giá trị của các phân số trong mảng (kết quả là phân số ở dạng tối giản).

c.Sắp xếp các phân số theo chiều tăng dần từ trái qua phải (theo dòng) và từ trên xuống dưới (theo cột). BT1-7.Cho ma trận hai chiều m dòng n cột (m,n>=3); các phần tử là các số nguyên dương. a.Hãy chuyển các số của ma trận về số chính phương nhỏ nhất lớn hơn hoặc bằng nó. b.Hãy chuyển các số của ma trận về số nguyên tố gần nó nhất c.Tìm một mặt nạ 3 dòng 3 cột chứa nhiều số nguyên tố nhất (mặt nạ di có các cạnh song song với các cạnh của ma trận). BT1-8.Cho một tập tin văn bản có tên là “BANGSO.INP” có cấu trúc như sau: -Dòng đầu tiên ghi hai số m và n. -Trong m dòng tiếp theo mỗi dòng ghi n số nguyên; các số cách nhau ít nhất một khoảng cách. a.Xoay vòng các cột qua phải một vị trí (cột 0 sẽ qua cột 1, cột 1 qua cột 2,... cột cuối cùng n-1 sẽ về vị trí của cột 0). b.Xoay vòng các dòng xuống dưới một vị trí (dòng 0 sẽ qua dòng 1, dòng 1 qua dòng 2,... dòng cuối cùng n-1 sẽ về vị trí của dòng 0). c.Xoay vòng các dòng xuống dưới k vị trí (ví dụ khi k =2 thì nghĩa là dòng 0 chuyển đến dòng 2, dòng 1 chuyển đến dòng 3,… dòng n-1 chuyển đến dòng 1). Kết quả của các câu a,b,c này được ghi vào tập tin BANGSO.OUT BT1-9.Khai báo kiểu cấu trúc dữ liệu mảng mà mỗi phần tử chứa thông tin về một quyển sách bao gồm các trường: Mã số sách, tên sách, tác giả, năm xuất bản.

a.Đếm số sách xuất bản năm X. b.Sắp xếp danh sách theo mã số sách tăng.

BT1-10.Trong mặt phẳng OXY cho đa giác lồi A1,A2,…,An

Page 24: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 23

a.Tính chu vi của đa giác. b.Tính diện tích của đa giác.

BT1-11.Xét tập tất cả phân số tối giản (số hữu tỉ) giữa 0 và 1 với mẫu số nhỏ hơn hoặc bằng N.

Ví dụ, với N = 5, ta có tập sau: 0/1 1/5 1/4 1/3 2/5 1/2 3/5 2/3 3/4 4/5 1/1 Cho số nguyên N (1 <= N <= 500), viết chương trình in ra số lượng các phân

số và in ra một phần các phân số đó để chứng tỏ bạn làm đúng. BT1-12.Tìm độ phức tạp của các thuật toán sau:

a.Tìm giá trị lớn nhất của một dãy số. b.Tìm ước số chung lớn nhất của 2 số nguyên dương a,b. c.Kiểm tra xem n có phải là số nguyên tố hay không ?

Page 25: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 24

Chương 2

TÌM KIẾM VÀ SẮP XẾP A.Tóm tắt lý thuyết 1.Phương pháp tìm kiếm

1.1.Bài toán tìm kiếm Cho dãy n số nguyên a0,a2,...,an-1 và một số nguyên x. Hãy tìm xem x có

thuộc vào dãy số trên hay không ? Nếu tìm được ở vị trí thứ i thì xuất kết quả là i, ngược lại nếu không tìm thấy thì xuất kết quả là –1 (chú ý dãy bắt đầu từ chỉ số 0).

Sau đây là hai giải thuật tìm kiếm thường được sử dụng nhất. 1.2.Tìm kiếm tuyến tính Bắt đầu từ phần tử thứ nhất a[0], ta lần lượt so sánh x với các giá trị a[i]. Nếu

có a[i] bằng x thì i chính là kết quả cần tìm và kết thúc giải thuật. Nếu trong dãy không có số a[i] nào bằng x thì xuất kết quả là –1 và cũng kết thúc giải thuật.

int LinearSearch ( int a[], int n, int x ) {

int i = 0; while ( i<n && a[i]!=x)

i++; if( i==n) return -1; // tìm hết nhưng không có x return i; // tìm thấy a[i] là phần tử có khóa x

} Hiệu quả của giải thuật được nâng cao bằng cách đặt thêm phần tử cầm canh

(sentinel) ở cuối mảng (a[n]=x) để bảo đảm rằng trong dãy a[i] lúc này luôn có phần tử bằng x và vòng lặp while luôn kết thúc. Do đó không cần kiểm tra điều kiện (i<n) nữa.

int LinearSearch ( int a[], int n, int x ) { int i = 0; // mảng gồm n phần tử từ a[0]...a[n-1] a[n] = x; // thêm phần tử thứ n+1

Page 26: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 25

while (a[i]!=x) i++; if( i==n) return -1; // tìm hết nhưng không có x return i; // tìm thấy x tại vị trí i } Giải thuật tìm kiếm tuyến tính có độ phức tạp tính toán là O(n). 1.3.Tìm kiếm nhị phân (với phương pháp tìm kiếm nhị phân thì dãy n số a0,a1,a2,..,an-1 phải có thứ tự

- giả sử đó là thứ tự không giảm). Giả sử dãy tìm kiếm hiện hành bao gồm các phần tử aleft,…,arịght. Gọi midle=(left+right)/2 Nhận xét rằng nếu x > a[i] thì x chỉ có thể xuất hiện bên phải a[i] - nghĩa là

trong đoạn [amidle+1, aright] của dãy, ngược lại nếu x < a[i] thì x chỉ có thể xuất hiện bên trái a[i] - trong đoạn [aleft, amidle-1] của dãy, nhờ vậy giải thuật sẽ thu gọn phạm vi tìm kiếm một cách đáng kể. Mỗi lần so sánh loại được một nửa thông tin không có ích.

Ví dụ; Cho dãy số 1 3 4 5 6 7 8 9 10 12

Cần tìm phần tử x=3 Bước 1: left=0, right=9, mid=4, a[mid]= 6 Do x < a[mid] nên right=3. Do left<=right tiếp tục qua bước 2. Bước 2: left=0, right=3, mid=1, a[mid]= 3 Do x = a[mid] nên vị trí cần tìm là mid =1. Giả sử cần tìm phần tử x=11 Bước 1: left=0, right=9, mid=4, a[mid]= 6 Do x > a[mid] nên left=5. Do left<=right tiếp tục qua bước 2. Bước 2: left=5, right=9, mid=7, a[mid]= 9 Do x > a[mid] nên left=8.

Page 27: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 26

Do left<=right tiếp tục qua bước 3. Bước 3: left=8, right=9, mid=8, a[mid]= 10 Do x > a[mid] nên left=9. Do left<=right tiếp tục qua bước 4. Bước 4: left=9, right=9, mid=9, a[mid]= 12 Do x < a[mid] nên left=10. Do left> right vòng lặp kết thúc và kết quả trả về là -1 Cài đặt theo kiểu đệ quy

int BinarySearch_Recursive(int a[],int n,int x,int left,int right) {

if (left>right) return -1; int mid=(left+right)/2; if (x==a[mid]) return mid; if (x<a[mid]) return BinarySearch_Recursive(a,n,x,left,mid-1); return BinarySearch_Recursive(a,n,x,mid+1,right);

} Cài đặt theo kiểu không đệ quy

int BinarySearch(int a[],int n,int x) {

int left=0,right=n-1,mid; do {

mid=(left+right)/2; if (x==a[mid]) return mid; else if (x<a[mid]) right=mid -1; else left=mid+1;

Page 28: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 27

} while (left<=right); return -1;

} Giải thuật tìm kiếm nhị phân có độ phức tạp tính toán là O(log2n)

2.2.Phương pháp sắp xếp 2.1.Bài toán sắp xếp

Cho một dãy n số, hãy sắp xếp dãy số theo thứ tự tăng dần (hoặc giảm dần). Ví dụ dãy ban đầu : S1 = {5 6 1 9 8 6} Dãy sau khi biến đổi: S2 = {1 5 6 6 8 9}

•Khái niệm nghịch thế Xét một mảng n số a0, a1,.., an-1. Nếu có i < j và ai > aj, thì ta gọi đó là một

nghịch thế. Mảng chưa sắp xếp sẽ có nghịch thế, và ngược lại mảng đã có thứ tự sẽ

không chứa nghịch thế. Để sắp xếp một mảng, ta có thể tìm cách làm giảm số các nghịch thế trong mảng này bằng cách hoán vị các phần tử ai,aj nếu có i < j và ai > aj theo một quy luật nào nào đó.

Hai thao tác so sánh và gán là những thao tác cơ bản của hầu hết các giải thuật sắp xếp, các kết quả của các phép so sánh cho phép giải thuật quyết định những tính huống cần phải thay đổi vị trí trong dãy.

Sau đây là một số phương pháp sắp xếp thường dùng nhất. 2.2.Phương pháp đổi chổ trực tiếp Như đã đề cập ở trên, để sắp xếp một dãy số, ta có thể xét các nghịch thế có

trong dãy và làm triệt tiêu dần chúng đi. Ý tưởng chính của giải thuật đổi chỗ trực tiếp là xuất phát từ đầu dãy, tìm tất cả nghịch thế chứa phần tử này, triệt tiêu chúng bằng cách đổi chỗ phần tử này với phần tử tương ứng trong cặp nghịch thế. Lặp lại quá trình trên với các phần tử tiếp theo trong dãy.

Ví dụ: Cho dãy số 5 6 1 9 8 6 Thì kết quả từng bước như sau:

dòng i j a[0] a[1] a[2] a[3] a[4] a[5] 0 0 2 5 6 1 9 8 6

Page 29: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 28

1 1 2 1 6 5 9 8 6 2 3 4 1 5 6 9 8 6 3 3 5 1 5 6 8 9 6 4 4 5 1 5 6 6 9 8 5 1 5 6 6 8 9

void interchangesort(int a[], int n) { for (int i=0; i<n-1;i++) for (int j=i+1;j<n ;j++) if (a[i]>a[j]) swap(a[i],a[j]); } Độ phức tạp tính toán của giải thuật là: O(n2) 2.3.Phương pháp chọn trực tiếp Nhận xét ta thấy, với mảng có thứ tự (không giảm), phần tử ai là min

(ai,ai+1,..,an-1). Ý tưởng của giải thuật chọn trực tiếp mô phỏng một trong những cách sắp xếp tự nhiên nhất trong thực tế: Giả định rằng, tất cả dữ liệu nhập (input data) có cùng kiểu, tập dữ liệu nhập là hữu hạn. các dữ liệu kết quả (output data) là các số nguyên theo thứ tự không giảm; chọn phần tử nhỏ nhất trong n phần tử khởi tạo, đưa phần tử này về vị trí đúng là đầu dãy hiện hành; sau đó không quan tâm đến nó nữa, xem dãy hiện hành mới chỉ còn n-1 phần tử của dãy ban đầu, bắt đầu từ vị trí thứ hai; lặp lại quá trình trên cho dãy hiện hành... cho đến khi dãy hiện hành chỉ còn một phần tử.

•Ví dụ : Cho dãy số 5 6 1 9 8 6 Nếu dãy cần được sắp theo thứ tự tăng dần,

dòng i j a[0] a[1] a[2] a[3] a[4] a[5] 0 0 2 5 6 1 9 8 6 1 1 2 1 6 5 9 8 6 2 2 2 1 5 6 9 8 6 3 3 5 1 5 6 9 8 6

Page 30: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 29

4 4 4 1 5 6 6 8 9 5 1 5 6 6 8 9

void selectionsort(int a[],int n) { for(int i = 0;i < n -1; i++) { int min = i; for ( int j = i+1; j < n; j++) if (a[j] < a[min]) min = j; swap( a[min], a[i] ); } } Độ phức tạp tính toán của giải thuật là: O(n2) 2.4.Phương pháp nổi bọt (Bubble Sort) Xuất phát từ cuối dãy (hoặc đầu dãy), đổi chỗ bất kỳ hai phần tử kế cận nào

ngược thứ tự để đưa phần tử nhỏ nhất (hoặc lớn nhất) trong các cặp phần tử đó về vị trí đúng là đầu (cuối) dãy hiện hành, kế tiếp không xét đến nó nữa ở bước tiếp theo, do vậy ở lần xử lý thứ i sẽ có vị trí đầu dãy là i. Lặp lại quá trình trên cho đến khi không còn cặp phần tử nào để xét.

•Ví dụ: Cho dãy số 5 6 1 9 8 6 Nếu dãy cần được sắp theo thứ tự tăng dần,

dòng j-1 j a[0] a[1] a[2] a[3] a[4] a[5] 0 4 5 5 6 1 9 8 6 1 3 4 5 6 1 9 6 8 2 1 2 5 6 1 6 9 8 3 0 1 5 1 6 6 9 8 4 4 5 1 5 6 6 9 8 5 1 5 6 6 8 9

Page 31: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 30

void bubblesort(int a[],int n) { for (int i=1;i<n;i++) for (int j=n-1;j>=i;j--) if (a[j]<a[j-1]) swap(a[j],a[j-1]); } Độ phức tạp tính toán của giải thuật là: O(n2) 2.5.Sắp xếp chèn trực tiếp Giải sử có một dãy a0, a2,.., an-1 trong đó i-1 phần tử đầu tiên đã có thứ tự. Ý

tưởng chính: Dãy đầu tiên chỉ có một thứ tự a0 là dãy đã được sắp. Ta từng bước mở rộng

dãy được sắp bằng cách lần lượt thêm phần tử ai (i=1..n-1) vào. Khi chèn ai vào dãy đã được sắp thì ta tiến hành các công đoạn sau: Thứ nhất là đặt x=ai để lưu giá trị cần chèn. Thứ hai tìm vị trí pos thích hợp trong đoạn a0 đến ai-1 để chèn ai vào. Thứ ba là chèn x vào vị trí pos.

•Ví dụ : Cho dãy số 5 6 1 9 8 6 Nếu dãy cần được sắp theo thứ tự tăng dần,

dòng pos+1 i a[0] a[1] a[2] a[3] a[4] a[5] 0 1 1 5 6 1 9 8 6 1 0 2 5 6 1 9 8 6 2 3 3 1 5 6 9 8 6 3 3 4 1 5 6 9 8 6 4 3 5 1 5 6 8 9 6 5 1 5 6 6 8 9

void insertionsort(int a[], int n) { int pos,x; for(int i=1; i<n; i++) {

Page 32: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 31

x=a[i]; pos=i-1; while(pos>=0 && a[pos]>x) a[pos+1] = a[pos--]; a[pos+1]=x;

} } Độ phức tạp tính toán của giải thuật là: O(n2) 2.6.Sắp xếp nhanh (Quick sort) Quick Sort được thực hiện bằng cách phân hoạch dãy đã cho thành hai phần

sau đó sắp các phần này riêng biệt nhau như sau: Đầu tiên chọn phần tử tùy ý x=a[(l+r)/2] làm mốc - là phần tử sẽ rơi vào vị trí

đặt cuối cùng của nó, kế tiếp quét từ đầu trái của mảng cho đến khi gặp một phần tử lớn hơn x và quét từ đầu phải của mảng cho đến khi gặp một phần tử bé hơn x, hai phần tử dừng việc quét dĩ nhiên là không đúng chỗ trong mảng kết quả cuối cùng nên phải hoán vị chúng.

Sau khi thực hiện bước này thì bảo đảm rằng tất cả các phần tử ở bên trái con trỏ trái nhỏ hơn x và các phần tử ở bên phải con trỏ phải lớn hơn x.

Ở đây x là phần tử phân hoạch; i, j lần lượt là con trỏ quét từ phía trái và phía phải. Sau bước này ta đã chia dãy thành hai dãy con; ta lại tiếp tục phân hoạch cho mỗi dãy con này.

•Ví dụ : Cho dãy số 5 6 1 9 8 6 Nếu dãy cần được sắp theo thứ tự tăng dần,

dòng l r x a[0] a[1] a[2] a[3] a[4] a[5] 0 0 5 1 5 6 1 9 8 6 1 0 1 1 1 6 5 9 8 6 2 1 5 9 1 6 5 9 8 6 3 1 4 5 1 6 5 6 8 9 4 2 4 6 1 5 6 6 8 9 5 3 4 6 1 5 6 6 8 9

Page 33: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 32

6 4 5 8 1 5 6 6 8 9 7 1 5 6 6 8 9

void quicksort(int a[],int l,int r) { int x=a[(l+r)/2]; int i=l; int j=r; do { while (a[i]<x) i++; while (a[j]>x) j--; if (i<=j) swap(a[i++],a[j--]); } while (i<j); if (l<j) quicksort(a,l,j); if (i<r) quicksort(a,i,r); } Độ phức tạp tính toán của giải thuật là: O(nlogn) 2.7.Sắp xếp cây (Heap sort) Để tìm phần tử nhỏ nhất ở bước i, phương pháp sắp xếp chọn trực tiếp đã

không tận dụng được các thông tin đã có được do các phép so sánh ở bước i-1. Phương pháp Heap Sort khắc phục được nhược điểm này. Định nghĩa heap: Giả sử xét trường hợp sắp xếp tăng dần, khi đó Heap được định nghĩa là một dãy các phần tử al,..,ar thoã các quan hệ sau với mọi i∈ [l,r]

ai ≥ a2i ai ≥ a2i+1 (ai,a2i), (ai,a2i+1) là các cặp phần tử liên đới

Tính chất của heap: Tính chất 1: Nếu al,.,ar là một Heap thì khi cắt bỏ một số phần tử ở hai đầu của Heap thì dãy con còn lại vẫn là một Heap .

Page 34: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 33

Tính chất 2: Nếu các phần tử a1,..,an là một Heap thì phần tử a1 (đầu heap) luôn là phần tử lớn nhất trong Heap. Tính chất 3: Mọi dãy al,al+1,…,ar với 2l > r là một Heap

•Giải thuật Giải thuật heap sort gồm hai giai đoạn sau: Giai đoạn 1: Hiệu chỉnh dãy số ban đầu thành Heap Giai đoạn 2: Sắp xếp dãy số dựa trên Heap

Bước 1:Đưa phần tử lớn nhất về vị trí đứng ở cuối dãy r = n;

Hoán vị (a1,ar) Bước 2: Loại bỏ phần tử lớn nhất ra khỏi Heap r=r-1;

Hiệu chỉnh phần còn lại của dãy từ al đến ar thành một Heap. Bước 3:Nếu r >1 ( heap còn phần tử) : lặp lại bước 2

Ngược lại: dừng Dựa vào tính chất 3, ta có thể thực hiện giai đoạn 1 bằng cách bắt đầu từ heap mặc nhiên an/2+1, an/2+2,…,an, lần lượt thêm vào các phần tử an/2, an/2-1,…,a1. ta sẽ nhận được Heap theo mong muốn. Như vậy giải đoạn 1 tương đương với n/2 lần thực hiện bước 2 của giai đoạn 2. •Cài đặt Để cài đặt Heap sort cần xây dựng một số thủ tục phụ trợ:

1.Thủ tục hiệu chỉnh một dãy al,ar thành heap Giả sử có al,al+1,…ar, trong đó đoạn al+1,…ar, đã là một heap. ta cần xây dựng

al,al+1,…,ar thành một heap. để làm điều này ta lần lượt xét quan hệ của một phần tử ai nào đó với các phần tử liên đới của nó trong dãy là a2i và a2i+1, nếu nó vi phạm quan hệ của heap thì đổi chổ ai với phần tử liên đới thich hợp của nó – việc đổi này có thể gây phản ứng dây chuyền. 2.Hiệu chỉnh ao,..,an-1 thành heap.

Cho một dãy bất kỳ al,…,ar , theo tính chất 3 ta có dãy an/2+1, an/2+2,…,an đã là một heap. Ghép thêm phần tử an/2 vào bên trái của heap hiện hành và hiệu chỉnh lại dãy an/2,an/2+1,..,ar thành heap.

STT l j a[0] a[1] a[2] a[3] a[4] a[5]

Page 35: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 34

0 2 4 5 6 1 9 8 6 1 1 3 5 6 8 9 1 6 2 0 1 5 9 8 6 1 6 3 1 2 9 5 8 6 1 6 4 2 5 9 8 5 6 1 6 5 0 5 9 8 6 6 1 5 6 0 1 5 8 6 6 1 9 7 1 2 8 5 6 6 1 9 8 0 4 8 6 5 6 1 9 9 0 1 1 6 5 6 8 9 10 1 3 6 1 5 6 8 9 11 0 3 6 6 5 1 8 9 12 0 1 1 6 5 6 8 9 13 1 2 6 1 5 6 8 9 14 0 2 6 5 1 6 8 9 15 0 1 1 5 6 6 8 9 16 0 1 5 1 6 6 8 9 17 1 5 6 6 8 9

void insertheap(int a[],int l,int r) { int j; j=l*2; while (j<=r) { if (j<r) if (a[j] <a[j+1]) j++; if (a[j] <=a[l]) return; swap(a[l],a[j]); output(a,n); l=j; j=l*2; }

Page 36: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 35

} void heapsort(int a[],int n) { int l=n/2; int r=n; while (l>0) insertheap(a,l--,r); while (r>0) { swap(a[1],a[r]); output(a,n); r--; insertheap(a,1,r); } }

Độ phức tạp tính toán của giải thuật là: O(nlog2n) 2.8.Sắp xếp trộn trực tiếp (Merge sort)

Bước 1: Chuẩn bị k=1;// k là chiều dài của dãy con trong bước hiện hành

Bước 2:Tách dãy a0,a1,…an-1 thành hai dãy b,c theo nguyên tắc luân phiên từng nhóm k phần tử. Bước 3:Trộn từng cặp dãy con gồm k phần tử của hai dãy b,c vào dãy a. Bước 4:

k=k*2; Nếu k<n thì trở lại bước 2. Ngược lại: Dừng

•Ví dụ : Cho dãy số

8 5 1 3 6 9 12 4 7 10 Nếu dãy cần được sắp theo thứ tự tăng dần,

k=1;

Page 37: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 36

Bước tách a thành b,c Dãy b: 8 1 6 12 7 Dãy c: 5 3 9 4 10

Trộn : 5 8 1 3 6 9 4 12 7 10 k=2;

Bước tách a thành b,c Dãy b: 5 8 6 9 7 10 Dãy c: 1 3 4 12 Trộn : 1 3 5 8 4 6 9 12 7 10

k=4; Bước tách a thành b,c Dãy b: 1 3 5 8 7 10 Dãy c: 4 6 9 12

Trộn : 1 3 4 5 6 8 9 12 7 10 k=8;

Bước tách a thành b,c Dãy b: 1 3 4 5 6 8 9 12 Dãy c: 7 10

Trộn : 1 3 4 5 6 7 8 9 10 12 void mergesort(int a[],int n) { int up,j,k,q,t,r,s,d,p; for (int i=n;i>0;i--) a[i]=a[i-1]; up=1; p=1; do { d=1; int m=n; if (up)

Page 38: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 37

{ i=1;j=n;k=n+1;q=2*n; } else { i=n+1;j=2*n;k=1;q=n; } do { if (m>=p) s=p; else s=m; m=m-s; if (m>=p) r=p; else r=m; m=m-r; while ((s!=0) && (r!=0)) { if (a[i]<a[j]) { a[k]=a[i];i=i+1;s=s-1; } else { a[k]=a[j];j=j-1;r=r-1; } k=k+d; } while (s!=0) { a[k]=a[i];k=k+d;i=i+1;s=s-1; } while (r!=0) {

Page 39: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 38

a[k]=a[j];k=k+d;j=j-1;r=r-1; } d=-d;t=k;k=q;q=t; } while (m!=0); up=!up; p=p*2; } while (p<n); if (!up) for (i=1;i<=n;i++) a[i]=a[i+n]; for (i=0;i<n;i++) a[i]=a[i+1]; } Độ phức tạp tính toán của giải thuật là: O(nlog2n)

B.Các dạng bài tập Dạng 1: Ứng dụng tìm kiếm Ví dụ 2-1.Cho dãy n số nguyên ai. a.Viết hàm tìm xem x có thuộc dãy ai hay không ? Nếu có trả về 1, nếu không có trả về 0. b.Viết hàm tìm giá trị lớn nhì của dãy. c.Tìm các giá trị xuất hiện nhiều lần nhất Dạng 2: Ứng dụng sắp xếp Ví dụ 2-2.Cho dãy n số nguyên ai. a.Viết hàm tìm giá trị lớn thứ k của dãy. b.Hãy cho biết k giá trị khác nhau lớn nhất của dãy. C.Bài tập Viết chương trình hoàn chỉnh cho các bài toán sau đây BT2-1.a.Cài đặt hoàn chỉnh các giải thuật sau: Tìm kiếm tuyến tính, tìm kiếm nhị phân. b.Hãy chứng minh độ phức tạp của các giải thuật trên. BT2-2.a.Cho dãy n số nguyên sau:

Page 40: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 39

8 5 1 3 6 9 12 4 7 10 Hãy mô phỏng các bước sắp xếp tăng dần dãy số trên bằng các giải thuật đổi chỗ trực tiếp, chọn trực tiếp, chèn trực tiếp và nổi bọt. b.Cài đặt hoàn chỉnh các giải thuật: Sắp xếp đổi chỗ trực tiếp, sắp xếp chọn trực tiếp, sắp xếp chèn trực tiếp, sắp xếp nổi bọt. c.Hãy chứng minh độ phức tạp cho các giải thuật trên. BT2-3.Cho dãy gồm n số nguyên sau:

8 5 1 3 6 9 12 4 7 10 Hãy mô phỏng các bước sắp xếp tăng dần dãy số trên bằng các giải thuật sắp xếp nhanh, sắp xếp trộn trực tiếp và sắp xếp cây. b.Cài đặt hoàn chỉnh các giải thuật: Sắp xếp nhanh (quick sort), sắp xếp trộn trực tiếp (merge sort), sắp xếp cây (heap sort). c.Hãy chứng minh độ phức tạp cho các giải thuật trên. BT2-4.Cho dãy n số nguyên a0,a1,…,an-1

a.Hãy cho biết vị trí của k phần tử có giá trị lớn nhất của dãy. b.Sắp xếp các phần tử tăng dần theo tổng các chữ số của từng phần tử. c.Hãy xóa tất cả các số nguyên tố có trong dãy.

BT2-5.Cho ma trận hai chiều m dòng n cột; các phần tử là các số nguyên dương. a.Tìm số nguyên tố lớn nhất của mảng b.Tìm những dòng của mảng có chứa giá trị nguyên tố

c.Tìm những dòng của mảng chỉ chứa các số nguyên tố. BT2-7.Cho ma trận hai chiều m dòng n cột; các phần tử là các số nguyên dương. a.Tìm dòng có tổng lớn nhất b.Sắp xếp các dòng sao cho dòng có tổng các phần tử lớn hơn sẽ nằm phía trên

c.Sắp xếp sao cho các dòng có nhiều số nguyên tố hơn sẽ nằm phía trên. BT2-8.Cho mảng một chiều gồm n phần tử là các số nguyên không âm. Hãy sắp xếp các số chẵn trong mảng theo thứ tự tăng, sắp xếp các số lẻ theo thứ tự giảm dần, các số 0 giữ nguyên vị trí. BT2-9.Cho mảng một chiều gồm n phần tử là các số nguyên dương. Hãy sắp xếp sao cho các phần tử chẵn ỏ đầu, các phần tử lẻ về cuối. Yêu cầu độ phức tạp là O(n).

Page 41: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 40

BT1-10.Với mỗi hóan vị A(a1,a2,a3,…aN) của N số tự nhiên đầu tiên, ta ký hiệu b=(b1,b2,…,bN) là một mảng mà bi bằng số các phần tử đứng trước số i và lớn hớn i (1<= i <=n). Mảng B được gọi là mảng nghịch thế của mảng A.

Ví dụ : Nếu A là một hóan vị của 9 số tự nhiên đầu tiên 5 9 1 8 2 6 4 7 3 Thì mảng nghịch thế sẽ là: 2 3 6 4 0 2 2 1 0 a.Viết hàm tìm nghịch thế của một hoán vị b.Viết hàm tìm hóan vị của một nghịch thế(nếu có)

BT2-10.Cho hai file chứa các số nguyên đã được sắp tăng dần, hãy trộn hai file này để được một file cũng được sắp tăng dần. BT2-11.Cho file văn bản chứa n dòng; mỗi dòng chứa một câu bao gồm các chữ cái thường từ a đến z và ký tự khoảng trắng (câu tạo thành các từ, đầu và cuối câu không có ký tự khoảng trắng, giữa các từ cách nhau đúng một khoảng trắng). a.Hãy sắp xếp các chuỗi theo số lượng các từ trong chuỗi (chuỗi ít từ hơn sẽ nằm phía trên). b.Hãy sắp xếp các chuỗi theo số lượng các nguyên âm có trong chuỗi (chuỗi ít nguyên âm hơn sẽ nằm phía trên). c.Hãy sắp xếp các chuỗi theo số lượng tần số của các ký tự trong chuỗi (chuỗi có nhiều ký tự khác nhau hơn sẽ nằm phía trên). BT2-12.Hãy cho biết thời gian thi hành của mỗi giải thuật sắp xếp đã học trên cùng một bộ dữ liệu chứa khoảng 30000 số nguyên ngẫu nhiên và trên cùng một máy tính của bạn (dữ liệu vào và ra được lưu trên file văn bản).

Page 42: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 41

Chương 3

DANH SÁCH LIÊN KẾT A.Tóm tắt lý thuyết Trong phần lập trình C, bạn đọc đã được giới thiệu các kiểu dữ liệu cơ sở như: kiểu số, kiểu ký tự hoặc các kiểu dữ liệu có cấu trúc đơn giản như: kiểu mẫu tin, kiểu mảng. Các đối tượng được biểu diễn bởi các kiểu dữ liệu này có kích thước không đổi trong suốt quá trình sống của nó –gọi là các kiểu dữ liệu tĩnh. Tuy nhiên một đối tượng trong quá trình sống của nó có thể thay đổi độ lớn; khi đó nếu dùng các kiểu dữ liệu tĩnh để biểu diễn thì việc giải quyết vấn đề trở nên phức tạp, không được tự nhiên và không hiệu quả. Trong chương 3 này, chúng tôi giới thiệu một cách thức tổ chức dữ liệu linh động hơn nhằm khắc phục được các khuyết điểm trên – đó là cấu trúc dữ liệu động – mà dạng đơn giản và quan trọng nhất là danh sách liên kết. 1.Danh sách liên kết (Link List) 1.1.Ðịnh nghĩa

Cho T là một kiểu được định nghĩa trước, kiểu danh sách Tx gồm các phần tử thuộc kiểu T được định nghĩa là: Tx = <Vx, Ox> ,trong đó:

Vx = {tập hợp có thứ tự các phần tử kiểu T được móc nối với nhau theo trình tự tuyến tính};

Ox = {tạo danh sách; Tìm một phần tử trong danh sách; chèn một phần tử vào danh sách; huỷ một phần tử khỏi danh sách ; liệt kê các phần tử của danh sách, sắp xếp danh sách,...}

1.2.Các hình thức tổ chức danh sách Có nhiều kiểu tổ chức liên kết giữa các phần tử trong danh sách như : -Danh sách liên kết đơn -Danh sách liên kết kép -Danh sách liên kết vòng -Danh sách tổng quát Hình thức liên kết này cho phép các thao tác thêm, huỷ phần tử trên danh

sách được thực hiện dễ dàng hơn, phản ánh được bản chất linh động của danh sách. 2.Danh sách liên kết đơn

Page 43: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 42

2.1.Tổ chức danh sách đơn theo cách cấp phát liên kết Cấu trúc dữ liệu của một phần tử trong danh sách đơn: Mỗi phần tử của danh sách đơn là một cấu trúc chứa hai thông tin :

-Thành phần dữ liệu: lưu trữ các thông tin về bản thân phần tử . -Thành phần mối liên kết: lưu trữ địa chỉ của phần tử kế tiếp trong danh sách,

hoặc lưu trữ giá trị NULL nếu là phần tử cuối danh sách. Ta có định nghĩa tổng quát

struct Node { Data Info;//data là kiểu dữ liệu đã định nghĩa trước Node* Next;//con trỏ chỉ đến cấu trúc Node };

• Một phần tử trong danh sách đơn là một biến động sẽ được yêu cầu cấp phát khi cần. Và danh sách đơn chính là sự liên kết các biến động này với nhau do vậy đạt được sự linh động khi thay đổi số lượng các phần tử .

• Nếu biết được địa chỉ của phần tử đầu tiên trong danh sách đơn thì có thể dựa vào thông tin next của nó để truy xuất đến phần tử thứ hai trong xâu, và lại dựa vào thông tin next của phần tử thứ 2 để truy xuất đến phần tử thứ 3,...Nghĩa là để quản lý một xâu đơn chỉ cần biết địa chỉ phần tử đầu xâu. Thường một con trỏ head sẽ được dùng để lưu trữ địa chỉ phần tử đầu xâu, ta gọi head là đầu xâu. Ta có khai báo:

Node *head; • Tuy về nguyên tắc chỉ cần quản lý xâu thông qua đầu xâu head, nhưng thực tế có

nhiều trường hợp cần làm việc với phần tử cuối xâu, khi đó mỗi lần muốn xác định phần tử cuối xâu lại phải duyệt từ đầu xâu. Ðể tiện lợi, có thể sử dụng thêm một con trỏ tail giữ địa chỉ phần tử cuối xâu. Khai báo tail như sau : Node *tail;

2.2. Các thao tác cơ bản trên danh sách đơn Giả sử có các định nghĩa:

struct node { int info;

Page 44: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 43

node *next; }; struct list { node *head,*tail; } ; list head,tail;

Và đã xây dựng thủ tục tạonút (getnode(x)) để tạo ra một phần tử cho danh sách với thông tin chứa trong x:

node* taonut (int x) {

p=new node; if (p==NULL)

exit(1); p->info=x; p->next=NULL; return p; }

2.2.1.Chèn một phần tử vào danh sách: Có 3 loại thao tác chèn một phần tử x được trỏ bởi con trỏ p vào danh sách: Chèn vào đầu danh sách

• Giải thuật : nếu danh sách rỗng thì

l.head = p; l.tail = p; ngược lại p->next = l.head; l.head = p;

void chennutvaodau(List &l,Node *p)

Page 45: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 44

{ if (l.head==NULL) { l.head=p; l.tail=p; } else { p->next=l.head; l.head=p; } } Như vậy để tạo một danh sách bằng cách chèn đầu ta có thể viết hàm như sau: void danhsachchendau(List &l) { int x,n; cout<<"Nhap vao so n = ";cin>>n; for (int i=1;i<=n;i++) { cin>>x; chennutvaodau(l,taonut(x)); } } Chèn vào cuối danh sách

• Giải thuật : Nếu danh sách rỗng thì l.head = new_elelment; l.tail = head; ngược lại l.tail ->next = new_elelment; l.tail = new_elelment ; Cài đặt

Page 46: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 45

void chennutvaocuoi(List &l,Node *p) { if (l.head==NULL) { l.head=p; l.tail=p; } else { l.tail->next=p; l.tail=p; } } Như vậy để tạo một danh sách bằng cách chèn cuỐi ta có thể viết hàm như sau: void danhsachchencuoi(List &l) { int x,n; cout<<"Danh sach co bao nhieu phan tu : ";cin>>n; for (int i=1;i<=n;i++) { cin>>x; chennutvaocuoi(l,taonut(x)); } } Chèn vào danh sách sau một phần tử q

• Giải thuật : Nếu ( q != NULL) thì

new_ele -> next = q->next; q->next = new_ele ;

Page 47: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 46

void chensaunutq(List &l,Node *q,int x) { Node *p=taonut(x); if (p==NULL) exit(1); if (q!=NULL) { p->next=q->next; q->next=p; if (q==l.tail) l.tail=p; } else chennutvaodau(l,p); }

3.2.2.2. Tìm một phần tử trong danh sách đơn • Giải thuật : Xâu đơn đòi hỏi truy xuất tuần tự, do đó chỉ có thể áp dụng giải thuật tìm tuyến tính để xác định phần tử trong xâu có khoá k. Sử dụng một con trỏ phụ trợ p để lần lượt trỏ đến các phần tử trong xâu. Giải thuật được thể hiện như sau :

Bước 1: p = Head; //Cho p trỏ đến phần tử đầu danh sách

Bước 2: Trong khi (p != NULL) và (p->next != k ) thực hiện:

p:=p->next;// Cho p trỏ tới phần tử kế Bước 3:

Nếu p != NULL thì p trỏ tới phần tử cần tìm Ngược lại: không có phần tử cần tìm.

Cài đặt:

Page 48: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 47

Node* Search(List l,int x) { p=l.pH; while ((p!=NULL)&&(p->Info!=x)) p=p->next; return p; }

3.2.2.3. Duyệt danh sách Duyệt danh sách là thao tác thường được thực hiện khi có nhu cầu xử lý các phần tử của danh sách theo cùng một cách thức hoặc khi cần lấy thông tin tổng hợp từ các phần tử của danh sách như:

- Ðếm các phần tử của danh sách, - Tìm tất cả các phần tử thoã điều kiện, - Huỷ toàn bộ danh sách (và giải phóng bộ nhớ)

Ðể duyệt danh sách (và xử lý từng phần tử) ta thực hiện các thao tác sau: • Giải thuật :

Bước 1: p = Head; //Cho p trỏ đến phần tử đầu danh sách

Bước 2: Trong khi (Danh sách chưa hết) thực hiện B21 : Xử lý phần tử p; B22 : p:=p->pNext; // Cho p trỏ tới phần tử kế

Cài đặt: void ProcessList(List &l) { p=l.pH; while (p!=NULL) { cout<<p->Info<<" "; p=p->pNext; }

Page 49: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 48

cout<<endl; }

3.2.2.4. Huỷ một phần tử khỏi danh sách Có 3 loại thao tác thông dụng huỷ một phần tử ra khỏi xâu. Chúng ta sẽ lần lượt khảo sát chúng. Lưu ý là khi cấp phát bộ nhớ, chúng ta đã dùng hàm new. Vì vậy khi giải phóng bộ nhớ ta phải dùng hàm delete.

Cách 1: Huỷ phần tử đầu xâu: • Giải thuật :

Nếu (Head != NULL) thì B1: p = Head; // p là phần tử cần huỷ B2: Head = Head->pNext; // tách p ra khỏi xâu

free(p); // Huỷ biến động do p trỏ đến B3: Nếu Head=NULL thì Tail = NULL; //Xâu rỗng

Cách 2: Huỷ một phần tử đứng sau phần tử q • Giải thuật :

Nếu (q!= NULL) thì b1: p = q->pNext; // p là phần tử cần huỷ b2: Nếu (p != NULL) thì // q không phải là cuối xâu q->pNext = p->pNext; // tách p ra khỏi xâu

free(p); // Huỷ biến động do p trỏ đến void RemoveNode(List &l,Node *q) { if (q!=NULL) { p=q->pNext; if (p!=NULL) { if(p==l.pT) l.pT=q; q->pNext=p->pNext; free(p); } }

Page 50: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 49

}

Cách 3: Huỷ 1 phần tử có khoá k • Giải thuật :

Bước 1: p=l.head; if p->info==k goi ham xoa phan tu dau cua danh sach l else { q=p ->pNext while (q->info!=k) { p=q; q=q->pNext } p->pnext=q->pnext } LƯU Ý : Ðể huỷ toàn bộ danh sách, ta có một chút thay đổi trong thủ tục duyệt (xử lý)

danh sách trên (ở đây, thao tác xử lý bao gồm hành động giải phóng một phần tử, do vậy phải cập nhật các liên kết liên quan) :

• Giải thuật : Bước 1:

Trong khi (Danh sách chưa hết) thực hiện p = Head; Head:=Head->pNext; // Cho p trỏ tới phần tử kế Hủy p;

Bước 2: Tail = NULL; //Bảo đảm tính nhất quán khi xâu rỗng

Page 51: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 50

3.3.SẮP XẾP DANH SÁCH Một danh sách có thứ tự (danh sách được sắp) là một danh sách mà các phần tử của nó được sắp xếp theo một thứ tự nào đó dựa trên một trường khoá. Ví dụ, danh sách các phần tử số có thứ tự tăng là danh sách mà với mọi cặp phần tử X, Y ta luôn có X ≤ Y nếu X xuất hiện trước Y trong danh sách (danh sách có 1 hoặc không có phần tử nào được xem là một danh sách được sắp). Ðể sắp xếp một danh sách, ta có thể thực hiện một trong 2 phương án sau: • Phương án 1: Hoán vị nội dung các phần tử trong danh sách (thao tác trên

vùng Info). Với phương án này, có thể chọn một trong những giải thuật sắp xếp đã biết để cài đặt lại trên xâu như thực hiện trên mảng, điểm khác biệt duy nhất là cách thức truy xuất đến các phần tử trên xâu thông qua liên kết thay vì chỉ số như trên mảng. Do dựa trên việc hoán vị nội dung của các phần tử, phương pháp này đòi hỏi sử dụng thêm vùng nhớ trung gian nên chỉ thích hợp với các xâu có các phần tử có thành phần Info kích thước nhỏ. Hơn nữa số lần hoán vị có thể lên đến bậc n2 với xâu n phần tử. Khi kích thước của trường Info lớn, việc hoán vị giá trị của hai phân tử sẽ chiếm chi phí đáng kể. Ðiều này sẽ làm cho thao tác xắp xếp chậm lại. Như vậy, phương án này không tận dụng được các ưu điểm của xâu . 3.3.2.Một số Phương pháp sẵp xếp trên danh sách Ví dụ : Cài đặt giải thuật sắp xếp Chọn trực tiếp trên xâu : void ListSelection(List &l) { for (Node *p=l.head;p!=l.tail;p=p->pNext) { Node *min=p; for (Node *q=p->pNext;q!=NULL;q=q->pNext) if (q->Info<min->Info) min=q; int temp=min->Info; min->Info=p->Info;

Page 52: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 51

p->Info=temp; } } Cài đặt giải thuật sắp xếp đổi chỗ trực tiếp trên xâu : void ListInterchengeSort(List &l) { for (Node *p=l.head;p!=NULL;p=p->pNext) for (Node *q=p->pNext;q!=NULL;q=q->pNext) if (p->Info>q->Info) { int temp=p->Info; p->Info=q->Info; q->Info=temp; } }

• Phương án 2: Thay đổi các mối liên kết (thao tác trên vùng Next) Do các nhược điểm của các phương pháp sắp xếp theo phương án 1, khi ữ liệu lưu tại mỗi phần tử trong xâu có kích thước lớn người ta thường dùng một cách tiếp cận khác. Thay vì hoán đổi giá trị, ta sẽ tìm cách thay đổi trình tự móc nối của các phần tử sao cho tạo lập nên được thứ tự mong muốn. Cách tiếp cận này sẽ cho phép ta chỉ thao tác trên các móc nối (trường pNext). Như ta đã biết, kích thước của trường này không phụ thuộc vào bản chất dữ liệu lưu trong xâu vì có bằng đúng một con trỏ (2 byte hoặc 4 byte trong môi trường 16 bit và 4 byte hoặc 8 byte trong môi trường 32 bit, .). Tuy nhiên thao tác trên các mọc nối thường sẽ phức tạp hơn là thao tác trực tiếp trên dữ liệu. Vì vậy, ta cần cân nhắc kỹ lưỡng trước khi chọn cách tiếp cận. Nếu dũ liệu không quá lớn thì ta nên chọn phương án 1 hoặc một giải thuật hiệu quả nào đó. Một trong những cách thay đổi móc nối đơn giản nhất là tạo một danh sách mới là danh sách có thứ tự từ danh sách cũ (đồng thời hủy danh sách

Page 53: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 52

cũ). Giả sử danh sách mới sẽ được quản lý bằng con trỏ đầu xâu Result, ta có phương án 2 của giải thuật chọn trực tiếp như sau :

Bước1: Khởi tạo danh sách mới Result là rỗng; Bước2: Tìm trong danh sách cũ l phần tử nhỏ nhất; Bước3: Tách min khỏi danh sách l; Bước4: Chèn min vào cuối danh sách Result; Bước5: Lặp lại bước 2 khi chưa hết danh sách Head; void ListSelectionSort2(List &l) { List lRes; Node *min; // ch? d?n ph?n t? có giá tr? nh? nh?t trong xâu Node *p,*q,*minrightev; lRes.head= lRes.pT = NULL; // kh?i t?o lRes while(l.head!= NULL) { p = l.pH; q = p->pNext;min = p; minrightev=NULL; while(q != NULL) { if(q->Info< min->Info ) { min = q; minrightev = p; } p = q; q = q->pNext; } if(minrightev != NULL) minrightev->pNext = min->pNext; else l.pH = min->pNext;

Page 54: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 53

min->pNext = NULL; InsertTail(lRes,min); } l = lRes; }

3.4.Danh sách hạn chế 3.4.1.Stack Stack là một vật chứa (container) các đối tượng làm việc theo cơ chế LIFO (Last In First Out) nghĩa là việc thêm một đối tượng vào stack hoặc lấy một đối tượng ra khỏi stack được thực hiện theo cơ chế "Vào sau ra trước".

Các đối tượng có thể được thêm vào stack bất kỳ lúc nào nhưng chỉ có đối tượng thêm vào sau cùng mới được phép lấy ra khỏi stack. Thao tác thêm 1 đối tượng vào stack thường được gọi là "Push". Thao tác lấy 1 đối tượng ra khỏi stack gọi là "Pop".

Ta có thể định nghĩa CTDL stack như sau: stack là một CTDL trừu tượng (ADT) tuyến tính hỗ trợ 2 thao tác chính:

Push(o): Thêm đối tượng o vào đầu stack Pop(): Lấy đối tượng ở đầu stack ra khỏi stack và trả về giá trị của nó. Nếu

stack rỗng thì lỗi sẽ xảy ra. Ngoài ra, stack cũng hỗ trợ một số thao tác khác:

isEmpty(): Kiểm tra xem stack có rỗng không. Top(): Trả về giá trị của phần tử nằm ở đầu stack mà không hủy nó khỏi

stack. Nếu stack rỗng thì lỗi sẽ xảy ra. Các thao tác thêm, trích và huỷ một phần tử chỉ được thực hiện ở cùng một phía của Stack do đó hoạt động của Stack được thực hiện theo nguyên tắc LIFO (Last In First Out - vào sau ra trước).

Ðể biểu diễn Stack, ta có thể dùng mảng 1 chiều hoặc dùng danh sách liên kết.

Page 55: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 54

3.4.2 Hàng đợi (Queue) Hàng đợi là một vật chứa (container) các đối tượng làm việc theo cơ chế

FIFO (First In First Out) nghĩa là việc thêm một đối tượng vào hàng đợi hoặc lấy một đối tượng ra khỏi hàng đợi được thực hiện theo cơ chế "Vào trước ra trước". Các đối tượng có thể được thêm vào hàng đợi bất kỳ lúc nào nhưng chỉ có đối tượng thêm vào đầu tiên mới được phép lấy ra khỏi hàng đợi. Thao tác thêm một đối tượng vào hàng đợi và lấy một đối tượng ra khỏi hàng đợi lần lượt được gọi là "enqueue" và "dequeue". Việc thêm một đối tượng vào hàng đợi luôn diễn ra ở cuối hàng đợi và một phần tử luôn được lấy ra từ đầu hàng đợi.

Trong tin học, CTDL hàng đợi có nhiều ứng dụng: khử đệ qui, tổ chức lưu vết các quá trình tìm kiếm theo chiều rộng và quay lui, vét cạn, tổ chức quản lý và phân phối tiến trình trong các hệ điều hành, tổ chức bộ đệm bàn phím, .

Ta có thể định nghĩa CTDL hàng đợi như sau: hàng đợi là một CTDL trừu tượng (ADT) tuyến tính. Tương tự như stack, hàng đợi hỗ trợ các thao tác:

EnQueue(o): Thêm đối tượng o vào cuối hàng đợi DeQueue(): Lấy đối tượng ở đầu queue ra khỏi hàng đợi và trả về giá trị

của nó. Nếu hàng đợi rỗng thì lỗi sẽ xảy ra. IsEmpty(): Kiểm tra xem hàng đợi có rỗng không. Front(): Trả về giá trị của phần tử nằm ở đầu hàng đợi mà không hủy

nó. Nếu hàng đợi rỗng thì lỗi sẽ xảy ra. Các thao tác thêm, trích và huỷ một phần tử phải được thực hiện ở 2 phía khác nhau của hàng đợi do đó hoạt động của hàng đợi được thực hiện theo nguyên tắc FIFO (First In First Out - vào trước ra trước). Cũng như stack, ta có thể dùng cấu trúc mảng 1 chiều hoặc cấu trúc danh sách liên kết để biểu diễn cấu trúc hàng đợi. 3.4.3. Ưu nhược điểm của danh sách liên kết Do các phần tử (nút) được lưu trữ không liên tiếp nhau trong bộ nhớ, do vậy danh sách liên kết có các ưu nhược điểm sau đây: -Mật độ sử dụng bộ nhớ của danh sách liên kết không tối ưu tuyệt đối (<100%).

Page 56: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 55

-Việc truy xuất và tìm kiếm các phần tử của danh sách liên kết mất nhiều thời gian bởi luôn luôn phải duyệt tuần tự qua các phần tử trong danh sách; -Tận dụng được những không gian bộ nhớ nhỏ để lưu trữ từng nút, tuy nhiên bộ nhớ lưu trữ thông tin mỗi nút lại tốn nhiều hơn do còn phải lưu thêm thông tin về vùng liên kết. Như vậy nếu vùng dữ liệu của mỗi nút là lớn hơn thì tỷ lệ mức tiêu tốn bộ nhớ này là không đáng kể, ngược lại thì nó lại gây lãng phí bộ nhớ. -Việc thêm, bớt các phần tử trong danh sách, tách nhập các danh sách khá dễ dàng do chúng ta chỉ cần thay đổi mối liên kết giữa các phần tử với nhau. Ứng dụng của danh sách hạn chế Danh sách hạn chế được sử dụng trong nhiều trường hợp, ví dụ: -Hàng đợi thường được sử dụng để lưu trữ các luồng dữ liệu cần xử lý tuần tự; -Ngăn xếp thường được xử lý trong các luồng dữ liệu truy hồi, đặc biệt là trong việc khử đệ quy cho các giải thuật. B.Các dạng bài tập Dạng 1: Tạo danh sách, duyệt danh sách Ví dụ: Cho một danh sách liên kết đơn l, mỗi nút là một số nguyên a.Tạo danh sách l chứa n số nguyên. b.In danh sách #include <conio.h> #include <stdlib.h> #include <iostream.h> #include <alloc.h> struct Node { int info; Node *next; }; struct List { Node *head,*tail;

Page 57: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 56

}; Node* taonut(int x); void chennutvaocuoi(List &l,Node *p); void danhsachchencuoi(List &l); void duyetdanhsach(List l); void chennutvaodau(List &l,Node *p); void danhsachchendau(List &l); void chensaunutq(List &l,Node *q,int x); void main() { clrscr();

List l; danhsachchencuoi(l); duyetdanhsach(l); getch(); } Node* taonut(int x) { Node *p=new Node; if (p==NULL) { cout<<"Khong du bo nho"; exit(1); } p->info=x; p->next=NULL; return p; } void chennutvaocuoi(List &l,Node *p)

Page 58: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 57

{ if (l.head==NULL) { l.head=p; l.tail=p; } else { l.tail->next=p; l.tail=p; } } void danhsachchencuoi(List &l) { int x,n; cout<<"Danh sach co bao nhieu phan tu : ";cin>>n; for (int i=1;i<=n;i++) { cin>>x; chennutvaocuoi(l,taonut(x)); } } void duyetdanhsach(List l) { cout<<endl; Node *p = l.head; while (p!=NULL) { cout<<p->info<<" "; p=p->next;

Page 59: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 58

} } void chennutvaodau(List &l,Node *p) { if (l.head==NULL) { l.head=p; l.tail=p; } else { p->next=l.head; l.head=p; } } void danhsachchendau(List &l) { int x,n; cout<<"Nhap vao so n = ";cin>>n; for (int i=1;i<=n;i++) { cin>>x; chennutvaodau(l,taonut(x)); } } void chensaunutq(List &l,Node *q,int x) { Node *p=taonut(x); if (p==NULL) exit(1);

Page 60: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 59

if (q!=NULL) { p->next=q->next; q->next=p; if (q==l.tail) l.tail=p; } else chennutvaodau(l,p); } Dạng 2: Các hàm tìm kiếm và sắp xếp Tìm xem x có thuộc về danh sách l hay không ? Nếu có trả về con trỏ trỏ đến nút tìm được; nếu không trả về giá trị NULL Node* timkiem(List l,int x) { Node *p=l.head; while ((p!=NULL) && (p->info!=x)) p=p->next; return p; } Tìm xem x có thuộc về danh sách l hay không ? Nếu có trả về giá trị 1; nếu không trả về giá trị 0. int timkiem01(List l,int x) { Node *p=l.head; while ((p!=NULL) && (p->info!=x)) p=p->next; return (p!=NULL); }

Page 61: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 60

Sắp xếp các phần tử theo chiều tăng dần bằng phương pháp chọn trực tiếp void sapxepchontructiep(List &l) { for (Node *p=l.head;p!=l.tail;p=p->next) { Node *min=p; for (Node *q=p->next;q!=NULL;q=q->next) if (q->info<min->info) min=q; hoanvi(min->info,p->info); } } Sắp xếp các phần tử theo chiều tăng dần bằng phương pháp đổi chỗ trực tiếp void sapxepdoichotructiep(List &l) { for (Node *p=l.head;p!=l.tail;p=p->next) for (Node *q=p->next;q!=NULL;q=q->next) if (p->info>q->info) hoanvi(p->info,q->info); } void hoanvi(int &x, int &y) { int temp=x;x=y;y=temp; } Dạng 5: Các hàm liên quan đến xóa nút //Xóa nút đầu của danh sách void xoanutdau(List &l) { Node *p=l.head; if (p!=NULL)

Page 62: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 61

{ l.head=l.head->next; delete(p); } else l.tail=NULL; } //Xóa nút kế sau nút được trỏ bởi con trỏ q void xoanutsauq(List &l,Node *q) { if (q!=NULL) { Node *p=q->next; if (p!=NULL) { if (p==l.tail) l.tail=q; q->next=p->next; delete(p); } } } //Xóa nút có giá trị k void xoanutkhoak(List &l, int k) { Node* p=l.head; if (p->info==k) xoanutdau(l); else { Node *q=p->next;

Page 63: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 62

while (q->info!=k && q!=NULL) { p=q; q=p->next; } p->next=q->next; } } void noihaidanhdanh(List &l1,List l2) { l1.tail->next=l2.head; l1.tail=l2.tail; } void chenphantu(List &l,int x) { cin>>x; if (l.head->info>=x) chennutvaodau(l,taonut(x)); else { Node*p=l.head; while (p->next->info<x && p->next!=NULL) p=p->next; if (p->next==NULL) chennutvaocuoi(l,taonut(x)); else { Node*q=taonut(x); q->next=p->next; p->next=q;

Page 64: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 63

} } } C.Bài tập BT3-1.Cho danh sách liên kết đơn l, mỗi nút là một số nguyên dương.

a.Tính tổng giá trị của các nút. b.Đếm xem trong danh sách có bao nhiêu nút có giá trị chẵn? bao nhiêu nút

có giá trị lẻ? c.Tìm số nguyên tố lớn nhất. d.Hãy tạo danh sách l1 chỉ chứa các số nguyên tố từ danh sách l. e.In ra các run tự nhiên từ danh sách liên kết đã cho: Ví dụ danh sách liên kết

ban đầu biểu diễn các số: 1 5 6 4 8 3 7 thì kết quả là các dãy số: 1 5 6 4 8 3 7 f.Xóa một nút được trỏ bởi con trỏ q. g.Chèn vào sau mỗi nút có giá nguyên tố một số chính phương nhỏ nhất lớn hơn nó (ví dụ sau số nguyên tố 29 là số chính phương 36).

BT3-2.a.Cho hai danh sách liên kết đơn l1,l2 chứa các số nguyên. Hãy nối danh sách l2 vào sau danh sách l1. b.Cho một danh sách liên kết đơn l chứa các số nguyên đã có thứ tự tăng. Hãy chèn một số nguyên x vào danh sách l để l vẫn có thứ tự tăng. c.Cho hai danh sách liên kết đơn l1,l2 chứa các số nguyên. Hãy trộn các phần tử của l1 và l2 vào l3 để l3 cũng được sắp xếp tăng (Hãy giải lại bài toán bằng cách chèn các phần tử của l2 vào l1 để l1 sau đó cũng được sắp tăng (l1 chứa các phần tử của l1 và l2 trước đó). d.Cho một danh sách liên kết đơn l chứa các số nguyên. Hãy giữ lại một giá trị trong các giá trị trùng nhau của l. e.Cho một danh sách liên kết đơn l chứa các số nguyên. Hãy tìm tần số xuất hiện của các giá trị BT3-3.Bằng các thao tác cơ bản trên hàng đợi hãy trình bày các giải thuật:

Page 65: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 64

Đảo ngược thứ tự một hàng đợi. Tìm một phần tử có khóa của trường key là x trong hàng đợi. Cài đặt các giải thuật trên bằng hàng đợi các số nguyên với cách tổ chúc hàng đợi bằng danh sách liên kết. BT3-4.a.Cho một ngăn xếp với các thao tác cơ bản. Hãy viết các hàm thực hiện các công việc sau đây:

-Lấy ra phần tử cuối cùng của ngăn xếp -Lấy ra phần tử thứ n của ngăn xếp, với n là số nguyên dương. Cài đặt cụ thể các giải thuật trên bằng ngăn xếp các số nguyên với cách tổ

chức ngăn xếp bằng mảng. b.Trình bày giải thuật đảo ngược một danh sách bằng hai cách: Đổi giá trị của các phần tử Đổi liên kết của các phần tử. Cài đặt các giải thuật trên cho danh sách các số nguyên. Nếu dùng đệ quy thì hãy dùng ngăn xếp để khử đệ quy. BT3-5.Tính giá trị của biểu thức số học Khi thực hiện chương trình, các ngôn ngữ lập trình thường phải tính giá trị của biểu thức. Thông thường, các ngôn ngữ lập trình tính giá trị biểu thức bằng hai bước sau: Thứ nhất: Chuyển biểu thức từ dạng trung tố (infix)sang dạng hậu tố (postfix). Thứ hai: Tính giá trị biểu thức hậu tố. Biểu thức trung tố là cách mà chúng ta thường sử dụng, trong biểu thức dạng trung tố thì các phép toán hai ngôi được viết xen giữa hai toán hạng. Việc tính trực tiếp các biểu thức trung tố sẽ gặp khó khăn vì phải dùng các cặp dấu ngoặc đơn để quy định thứ tự thực hiện các biểu thức con. Để tính các biểu thức, người Ba Lan đã đưa ra một ký pháp quy định cách viết các biểu thức (gọi là ký pháp Ba Lan) trong đó các phép toán được đặt sau toán hạng. Việc dùng ký pháp Ba Lan không cần dấu ngoặc nhưng vẫn thể hiện được thứ tự ưu tiên khi tính giá trị biểu thức nên đã dễ dàng xây dựng giải thuật để tính. Ví dụ: Biểu thức dạng trung tố 3+ (5-2)*3/7- được viết dưới dạng biểu thức hậu tố là 3 5 2 – 3 * 7/4 -+

Page 66: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 65

Cài đăt chương trình tính biểu thức số học bằng cách chuyển biểu thức trung tố thành biểu thức hậu tố dùng ký pháp Ba Lan.Giới hạn chỉ xét các biểu thức sô học gồm các phép toán hai ngôi: +- */ trên các số có một chữ số. BT3-6.a.Hãy cài đặt các giải thuật sắp xếp đã học trên xâu đơn bằng cách thay đổi các mối liên kết (không hoán đổi vùng info của các nút) b.Hãy cài đặt các giải thuật sắp xếp đã học trên xâu kép BT3-7.Cho danh sách liên kết đơn l, mỗi nút là một phân số (giả sử tử số và mẫu số của các phân số là các số nguyên dương). Cấu trúc của danh sách liên kết l được cho như sau:

struct Phanso {int tu; int mau;}; struct Node {Phanso info; struct Node *next;}; struct List {Node *head,*tail;};

Hãy viết các hàm thực hiện các yêu cầu sau: a.Tìm phân số có giá trị lớn nhất của danh sách l. b.Hãy đếm xem trong sách l có bao nhiêu phân số nhỏ hơn 1 và lớn hơn 0 ? c.Sắp xếp các phân số theo chiều tăng dần. d.Đếm số lượng phân số tối giản của danh sách l (phân số tối giản là phân số mà ước số chung lớn nhất của từ số và mẫu số của nó bằng 1). e.Hãy tạo danh sách l1 chỉ chứa các phân số có giá trị nhỏ hơn 1 từ danh sách l. BT3-8.Cho một danh sách liên kết đơn. Mỗi phần tử info là một ký tự ('A'..'Z'). a.Tạo một danh sách liên kết đơn mới chứa các ký tự không có trong danh sách đã cho. b.Viết chương trình con loại khỏi danh sách đã cho các phần tử vi phạm điều kiện tăng dần của danh sách. Biết rằng phần tử đầu tiên được giữ lại trong danh sách. Ví dụ: Danh sách liên kết biểu diễn : D F H G K M A B Q Danh sách liên kết sau khi loại: D F H K M Q. c.Với danh sách đã cho có thứ tự tăng dần (không có phần tử trùng nhau). Viết chương trình bổ sung vào danh sách này sao cho cho danh sách sẽ chứa đầy đủ các ký tự từ 'A' đến 'Z'. BT3-9.Sử dụng danh sách liên kết đơn viết chương trình thực hiện các yêu cầu sau:

Page 67: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 66

a.Khai báo cấu trúc dữ liệu của một danh sách liên kết đơn các tỉnh. Biết rằng thông tin của mỗi tỉnh bao gồm: tên tỉnh, diện tích, dân số. b.Cài đặt các thao tác cơ bản cho danh sách liên kết đơn các tỉnh (thêm, sửa, xóa, duyệt). c.Tính tổng diện tích của tất cả các tỉnh trong danh sách liên kết. d.Tìm các tên tình có diện tích lớn nhất. e.Tìm một tỉnh có dân số lớn nhất. f.Sắp xếp danh sách tăng dần theo tên tỉnh.

BT3-10.Cho một danh sách liên kết đơn lưu thông tin của các thí sinh khi thi tuyển vào lớp 10 với ba môn thi là môn1, môn 2 và môn 3, thông tin gồm :

- Số báo danh (chuỗi, tối đa 8 ký tự) - Họ tên (chuỗi, tối đa 20 ký tự) - Điểm môn 1 (số nguyên) - Điểm môn 2 (số nguyên) - Điểm môn 3 (số nguyên)

Hãy thực hiện yêu cầu sau: a.Hãy in lên màn hình những thí sinh có điểm tổng ≥ 18 và không có môn nào dưới điểm 5; danh sách này phải được sắp theo thứ tự điểm tổng giảm dần. b.Xóa hồ sơ thí sinh có số báo danh là x. c.Cập nhật điểm thi của một thí sinh có số báo danh là y (y là chuỗi ký tự) theo các giá trị mới nhập từ bàn phím. d.Chèn vào cuối danh sách thông tin về một thí sinh có số báo danh z với đầy đủ các thông tin trên được nhập từ bàn phím. BT3-11.Dùng danh sách liên kết đơn để biểu diễn một đa thức. Nêu giải thuật và cài đặt các hàm sau:

-Nhập đa thức -Xuất một đa thức -Tính giá trị của đa thức tại điểm xo -Cộng đa thức -Nhân đa thức

Page 68: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 67

BT3-12.Dùng danh sách liên kết đơn trong đó các phần tử là các số nguyên được sắp theo thứ tự để cài đặt cho tập hợp các số nguyên. Hãy tổ chức dữ liệu và cài đặt các hàm sau:

-Thêm một phần tử vào tập hợp -Loại một phần tử khỏi tập hợp -Kiểm tra một phần tử có trong tập hợp hay không -Giao hai tập hợp -Hợp hai tập hợp -Hiệu hai tập hợp

Từ các hàm trên, hãy viết chương trình thực hiện các công việc: -Nhập vào hai tập hợp -Tìm giao, hợp. hiệu của hai tập hợp và in kết quả

Page 69: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 68

Chương 4

CẤU TRÚC CÂY A.Tóm tắt lý thuyết 4.1.Cấu trúc cây

4.1.1.Định nghĩa

Cây là một tập hợp T các phần tử (gọi là nút của cây) trong đó có một nút đặc biệt được gọi là gốc, các nút còn lại được chia thành những tập rời nhau T1, T2 , ..., Tn theo quan hệ phân cấp trong đó Ti cũng là một cây. Mỗi nút ở cấp i sẽ quản lý một số nút ở cấp i+1, đây gọi là quan hệ cha-con.

Chẳng hạn sau đây là hình ảnh của một cây Hình 4.1. Nút A gọi là nút gốc của cây. Các nút B,C,D là gốc của các cây con của A. Nút A là cha của các nút B,C,D và B,C,D là các nút con của A. 4.1.2.Một số khái niệm cơ bản Bậc của một nút:

Là số cây con của nút đó. Ví dụ bậc của A là 3, bậc của H là 2, bậc của D là 3.

Bậc của một cây: Là bậc lớn nhất của các nút trong cây (số cây con tối đa của một nút thuộc cây). Cây có bậc n thì gọi là cây n-phân. Ví dụ cây trên có bậc là 3.

A

B C D

E F G H I

J K

Page 70: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 69

Nút gốc: Là nút không có nút cha. Ví dụ A là nút gốc của cây.

Nút lá: Là nút có bậc bằng 0. Ví dụ E,C,K,I,… là các nút lá

Nút nhánh: Là nút có bậc khác 0 và không phải là gốc . Ví dụ B,D,H…là các nút nhánh

Mức của một nút: mức (gốc (T) ) = 0. gọi T1, T2, T3, ... , Tn là các cây con của T0 mức (T1) = mức (T2) = ... = mức (Tn) = mức (T0) + 1.

chẳng hạn nút A có mức là 0, D có mức là 1, G có mức là 2, J có mức là 3. Độ dài đường đi từ gốc đến nút x:

Bằng số nút trên đường đi đó trừ đi 1. chẳng hạn đường đi từ A đến G là 2, đường đi từ A đến K là 3.

Chiều cao của một cây Là số mức lớn nhất của nút có trên cây đó cộng thêm 1 chẳng hạn cây trên có chiều cao là 4.

4.2.Cây nhị phân 4.2.1.Định nghĩa

Cây nhị phân là cây mà mỗi nút có tối đa 2 cây con Trong thực tế thường gặp các cấu trúc có dạng cây nhị phân. Một cây tổng quát có thể biểu diễn thông qua cây nhị phân. 4.2.2 Một số tính chất của cây nhị phân:

Số nút nằm ở mức i ≤ 2I. Với h là chiều cao của cây thì

Số nút lá ≤ 2h-1 thì log2(số nút trong cây). ≤ h Số nút trong cây ≤ 2h –1.

Page 71: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 70

A

B D

E G H I

O P Q N K L M J

chẳng hạn cho một cây nhị phân có chiều cao là 4 như sau: Hình 4.2. Thì số nút

nằm ở mức 0 là 1, số nút nằm ở mức 1 là 2, số nút nằm ở mức 2 là 4, số nút nằm ở mức 3 là 8 và số nút lá của cây là 8.

4.2.3 Biểu diễn cây nhị phân T Cây nhị phân là một cấu trúc bao gồm các phần tử (nút) được kết nối với nhau theo quan hệ “cha-con” với mỗi cha có tối đa 2 con. Để biểu diễn cây nhị phân ta chọn phương pháp cấp phát liên kết. Ứng với một nút, ta dùng một biến động lưu trữ các thông tin:

-Thông tin lưu trữ tại nút. -Địa chỉ nút gốc của cây con trái trong bộ nhớ. -Địa chỉ nút gốc của cây con phải trong bộ nhớ.

Khai báo tương ứng trong ngôn ngữ C/C++ có thể như sau:

struct node { Data info; struct node *left,*right; }; typedef node *tree; tree root;

Page 72: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 71

A

B C

F G D E

Do tính chất mềm dẻo của cách biểu diễn bằng cấp phát liên kết, phương pháp này được dùng chủ yếu trong biểu diễn cây nhị phân. Từ đây trở đi, khi nói về cây nhị phân, chúng ta sẽ dùng phương pháp biểu diễn này.

4.2.4 Duyệt cây nhị phân Nếu như khi khảo sát cấu trúc dữ liệu dạng danh sách liên kết ta không quan

tâm nhiều đến bài toán duyệt qua tất cả các phần tử của chúng thì bài toán duyệt cây hết sức quan trọng. Nó là cốt lõi của nhiều thao tác quan trong khác trên cây. Do cây nhị phân là một cấu trúc dữ liệu phi tuyến nên bài toán duyệt cây là bài toán không tầm thường.

Có nhiều kiểu duyệt cây khác nhau, và chúng cũng có những ứng dụng khác nhau. Đối với cây nhị phân, do cấu trúc đệ qui của nó, việc duyệt cây tiếp cận theo kiểu đệ qui là hợp lý và đơn giản nhất. Sau đây chúng ta sẽ xem xét một số kiểu duyệt thông dụng.

Có 3 kiểu duyệt chính có thể áp dụng trên cây nhị phân: duyệt theo thứ tự trước (NLR), thứ tự giữa (LNR) và thứ tự sau (LRN). Tên của 3 kiểu duyệt này được đặt dựa trên trình tự của việc thăm nút gốc so với việc thăm 2 cây con. Tương tự như vậy ta có 3 cách duyệt khác là: NRL, RNL, RLN.

Duyệt theo thứ tự trước (node-left-right) Kiểu duyệt này trước tiên thăm nút gốc sau đó thăm các nút của cây con trái

rồi đến cây con phải. Thủ tục duyệt có thể trình bày đơn giản như sau: (trong mục này việc xử lý các nút chỉ là in giá trị của phần tử đó)

Duyệt theo thứ tự giữa (Left- Node-Right) Kiểu duyệt này trước tiên thăm các nút của cây con trái sau đó thăm nút gốc

rồi đến cây con phải. Thủ tục duyệt có thể trình bày đơn giản như sau: Duyệt theo thứ tự sau (left-right-node)

Kiểu duyệt này trước tiên thăm các nút của cây con trái sau đó thăm đến cây con phải rồi cuối cùng mới thăm nút gốc. roothủ tục duyệt có thể trình bày đơn giản như sau:

Chẳng hạn với hình 4.3.sau đây

Page 73: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 72

Hình 4.3 thì dãy các tên ứng với nút được thăm trong các phép duyệt là:

Theo thứ tự trước: A,B,D,E,C,F,G Theo thứ tự giữa: D,B,E,A,F,C,G Theo thứ tự sau: D,E,B,F,G,C,A

Bạn đọc hãy liệt kê các nút được thăm ứng với các cách duyệt: NRL, RNL, RLN. 4.3.Cây nhị phân tìm kiếm 4.3.1. cây nhị phân tìm kiếm Định nghĩa:

Cây nhị phân tìm kiếm (CNPTK) là cây nhị phân trong đó tại mỗi nút, khóa của nút đang xét lớn hơn khóa của tất cả các nút thuộc cây con trái và nhỏ hơn khóa của tất cả các nút thuộc cây con phải.

Nhờ ràng buộc về khóa trên CNPTK, việc tìm kiếm trở nên có định hướng. Hơn nữa, do cấu trúc cây việc tìm kiếm trở nên nhanh đáng kể. Nếu số nút trên cây là N thì chi phí tìm kiếm trung bình chỉ khoảng log2N.

Trong thực tế, khi xét đến cây nhị phân chủ yếu người ta xét CNPTK. Sau đây là một ví dụ về cây nhị phân tìm kiếm:

Hình 4.4

4.3.2.3.Duyệt cây Thao tác duyệt cây trên cây nhị phân tìm kiếm hoàn toàn giống như trên cây nhị phân. Chỉ có một lưu ý nhỏ là khi duyệt theo thứ tự giữa, trình tự các nút duyệt qua sẽ cho ta một dãy các nút theo thứ tự tăng dần của khóa.

chẳng hạn với hình 4.4 thì kết quả của các phép duyệt như sau:

4

2 6

5 7 1 3

Page 74: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 73

Theo thứ tự trước: 4,2,1,3,6,5,7 Theo thứ tự giữa: 1,2,3,4,5,6,7 Theo thứ tự sau: 1,3,2,5,7,6,4

B.CÁC DẠNG BÀI TẬP Dạng 1: Hàm tạo cây nhị phân tìm kiếm, các hàm duyệt cây #include <iostream.h> #include <conio.h> #include <alloc.h> struct node { int info; struct node *left,*right; }; typedef node *tree; int chennutvaoBST(tree &root,int k); void taoBST(tree &root); void LNR(tree root); void NLR(tree root); void LRN(tree root); void main() { clrscr(); tree root; taoBST(root); LNR(root); getch(); } //---------------Chèn nút có giá trị k vào cây có gốc là root------------------

Page 75: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 74

Việc thêm một phần tử X vào cây phải bảo đảm điều kiện ràng buộc của CNPTK. Ta có thể thêm vào nhiều chỗ khác nhau trên cây, nhưng nếu thêm vào một nút lá sẽ là tiện lợi nhất do ta có thể thực hiện quá trình tương tự thao tác tìm kiếm. Khi chấm dứt quá trình tìm kiếm cũng chính là lúc tìm được chỗ cần thêm. Hàm chennutvaoBST trả về giá trị –1, 0, 1 khi không đủ bộ nhớ, gặp nút cũ hay thành công: int chennutvaoBST(tree &root,int k) { if (root!=NULL) { if (root->info==k) return 0; if (root->info>k) return chennutvaoBST(root->left,k); else return chennutvaoBST(root->right,k); } else { root=new node; if (root==NULL) return -1; root->info=k; root->left=root->right=NULL; return 1; } } //---------------Tạo cây có gốc là root--------------- Ta có thể tạo một cây nhị phân tìm kiếm bằng cách lặp lại quá trình chèn thêm một phần tử vào một cây rỗng. void taoBST(tree &root) { int k,n; cout<<"\nIn put n=";cin>>n; root=NULL;

Page 76: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 75

for (int i=1;i<=n;i++) { cin>>k; chennutvaoBST(root,k); } root1=root;// dùng cho những hàm phía sau } //---------------Các hàm duyệt cây--------------- void LNR(tree root) { if (root!=NULL) { LNR(root->left); cout<<root->info<<" "; LNR(root->right); } } void NLR(tree root) { if (root!=NULL) { cout<<root->info<<" "; NLR(root->left); NLR(root->right); } } void LRN(tree root) {

Page 77: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 76

if (root!=NULL) { LRN(root->left); LRN(root->right); cout<<root->info<<" "; } } Dạng 2: Các hàm tìm kiếm nút có giá trị x trên cây nhị phân tìm kiếm Kiểm tra xem k có trong cây root hay không ? Nếu có thì trả về con trỏ trỏ đến nút tìm được; trong trường hợp không có thì trả về giá trị NULL. node* timkiem(tree root,int k) { if (root==NULL) return NULL; else if (root->info==k) return root; if (k>root->info) return timkiem(root->right,k); else return timkiem(root->left,k); } Lưu ý: Hãy viết lại hàm trên bằng cách không sử dụng phương pháp đệ quy. Kiểm tra xem k có trong cây root hay không ? Nếu có thì trả về giá trị 1; nếu không có thì trả về giá trị 0. int timkiem01(tree root,int k) { if (root==NULL) return 0; else if (root->info==k) return 1; if (k>root->info) return timkiem01(root->right,k); else return timkiem01(root->left,k); }

Page 78: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 77

Dễ dàng thấy rằng số lần so sánh tối đa phải thực hiện để tìm phần tử k là h, với h là chiều cao của cây. Như vậy thao tác tìm kiếm trên CNPTK có n nút tổng chi phí trung bình khoảng O(log2n) . Dạng 3: Các hàm ứng dụng phép duyệt cây đơn giản //----------Tính tổng giá trị của các nút trong cây ---------- int tong(tree root) { if (root==NULL) return 0; return root->info+tong(root->left)+tong(root->right); } //-----------------Tìm giá trị lớn nhất của cây ----------- int giatrilonnhat(tree root) { while (root->right!=NULL) root=root->right; return root->info; } //------------Đếm số nút của cây---------------- int demnut(tree root) { if (root==NULL) return 0; return demnut(root->left)+demnut(root->right)+1; } //-------------Đếm số nút lá của cây------------- int demnutla(tree root) { if (root==NULL) return 0; if ((root->left==NULL) && (root->right==NULL)) return 1;

Page 79: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 78

else return demnutla(root->left)+demnutla(root->right); } //--------------- Đếm số nút có đúng một cây con --------------- int demnut1caycon(tree root) { if (root==NULL) return 0; else { if ((root->left!=NULL && root->right==NULL) || (root->left==NULL && root->right!=NULL)) return 1+demnut1caycon(root->left)+demnut1caycon(root->right); return demnut1caycon(root->left)+demnut1caycon(root->right); } } //--------------- Đếm số nút có hai cây con--------------- int demnut2caycon(tree root) { if (root==NULL) return 0; else { if (root->left!=NULL && root->right!=NULL) return 1+demnut2caycon(root->left)+demnut2caycon(root->right); return demnut2caycon(root->left)+demnut2caycon(root->right); } } int nguyento(int n) { if (n<2) return 0; int k=sqrt(n);

Page 80: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 79

for (int i=2;i<=k;i++) if (n%i==0) return 0; return 1; } //----------------Đếm xem có bao nhiêu nút có giá trị nguyên tố ?---------------- int demnutnguyento(tree root) { if (root==NULL) return 0; return nguyento(root->info)+demnutnguyento(root->left)+demnutnguyento(root->right); } Dạng 4: Một số hàm liên quan đến khái niệm chiều cao và mức của cây //---------------Chiều cao của cây--------------- int chieucao(tree root) { if (root==NULL) return 0; else { long hr=chieucao(root->left); long hl=chieucao(root->right); return 1+((hl<hr) ? hr:hl); } } //---------------Tìm mức của nút có giá trị là x---------------- int muccuanut(tree root, int x) { if (root!=NULL) { if (root->info==x) return 0; if (root->info>x) return 1+muccuanut(root->left,x); if (root->info<x) return 1+muccuanut(root->right,x); }

Page 81: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 80

return 0; } Dạng 5: Các hàm xóa một nút của cây

Việc hủy một phần tử k ra khỏi cây phải bảo đảm điều kiện ràng buộc của CNPTK. Có 3 trường hợp khi hủy nút k có thể xảy ra: • k là nút lá. • k chỉ có 1 con (trái hoặc phải). • k có đủ cả 2 con

Trường hợp thứ nhất: Chỉ đơn giản hủy k vì nó không móc nối đến phần tử nào khác.

Trường hợp thứ hai: Trước khi hủy k ta móc nối cha của k với con duy nhất của nó.

Trường hợp cuối cùng: Ta không thể hủy trực tiếp do k có đủ 2 con ⇒ Ta sẽ hủy gián tiếp. Thay vì

hủy k, ta sẽ tìm một phần tử thế mạng k’. Phần tử này có tối đa một con. Thông tin lưu tại k’ sẽ được chuyển lên lưu tại k. Sau đó, nút bị hủy thật sự sẽ là k’ giống như 2 trường hợp đầu. Vấn đề là phải chọn k’ sao cho khi lưu k’ vào vị trí của k, cây vẫn là CNPTK. Có 2 phần tử thỏa mãn yêu cầu:

• Phần tử nhỏ nhất (trái nhất) trên cây con phải. • Phần tử lớn nhất (phải nhất) trên cây con trái.

Việc chọn lựa phần tử nào là phần tử thế mạng hoàn toàn phụ thuộc vào ý thích của người lập trình. Ở đây, chúng tôi sẽ chọn phần tử (phải nhất trên cây con trái làm phân tử thế mạng. Hàm xoanut trả về giá trị 1, 0 khi hủy thành công hoặc không có k trong cây: Hủy toàn bộ CNPTK Việc toàn bộ cây có thể được thực hiện thông qua thao tác duyệt cây theo thứ tự sau. Nghĩa là ta sẽ hủy cây con trái, cây con phải rồi mới hủy nút gốc. //--------Hàm tìm phần tử thế mạng------------------------ void phantuthemang(tree &p,tree &q)

Page 82: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 81

{ if (q->left) phantuthemang(p,q->left); else { p->info=q->info; p=q; q=q->right; } } //---------------Xóa nút có giá trị là k---------------------- int xoanut(tree &root,int k) { if (root==NULL) return 0; if (root->info>k) return xoanut(root->left,k); if (root->info<k) return xoanut(root->right,k); else { node *p=root; if (root->right==NULL) root=root->left; else if (root->left==NULL) root=root->right; else { node *q=root->right; phantuthemang(p,q); } delete(p); } return 1; }

Page 83: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 82

C.Bài tập BT4-1.a.Hãy vẽ cây nhị phân tìm kiếm T biết rằng khi duyệt cây T theo thứ tự left – right - node thì được dãy như sau: 1, 4, 7, 5, 3, 16, 18, 15, 29, 25, 30, 20, 8 -Hãy duyệt cây T trên theo thứ tự node - left - right. b.Hãy vẽ cây nhị phân tìm kiếm T biết rằng khi duyệt cây T theo thứ tự Node – Left -Right thì được dãy: 9, 4, 1, 3, 8, 6, 5, 7, 10, 14, 12, 13, 16, 19. -Hãy duyệt cây T trên theo thứ tự left – right – node, left -node-right. -Hãy vẽ lại cây sau khi xoá nút 10 sao cho T vẫn là cây nhị phân tìm kiếm. BT4-2.a.Hãy vẽ lại cây nhị phân T biết khi duyệt cây T ta được dãy số sau: NLR: 24 5 12 14 18 20 33 13 19 28 LNR: 12 5 18 14 33 20 24 19 28 13 b.Hãy vẽ cây nhị phân tìm kiếm T biết rằng thứ tự các số nhập vào là: 8, 3, 5, 2, 20, 11, 30, 9, 18, 4. Sau đó, nếu hủy lần lượt các nút 5, 20 (theo thứ tự đó) thì cây sẽ thay đổi như thế nào trong từng bước hủy (vẽ lại cây cho mỗi bước). BT4-3.Cho cây nhị phân tìm kiếm T, mỗi nút chứa một số nguyên. Hãy viết các hàm thực hiện các yêu cầu sau: a.Đếm xem trong cây có bao nhiêu nút có giá trị âm? bao nhiêu nút có giá trị dương? b.Tìm phần tử dương nhỏ nhất của cây. BT4-4.a.Đếm xem trong cây có bao nhiêu nút mà gia trị của nó cùng với giá trị của hai cây con trực tiếp của nó cũng là số nguyên tố? b.Tìm cây con sao cho (số nút của cây con – số nút nguyên tố của cây con) là nhỏ nhất. c.Tìm cây con T có tổng lớn nhất (chi tìm một cây thỏa). d.Tìm cây con khác nút lá chỉ chứa toàn số nguyên tố. BT4-5.Xây dựng cấu trúc cây sao cho mỗi nút của cây lưu trữ một phân số (tử số và mẫu số là các số nguyên). a.Hãy tìm phân số có giá trị lớn nhất b.Tính tổng giá trị các phân số trong cây. BT4-6.Biểu diễn tình trạng cây cân bằng AVL sau khi thực hiện các thao tác sau: Lần lượt thêm các node theo trình tự: 13,7,2,11,19,16,4,3,1,8,12,6,24,14,20,23,18 a.Xóa 13

Page 84: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 83

b.Xóa 19 BT4-7.Cho một cây nhị phân T, mỗi nút là một số nguyên. Hãy viết các hàm thực hiện các yêu cầu sau: a.Cho biết chiều cao của cây AVL b.Kiểm tra xem T có phải là cây cân bằng hoàn toàn không ? c.Kiểm tra xem T có phải là cây nhị phân cân bằng không ? d.Thêm một phần tử vào cây AVL e.Hủy một phần tử trên cây AVL. BT4-8.a.Tìm một dãy n khóa sao cho khi lần lượt dùng giải thuật thêm vào cây AVL sẽ phải thực hiện mỗi thao tác cân bằng (LL, LR, RL, RR) lại ít nhất một lần. b.Hãy vẽ cây AVL với 12 nút có chiều cao cực đại trong tất cả các cây AVL 12 nút. BT4-9.Cho một cây nhị phân biểu diễn 1 biểu thức toán học. Biết rằng gốc của cây là root, mỗi nút có thuộc tính key (kiểu char) , chứa 1 phép tính(+,- , *,/) hay một giá trị nguyên (các nút lá). Hãy viết hàm tính giá trị biểu thức chứa trong cây. BT4-10.Viết chương trình quản lý một danh bạ điện thoại đơn giản bao gồm các thông tin: số thuê bao, họ tên chủ thuê bao, địa chỉ. Chương trình cho phép tìm kiếm thông tin, thêm hoặc xóa đi một số thuê bao nào đó. BT4-11.Xây dựng cấu trúc cây từ điển sao cho mỗi nút của cây lưu trữ nghĩa tiếng Anh và nghĩa tiếng Việt của một từ nào đó. Hãy viết chức năng tra Anh-Việt cho một từ nào đó BT4-12.Cho cây nhị phân T trong đó thông tin tại mỗi nút trong cây biểu diễn các thành phần thông tin của một độc giả. Biết rằng mỗi độc giả gồm những thành phần: Mã độc giả, tên độc giả, ngày sinh, địa chỉ, ngày lập thẻ.

a.Tìm địa chỉ của độc giả lớn tuổi nhất trong cây. b.Liệt kê các độc giả trong cây sinh sau năm 1985 c.Đếm số lượng node có đủ 2 cây con có ngày lập thẻ trong ngày 09/09/2010. d.Tìm kiếm địa chỉ theo mã độc giả. e.Liệt kê các độc giả trong cây.

Page 85: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 84

Một số đề thi mẫu

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 1) Thời gian làm bài: 90 phút, không kể thời gian phát đề

Câu I (2 điểm)

a.Viết hàm sắp xếp dãy n số nguyên ao,a1,a2,...an-1 tăng dần theo giải thuật Heap sort.

b.Hãy minh họa cách sắp xếp tăng dần dãy số sau đây theo giải thuật Heap sort: 26, 7, 22, 15, 1, 18, 11, 27 Câu II (2 điểm)

Cho cây nhị phân tìm kiếm T gồm 12 số nguyên với phép duyệt LRN cho kết quả như sau: 1, 3, 2, 6, 7, 5, 4, 10, 9, 12, 11, 8

a.Hãy vẽ cây nhị phân tìm kiếm T. b.Duyệt cây T theo thứ tự NLR c.Vẽ lại cây sau khi xoá nút 8 sao cho T vẫn cây nhị phân tìm kiếm.

Câu III (3 điểm) Cho một danh sách liên kết đơn l; mỗi nút là một số nguyên dương. a.Viết hàm tạo danh sách l1 chỉ chứa các số nguyên tố từ danh sách l. b.Viết hàm xóa các số nguyên tố trong danh sách l.

Câu IV(3 điểm) Cho cây nhị phân tìm kiếm T; mỗi nút là một số nguyên. a.Viết hàm tìm mức của nút có giá trị lớn nhất. b.Viết hàm tìm tất cả các đường đi từ gốc có độ dài là 4 trên cây. c.Viết hàm đếm số cây con chỉ chứa toàn là số nguyên tố.

--------------------------------------------------Hết-----------------------------------------

Page 86: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 85

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 2) Thời gian làm bài: 90 phút, không kể thời gian phát đề

Câu I (2 điểm)

a.Viết hàm sắp xếp dãy n số nguyên ao,a1,a2,...an-1 tăng dần theo giải thuật Quick sort.

b.Hãy minh họa cách sắp xếp tăng dần dãy số sau đây theo giải thuật Quick sort: 28, 9, 25, 19, 5, 21, 14, 31. Câu II (2 điểm)

Cho một mảng các giá trị nguyên như sau: Chỉ số 0 1 2 3 4 5 6 7 8 9 Giá trị 10 3 16 7 1 4 9 14 2 8

a.Cho biết kết quả sau khi biến đổi mảng trên thành dãy heap. b.Thể hiện dãy heap đó theo dạng cây nhị phân.

Câu III (3 điểm) Hãy khai báo kiểu cấu trúc dữ liệu liên kết đơn mà mỗi phần tử chứa thông

tin về một quyển sách bao gồm các trường: Mã số sách (số nguyên 4 byte), tên sách (chuỗi, tối đa 40 ký tự), tác giả (chuỗi, tôi đa 30 ký tự), năm xuất bản (số nguyên 4 byte).

a.Đếm số sách xuất bản năm X. b.Sắp xếp danh sách theo mã số sách.

Câu IV (3 điểm) Giả sử T là một cây nhị phân tìm kiếm mà tại mỗi nút lưu một số nguyên.

Hãy khai báo kiểu dữ liệu T và xây dựng các hàm sau: a.Hàm tìm kiếm một node có khóa X (trả về con trỏ trỏ dến nút tìm được). b.Hàm đếm số nút có khóa lớn hơn X. c.Hãy tìm cây con có tổng lớn nhất.

--------------------------------------------------Hết-----------------------------------------

Page 87: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 86

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 3) Thời gian làm bài: 90 phút, không kể thời gian phát đề

Câu I (2 điểm) a.Viết hàm cài đặt giải thuật Selection-Sort bằng đệ qui và không đệ qui. b.Cách cài đặt giải thuật này bằng đệ qui có gì tốt (không tốt) so với kiểu cài

đặt bằng phép lặp thông thường. c.Hãy cho biết số lần so sánh và số lần gán của thuật toán Selection-Sort.

Câu II(2 điểm) Làm thế nào để chuyển các phần tử trong stack S1 sang stack S2 với những

yêu cầu như sau: • Thứ tự các phần tử trong S2 giống như trong S1 • Không được dùng thêm stack phụ, chỉ được dùng thêm vài biến phụ. • Viết giải thuật dạng mã giả.

Câu III (3 điểm) a.Cho hai danh sách liên kết đơn l1,l2 có các phần tử là các số nguyên và đã

được sắp tăng dần. Hãy trộn hai danh sách này để được một danh sách l3 cũng được sắp tăng dần (không được sắp xếp).

b.Cho một hàng đợi với các thao tác cơ bản. Hãy viết các hàm thực hiện các công việc sau đây:

-Lấy ra phần tử cuối cùng của hàng đợi Lấy ra phần tử thứ n của hàng đợi, với n là số nguyên dương. Cài đặt cụ thể các giải thuật trên bằng ngăn xếp các số nguyên với cách tổ chức hàng đợi bằng danh sách liên kết.

Câu IV(3 điểm) Cho cây nhị phân tìm kiếm T, mỗi nút chứa một số nguyên. Hãy viết các hàm

thực hiện các yêu cầu sau: a.In ra tất cả các nút ở mức thứ k của cây T b.In ra tất cả các nút theo thứ tự tầng 0 đến tầng thứ h-1 của cây T( h là chiều cao của cây)

--------------------------------------------------Hết-----------------------------------------

Page 88: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 87

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 4) Thời gian làm bài: 90 phút, không kể thời gian phát đề

Câu I (2 điểm) a.Nếu ta xóa 1 phần tử khỏi cây BST và sau đó thêm nó vào lại cây. Hỏi: hình dạng của cây đó bị thay đổi không? Giải thích? b.Chứng minh rằng một cây nhị phân với chiều cao h sẽ có tối đa 2h-1 nút Câu II (2 điểm) a.Sử dụng Stack, viết chương trình chuyển đổi một số nguyên n trong hệ thập phân (hệ 10) sang biểu diễn hệ nhị phân (hệ 2). b.Hãy áp dụng giải thuật Balan ngược để biến đổi biểu thức sau thành dạng postfit: (a+b/(c-d))*e. Trình bày trạng thái của stack sau mỗi lần có sự thay đổi. Câu III (3 điểm)

Hãy quản lý các số thuê bao điện thoại bằng danh sách liên kết đơn. Mỗi phần tử chứa các thông tin: họ tên, địa chỉ, số điện thoại cố định.

Hãy viết hàm tìm kiếm địa chỉ của một số điện thoại cho trước. Câu IV (3 điểm)

Cho cây nhị phân tìm kiếm T, mỗi nút chứa một số nguyên. Hãy viết các hàm thực hiện các yêu cầu sau: a.In đường đi từ gốc đến nút có giá trị lớn nhất.

b.Tìm một cây con không chứa số nguyên tố nào. --------------------------------------------------Hết-----------------------------------------

Page 89: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 88

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 5)

Thời gian làm bài: 90 phút, không kể thời gian phát đề Câu I(2 điểm) Cho mảng A chứa các phần tử:

Vị trí i 0 1 2 3 4 5 6 7 8 9 A[i] 5 1 2 8 6 10 3 9 4 7

a.Hãy cho biết kết quả sau khi hiệu chỉnh mảng A thành (binary) heap? b.Vẽ (binary) heap ở dạng cây nhị phân. c.Cho biết số phần tử tối thiểu và tối đa của một (binary) heap có chiều cao là h ?

Câu II(2 điểm) a.Hãy minh họa cách sắp xếp tăng dần dãy số sau đây theo giải thuật Quick

sort: 28, 9, 25, 19, 5, 21, 14, 31. b.Chứng minh giải thuật Merge sort có độ phức tạp là O(Nlog2N)

Câu III (3 điểm) Cho danh sách liên kết đơn, mỗi nút là một phân số (tử số và mẫu số là các số

nguyên) a.Hãy đếm xem trong danh sách có bao nhiêu phân số có giá trị lớn hơn 1.

b.Hãy xóa các phân số không phải là phân số tối giản trong danh sách. Câu IV (3 điểm)

Cho cây nhị phân tìm kiếm T có khóa là các số nguyên. a.In các khóa theo chiều giảm dần. b.Tìm giá trị nút lá ở mức sâu nhất (chỉ tìm 1 giá trị thỏa)

--------------------------------------------------Hết-----------------------------------------

Page 90: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 89

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 6) Thời gian làm bài: 90 phút, không kể thời gian phát đề

Câu I (2 điểm) Hãy vẽ cây nhị phân tìm kiếm T biết rằng khi duyệt cây T theo thứ tự left – right - node thì được dãy như sau: 1, 4, 7, 5, 3, 16, 18, 15, 29, 25, 30, 20, 8

-Hãy duyệt cây T trên theo thứ tự node - left - right. -Cây T có chiều cao là bao nhiêu ? Tìm các đường đi từ gốc có độ dài là 4

trên cây. Câu II (2 điểm) a.Trong giải thuật Quick sort cơ bản, phần tử pivot được chọn là: pivot=a[r]. Hãy cho biết ưu/khuyết điểm của cách chọn này. b.Với cách chọn pivot=a[(l+r)/2], hãy cho biết chi phí cần thiết để quick sort sắp xếp một mảng gồm có n phần tử bằng nhau. Câu III(3 điểm) Hãy khai báo kiểu dữ liệu danh sách liên kết đơn mà mỗi phần tử chứa thông tin về một sinh viên bao gồm các trường: mã số sinh viên (chuỗi, 12 ký tự), họ lót sinh viên (chuỗi, tối đa 12 ký tự), tên sinh viên (chuỗi, tối đa 12 ký tự), điểm trung bình tích lũy (số thực). Với các kiểu dữ liệu vừa khai báo, hãy xây dựng các hàm sau: a.Hàm xếp loại tốt nghiệp cho sinh viên. Trong đó xếp loại X(xuất sắc) nếu có điểm trung bình tích lũy (ĐTB) >=9.0; Loại G(Giỏi) nếu có điểm trung bình tích lũy 8<= (ĐTB)<9.0; Loại K(khá) nếu có điểm trung bình tích lũy 7<= ĐTB < 8; Loại T (trung bình) nếu có điêm trung bình tích lũy 5<= ĐTB <7. b.Hàm sắp xếp danh sách theo điểm trung bình tích lũy. c.Hàm sắp xếp danh sách đạt loại X, danh sách cần được sắp theo tên tăng dần, nếu tên trùng thì sắp theo họ tăng dần. Câu IV (3 điểm) Giả sử T là một cây nhị phân tìm kiếm mà tại mỗi nút lưu một số nguyên. Hãy khai báo kiểu dữ liệu T và xây dựng các hàm sau:

a.Hàm thêm một node có khóa X vào T. b.Hàm đếm số nút có khóa lớn hơn X nhưng nhỏ hơn Y (X<Y).

--------------------------------------------------Hết-----------------------------------------

Page 91: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 90

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 7) Thời gian làm bài: 90 phút, không kể thời gian phát đề

Câu I (2 điểm) Cho cây nhị phân tìm kiếm T gồm 12 số nguyên với thứ tự các nút được chèn vào cây như sau: 11 (nút gốc), 7, 5, 1, 6, 8, 10, 9, 18, 12, 13, 19. -Hãy vẽ cây nhị phân tìm kiếm T. -Hãy duyệt cây T trên theo thứ tự NLR. -Hãy duyệt cây T trên theo thứ tự RNL. -Hãy vẽ lại cây sau khi xoá nút 11 sao cho T vẫn là cây nhị phân tìm kiếm. Câu II(2 điểm)

a.Hãy nêu sự khác nhau của cây nhị phân tìm kiếm (BST) và Heap? b.Có thể liệt kê N phần tử của Heap theo thứ tự tăng dần với chi phí O(N)

không? Vì sao? Câu III (3 điểm) Cho danh sách liên kết đơn l, mỗi nút là một phân số (giả sử tử số và mẫu số của các phân số là các số nguyên dương). Cấu trúc của danh sách liên kết l được cho như sau:

struct Phanso {int tu; int mau;}; struct Node {Phanso info; struct Node *next;}; struct List {Node *head,*tail;};

Hãy viết các hàm thực hiện các yêu cầu sau: a.Hãy đếm số lượng phân số có giá trị lớn hơn 1 của danh sách l. b.Hãy tạo danh sách l1 chỉ chứa các phân số tối giản từ danh sách l (phân số tối giản là phân số mà ước số chung lớn nhất của từ số và mẫu số của nó bằng 1). c.Đếm số lượng phân số tối giản của danh sách l (phân số tối giản là phân số mà ước số chung lớn nhất của từ số và mẫu số của nó bằng 1). Câu IV (3 điểm) Hãy viết phần khai báo cấu trúc dữ liệu để mô tả một cây nhị phân tìm kiếm mà mỗi phần tử là một số nguyên dương. Hãy viết các hàm thực hiện các yêu cầu sau: a.Viết hàm đếm xem trong cây có bao nhiêu nút có đúng 2 cây con ? b.Viết hàm tính tổng các nút có giá trị nguyên tố trong cây.

--------------------------------------------------Hết-----------------------------------------

Page 92: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 91

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 8)

Thời gian làm bài: 90 phút, không kể thời gian phát đề Câu I (2 điểm) a.Sử dụng danh sách liên kết kép để khai báo cấu trúc cho một danh sách mà mỗi nút là một số nguyên. b.Hãy viết hàm sắp xếp một danh sách tăng dần bằng cách sử dụng danh sách liên kết kép. Câu II (2 điểm) Hãy viết các hàm tìm kiếm một phần tử x trong các trường hợp: Danh sách cần tìm được biểu diễn bằng một mảng 1 chiều, danh sách cần tìm được biểu diễn bằng một danh sách liên kết đơn, danh sách cần tìm được biểu diễn bằng một cây nhị phân tìm kiếm. Các hàm này nếu tìm thấy trả về giá trị 1, nếu không tìm thấy trả về giá trị 0. Câu III (3 điểm) 1.Cho một danh sách liên kết đơn trong đó, mỗi phần tử chứa 1 số nguyên Hãy viết hàm xóa phần tử có giá trị lớn nhất trong danh sách. Lưu ý:

− Chỉ được phép duyệt danh sách 1 lần − Nếu có nhiều phần tử lớn nhất giống nhau, xóa phần tử đầu tiên

2.Dùng ngăn xếp các số nguyên với các phép toán push,pop. Hãy lập chương trình đổi một số thập phân thành số nhị phân. Câu IV (3 điểm)

Cho cây nhị phân tìm kiếm T có khóa là các số nguyên. a.Liệt kê tất cả các phần tử của một cây nhị phân tìm kiếm thỏa điều kiện giá

trị của phần tử nằm trong khoảng [Min, Max] cho trước. b.Tìm cây con T có tổng lớn nhất (chi tìm 1 cây thỏa)

--------------------------------------------------Hết-----------------------------------------

Page 93: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 92

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 9)

Thời gian làm bài: 90 phút, không kể thời gian phát đề Câu I (2 điểm) a.Hãy định nghĩa một heap và nêu các tính chất của một heap b.Hãy hiệu chỉnh dãy số sau đây thành một heap 12 2 8 5 1 6 4 15 Câu II (2 điểm) a.Hãy viết hàm sắp xếp chọn trực tiếp bằng cách sử dụng danh sách liên kết đơn. Yêu cầu thay đổi các mối liên kết (thao tác trên vùng next). b.Bằng các thao tác cơ bản trên hàng đợi sau đây:

int Qisempty(List Q) : kiểm tra hàng đợi rỗng void Qpush(List &Q, int x) :Thêm một phần tử vào cuối hàng đợi int Qpop(List &Q) : Trích/hủy phần tử ở đâu hàng đợi

Hãy viết hàm tính tổng các phần tử của mảng một chiều n phần tử. Câu III (3 điểm) a.Cho một danh sách liên kết đơn l chứa các số nguyên đã có thứ tự tăng. Hãy chèn một số nguyên x vào danh sách l để l vẫn có thứ tự tăng. b.Cho một danh sách liên kết đơn l chứa các số nguyên. Hãy giữ lại một giá trị trong các giá trị trùng nhau của l. Câu IV (3 điểm) a.Hãy viết phần khai báo cấu trúc dữ liệu để mô tả một danh sách liên kết đơn l mà mỗi phần tử là một số nguyên. Hãy viết hàm sắp xếp các phần tử trong danh sách l theo chiều tăng dần. b.Hãy viết phần khai báo cấu trúc dữ liệu để mô tả một cây nhị phân tìm kiếm mà mỗi phần tử là một số nguyên. Hãy viết hàm tìm chiều cao của cây.

--------------------------------------------------Hết-----------------------------------------

Page 94: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 93

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 10)

Thời gian làm bài: 90 phút, không kể thời gian phát đề Câu I (2 điểm) a.Nêu các định nghĩa sau: Cây nhi phân, cây nhị phân tìm kiếm, cây nhị phân cân bằng hoàn toàn, cây nhị phân cân bằng. b.Hãy cài đặt thuật toán tìm kiếm tuyến tính c.Hãy cho biết số lần so sánh của giải thuật tìm kiếm tuyến tính trong trường hợp xấu nhất, tốt nhất, trung bình. Hãy cho biết độ phức tạp của thuật toán này. Câu II (2 điểm) Viết các hàm sau đây: a.Thêm một phần tử x vào stack S bằng cách dùng danh sách liên kết. b.Xem thông tin của phần tử ở đỉnh stack S bằng cách dùng danh sách liên kết. c.Thêm một phần tử x vào cuối hàng đợi Q bằng cách dùng danh sách liên kết. d.Xem thông tin của phần tử ở đầu hàng đợi Q bằng cách dùng danh sách liên kết. Câu III (3 điểm) Cho một danh sách liên kết đơn trong đó, mỗi phần tử chứa 1 số nguyên b.Viết hàm xóa tất cả các số nguyên tố trong danh sách. c.Hãy tìm tần số xuất hiện của các giá trị Câu IV (3 điểm)

Cho cây nhị phân tìm kiếm T có khóa là các số nguyên. a.Viết hàm tìm mức của một nút có giá trị x. b.Viết hàm in các nút ở mức k của cây.

--------------------------------------------------Hết-----------------------------------------

Page 95: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 94

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 11) Thời gian làm bài: 90 phút, không kể thời gian phát đề

Câu I (2 điểm) a. Nêu các định nghĩa về cách tổ chức danh sách liên kết sau: Danh sách liên kết đơn, danh sách liên kết kép, danh sách liên kết vòng, danh sách có nhiều mối liên kết. b.Hãy cài đặt giải thuật tìm kiếm nhị phân bằng đệ qui. c.Hãy cho biết số lần so sánh của giải thuật tìm kiếm nhị phân trong trường hợp xấu nhất, tốt nhất, trung bình. Hãy cho biết độ phức tạp của thuật toán này. Câu II (2 điểm) Sử dụng Stack, viết chương trình nhập vào một số nguyên, không âm bất kỳ, sau đó xuất ra màn hình số đảo ngược thứ tự các chữ số của số nhập vào. Ví dụ: - Nhập vào một số nguyên: 10245

- Số nguyên ở dạng đảo ngược: 54201 Câu III (3 điểm) a.Cho hai danh sách liên kết đơn l1,l2 chứa các số nguyên. Hãy nối danh sách l2 vào sau danh sách l1. b.Khai báo một danh sách liên kết đơn trong đó, mỗi phần tử lưu trữ dữ liệu gồm: Giá trị của nút đó: kiểu số nguyên Tần số xuất hiện của giá trị:Kiểu số nguyên. Hãy viết hàm rút gọn danh sách liên kết đơn theo dạng sau: Nếu các phần tử có cùng giá trị thì chỉ giữ lại một phần tử và tần số của giá trị đó thì được cộng thêm 1. Câu IV (3 điểm)

Giả sử ta có một tập hợp gồm n phần tử kiểu số nguyên cần lưu trữ bằng cấu trúc cây nhị phân tìm kiếm (BST). Lưu ý rằng các phần tử có thể trùng nhau. Hãy trình bày một đề xuất về cấu trúc cây BST sao cho:

a.Lưu trữ và tìm kiếm được các phần tử. Với các trường hợp phần tử trùng nhau, cần thể hiện được số lượng phần tử trùng lặp.

b.Mô tả cấu trúc bằng C/C++ c.Viết hàm cho hai thao tác chính: Thêm phần tử, xóa phần tử.

--------------------------------------------------Hết-----------------------------------------

Page 96: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 95

MÔN: CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN (ĐỀ SỐ 12)

Thời gian làm bài: 90 phút, không kể thời gian phát đề Câu I (2 điểm)

a.Hãy viết các hàm sắp xếp theo giải thuật chèn trực tiếp và nổi bọt. b.Cho dãy gồm các số nguyên sau: 99 52 33 84 21 75 68 Hãy mô phỏng các bước sắp xếp tăng dần dãy số trên bằng các giải thuật

chèn trực tiếp và nổi bọt. Câu II (2 điểm) a.Hãy vẽ lại cây nhị phân T biết khi duyệt cây T ta được dãy số sau: NLR: 15 1 2 4 7 10 21 3 9 19 LNR: 2 1 7 4 21 10 15 9 19 3 b.Nếu ta xóa 1 phần tử khỏi cây BST và sau đó thêm nó vào lại cây. Hỏi: hình dạng của cây đó bị thay đổi không? Giải thích? Câu III (3 điểm) a.Cho 2 số nguyên lớn a và b (số chữ số >= 100). Sử dụng danh sách liên kết đơn để biểu diễn cho số nguyên a và số nguyên b. Hãy viết hàm tính tổng a+b b. Cho hai danh sách liên kết đơn l1,l2 chứa các số nguyên. Hãy trộn các phần tử của l1 và l2 vào l3 để l3 cũng được sắp xếp tăng (Hãy lại giải bài toán bằng cách chèn các phần tử của l2 vào l1 để l1 sau đó cũng được sắp tăng (l1 chứa các phần tử của l1 và l2 trước đó) Câu IV (3 điểm) Cho cây nhị phân tìm kiếm T, mỗi nút chứa một số nguyên. Hãy viết các hàm thực hiện các yêu cầu sau: a.Đếm các nút là có giá trị > n1 và <n2 b.Xóa tất cả các nút mang giá trị là số nguyên tố.

--------------------------------------------------Hết-----------------------------------------

Page 97: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 96

Gợi ý và hướng dẫn giải bài tập Chương 1: Tổng quan về cấu trúc dữ liệu và giải thuật BT1-1a. #include<stdio.h> #include<conio.h> #include<iostream.h> //------------------------------------------------------------ void nhapmang(int a[],int &n) { cin>>n; for(int i=0;i<n; i++) cin>>a[i]; } int daychandainhat(int a[],int n) { int chieudaimax=0,demchan=0; for (int i=0;i<n;i++) if (a[i]%2==0) demchan++; else { if (demchan>chieudaimax) chieudaimax=demchan; demchan=0; } return demchan>chieudaimax?demchan:chieudaimax; } void main() {

Page 98: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 97

clrscr(); int a[100],n; nhapmang(a,n); cout<<daychandainhat(a,n); getch(); } BT1-2. #include <iostream.h> #include <conio.h> void nhap(int a[], int &n, int &k); void xuat(int a[], int n); void daonguoc(int a[], int u,int v); void daodoan(int a[], int n, int k); void main(void) { int a[100],n,k; nhap(a,n,k); daodoan(a,n,k); xuat(a,n); } void nhap(int a[], int &n, int &k) { clrscr(); cin>>n; for (int i=0;i<n;i++) cin>>a[i]; cin>>k; } void daonguoc(int a[],int u,int v)

Page 99: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 98

{ int tam; while (u<v) { tam=a[u]; a[u++]=a[v]; a[v--]=tam; } } void daodoan(int a[], int n, int k) { daonguoc(a,0,k-1); daonguoc(a,k,n-1); daonguoc(a,0,n-1); } void xuat(int a[], int n) { for (int i=0;i<n;i++) cout<<a[i]<<" "; getch(); } BT1-3 #include <conio.h> #include <iostream.h> void congsolon(int a[], int m, int b[], int n); void trusolon(int a[], int m, int b[], int n); void nhansolon(int a[], int m, int b[], int n); void main() { clrscr(); int m,n;

Page 100: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 99

int a[100],b[100]; nhansolon(a,m,b,n); getch(); } void congsolon(int a[], int m, int b[], int n) { cin>>m>>n; int max=m>n?m:n; for (int i=0;i<=max;i++) {a[i]=0;b[i]=0;} for (i=max-m+1;i<=max;i++) cin>>a[i]; for (i=max-n+1;i<=max;i++) cin>>b[i]; int c[100]; int sonho=0; for (i=max;i>0;i--) { c[i]=(a[i]+b[i]+sonho)%10; sonho=(sonho+a[i]+b[i])/10; } c[0]=sonho; if (c[0]!=0) cout<<c[0]; for (i=1;i<=max;i++) cout<<c[i]; } void trusolon(int a[], int m, int b[], int n) { cin>>m>>n; int max=m>n?m:n; for (int i=0;i<=max;i++) {a[i]=0;b[i]=0;} for (i=max-m+1;i<=max;i++) cin>>a[i]; for (i=max-n+1;i<=max;i++) cin>>b[i]; int c[100];

Page 101: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 100

for (i=max;i>=1;i--) if (a[i]<b[i]) { c[i]=a[i]+10-b[i]; b[i-1]=b[i-1]+1; } else c[i]=a[i]-b[i]; if (c[1]!=0) cout<<c[1]; for (i=2;i<=max;i++) cout<<c[i]; } void nhansolon(int a[], int m, int b[], int n) { int c[100]; cin>>m>>n; for (int i=1;i<=m;i++) cin>>a[i]; for (i=1;i<=n;i++) cin>>b[i]; for (i=1;i<=m+n;i++) c[i]=0; for (i=1;i<=m;i++) for (int j=1;j<=n;j++) c[i+j]=c[i+j]+a[i]*b[j]; for (i=m+n;i>=3;i--) { c[i-1]=c[i-1] + c[i]/10; c[i]=c[i]%10; } for (i=2;i<=m+n;i++) cout<<c[i]; }

Page 102: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 101

BT1-4 #include<conio.h> #include<iostream.h> void nhapdathuc(int a[], int &n) { cin>>n; for(int i=n; i>=0;i--) cin>>a[i]; } int giatridathuc(int a[],int n,int x) { cout<<"Nhap gia tri xo = ";cin>>x; int mu=1; int tong=0; for(int i=0;i<=n;i++) { tong+=a[i]*mu; mu=mu*x; } return tong; } void tongdathuc(int a[], int m, int b[], int n, int c[], int &l) { l=m>n?m:n; for(int i=l;i>=0;i--) c[i]=0; for(i=0;i<=l;i++) c[i]=a[i]+b[i]; } void hieudathuc(int a[], int m, int b[], int n, int c[], int &l)

Page 103: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 102

{ l=m>n?m:n; for(int i=l;i>=0;i--) c[i]=0; for(i=0;i<=l;i++) c[i]=a[i]-b[i]; } void tichdathuc(int a[], int m, int b[], int n, int c[], int &l) { for(int i=m+n;i>=0;i--) c[i]=0; for(i=0;i<=n;i++) for(int j=0;j<=m;j++) c[i+j]+=a[i]*b[j]; l=m+n; } void xuatdathuc(int nc[],int n) { cout<<endl<<"Da thuc ket qua : "; for(int i=n;i>0;i--) cout<<c[i]<<"x^"<<i<<" + "; cout<<c[0]; } void main() { clrscr(); int a[100],b[100],c[100],x; int m,n,l; nhapdathuc(a,m); nhapdathuc(b,n); tichdathuc(a,m,b,n,c,l); xuatdathuc(c,l);

Page 104: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 103

getch(); }

Page 105: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 104

Chương 2: Tìm kiếm và sắp xếp BT2-2. #include <iostream.h> #include <conio.h> void input(int a[],int &n); int linearsearch ( int a[], int n, int x ); int linearsearch_sentinel( int a[], int n, int x ); int binarysearch(int a[],int n,int x); int binarysearch_recursive(int a[],int n,int x,int left,int right); void main() { clrscr(); int a[1000],n,x; input(a,n); cout<<"Nhap phan tu can tim : ";cin>>x; cout<<binarysearch(a,n,x); getch(); } void input(int a[],int &n) { cin>>n; for (int i=0;i<n;i++) cin>>a[i]; } int linearsearch ( int a[], int n, int x ) { int i = 0; while (i<n && a[i]!=x) i++;

Page 106: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 105

if (i==n) return -1; return i; } int linearsearch_sentinel( int a[], int n, int x ) { int i = 0; a[n] = x; while (a[i]!=x) i++; if( i==n) return -1; return i; } int binarysearch(int a[],int n,int x) { int left=0,right=n-1,mid; do { mid=(left+right)/2; if (x==a[mid]) return mid; else if (x<a[mid]) right=mid -1; else left=mid+1; } while (left<=right); return -1;

Page 107: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 106

} int binarysearch_recursive(int a[],int n,int x,int left,int right) { if (left>right) return -1; int mid=(left+right)/2; if (x==a[mid]) return mid; if (x<a[mid]) return binarysearch_recursive(a,n,x,left,mid-1); return binarysearch_recursive(a,n,x,mid+1,right); } #include <iostream.h> void input(int a[],int &n); void output(int a[],int n); void interchangesort(int a[], int n); void selectionsort(int a[],int n); void insertionsort(int a[], int n); void bubblesort(int a[],int n); void swap(int &x,int &y); void main() { int a[100],n; input(a,n); bubblesort(a,n); output(a,n); } void input(int a[],int &n)

Page 108: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 107

{ cin>>n; for (int i=0;i<n;i++) cin>>a[i]; } void output(int a[],int n) { for (int i=0;i<n;i++) cout<<a[i]<<" : "; } void swap(int &x,int &y) { int temp=x; x=y; y=temp; } void interchangesort(int a[], int n) { for (int i=0; i<n-1;i++) for (int j=i+1;j<n ;j++) if (a[i]>a[j]) swap(a[i],a[j]); } void selectionsort(int a[],int n) { for(int i = 0;i < n -1; i++) { int min = i;

Page 109: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 108

for ( int j = i+1; j < n; j++) if (a[j] < a[min]) min = j; swap( a[min], a[i] ); } } void insertionsort(int a[], int n) { int pos; int x; for(int i=1; i<n; i++) { x=a[i]; pos=i-1; while((pos>=0) && (a[pos]>x)) { a[pos+1] = a[pos]; pos--; } a[pos+1]=x; } } void bubblesort(int a[],int n) { for (int i=1;i<n;i++) for (int j=n-1;j>=i;j--) if (a[j]<a[j-1]) swap(a[j],a[j-1]); } #include <iostream.h> #include <stdlib.h>

Page 110: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 109

#include <conio.h> void input(int a[],int &n); void output(int a[],int n); void insertheap(int a[],int l,int r); void heapsort(int a[],int n); void quicksort(int a[],int l,int r); void mergesort(int a[],int n); void swap(int &x,int &y); int n; void main() { clrscr(); int a[100]; input(a,n); //heapsort(a,n); mergesort(a,n); output(a,n); getch(); } void input(int a[],int &n) { cin>>n; for (int i=0;i<n;i++) cin>>a[i]; }

Page 111: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 110

void output(int a[],int n) { for (int i=0;i<n;i++) cout<<a[i]<<" : "; cout<<endl; } void insertheap(int a[],int l,int r) { int j; j=l*2; while (j<=r) { if (j<r) if (a[j] <a[j+1]) j++; if (a[j] <=a[l]) return; swap(a[l],a[j]); l=j; j=l*2; } } void heapsort(int a[],int n) { int l=n/2; int r=n-1; while (l>=0) insertheap(a,l--,r); while (r>0) { swap(a[0],a[r]);

Page 112: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 111

r--; insertheap(a,0,r); } } void quicksort(int a[],int l,int r) { int x=a[(l+r)/2]; int i=l; int j=r; do { while (a[i]<x) i++; while (a[j]>x) j--; if (i<=j) swap(a[i++],a[j--]); } while (i<j); if (l<j) quicksort(a,l,j); if (i<r) quicksort(a,i,r); } void mergesort(int a[],int n) { int up,j,k,q,t,r,s,d,p; for (int i=n;i>0;i--) a[i]=a[i-1]; up=1; p=1; do {

Page 113: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 112

d=1; int m=n; if (up) { i=1;j=n;k=n+1;q=2*n; } else { i=n+1;j=2*n;k=1;q=n; } do { if (m>=p) s=p; else s=m; m=m-s; if (m>=p) r=p; else r=m; m=m-r; while ((s!=0) && (r!=0)) { if (a[i]<a[j]) { a[k]=a[i];i=i+1;s=s-1; } else { a[k]=a[j];j=j-1;r=r-1; } k=k+d; } while (s!=0) { a[k]=a[i];k=k+d;i=i+1;s=s-1;

Page 114: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 113

} while (r!=0) { a[k]=a[j];k=k+d;j=j-1;r=r-1; } d=-d;t=k;k=q;q=t; } while (m!=0); up=!up; p=p*2; } while (p<n); if (!up) for (i=1;i<=n;i++) a[i]=a[i+n]; for (i=0;i<n;i++) a[i]=a[i+1]; } void swap(int &x,int &y) { int temp=x; x=y; y=temp; }

Page 115: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 114

Chương 3: Cấu trúc danh sách liên kết Dạng 3: Ứng dụng hàm duyệt danh sách //Tìm giá trị lớn nhất của danh sách int giatrilonnhat(List l) { Node *p=l.head; int max=p->info; while (p!=NULL) { p=p->next; if (p->info>max) max=p->info; } return max; } int nguyento(int n) { if (n<2) return 0; int k=sqrt(n); for (int i=2;i<=k;i++) if (n%i==0) return 0; return 1; } // Đếm xem trong danh sách có bao nhiêu số nguyên tố int demsonguyento(List l) { Node *p=l.head; int d=0; while (p!=NULL) {

Page 116: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 115

if (nguyento(p->info)) d++; p=p->next; } return d; } Dạng 4: Tách danh sách void tachdanhsachchanle(List l, List &l1, List &l2) { for (Node *p=l.head;p!=NULL;p=p->next) if (p->info%2!=0) chennutvaocuoi(l1,taonut(p->info)); else chennutvaocuoi(l2,taonut(p->info)); } //Xóa các nút có giá trị nguyên tố void xoanutnguyento(List &l) { Node *p=l.head; while (p!=NULL) { if (nguyento(p->info)) xoanutkhoak(l,p->info); p=p->next; } } BT3-2 #include<iostream.h> #include<stdlib.h>

Page 117: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 116

#include<alloc.h> #include<conio.h> struct phanso { int tu; int mau; }; struct Node { phanso info; struct Node * next; }; struct List { Node *head; Node *tail; }; Node * taonut(phanso x); void chennutvaocuoi(List &l, Node * New_ele); void danhsachchencuoi(List &l); void duyetdanhsach(List l); void phansolonnhat(List l); void demphanso01(List l); void hoanvi(phanso &ps1,phanso &ps2); void sapxepphanso(List &l); void main()

Page 118: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 117

{ clrscr(); phanso ps; List l; danhsachchencuoi(l); phansolonnhat(l); demphanso01(l); sapxepphanso(l); duyetdanhsach(l); } Node * taonut (phanso ps) { Node *p; p=new Node; if(p==NULL) { cout<<"Khong du bo nho"; exit; } p->info=ps; p->next=NULL; return p; } void chennutvaocuoi(List &l, Node * New_ele) { if(l.head==NULL) { l.head=New_ele; l.tail=New_ele;

Page 119: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 118

} else { l.tail->next=New_ele; l.tail=New_ele; } } void danhsachchencuoi(List &l) { int n; phanso ps; cout<<"So phan tu cua danh sach : ";cin>>n; for(int i=1;i<=n;i++) { cout<<"Tu so:";cin>>ps.tu; cout<<"Mau so:";cin>>ps.mau; chennutvaocuoi(l,taonut(ps)); } } void duyetdanhsach(List l) { Node *p=l.head; while (p!=NULL) { cout<<p->info.tu<<"/"<<p->info.mau<<" "; p=p->next; } cout<<endl; }

Page 120: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 119

void phansolonnhat(List l) { phanso psmax; Node *p=l.head; psmax=p->info; for(p=l.head;p!=NULL;p=p->next) if(p->info.tu*psmax.mau>psmax.tu*p->info.mau) psmax=p->info; cout<<psmax.tu<<"/"<<psmax.mau; } void demphanso01(List l) { int dem=0; for(Node*p=l.head;p!=NULL;p=p->next) if(p->info.tu * p->info.mau>0 && p->info.tu<p->info.mau) dem++; cout<<"\nSo luong phan so 0< va <1 : "<<dem; } void hoanvi(phanso &ps1,phanso &ps2) { phanso ps=ps1;ps1=ps2;ps2=ps; } void sapxepphanso(List &l) { for(Node *p=l.head;p!=l.tail;p=p->next) for(Node *q=p->next;q!=NULL;q=q->next) if(p->info.tu*q->info.mau>p->info.mau*q->info.tu)

Page 121: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 120

hoanvi(p->info,q->info); } void xoadanhsach(List &l) { Node *p; while (l.head!=NULL) { p=l.head; l.head=l.head->next; delete p; } l.tail=NULL; } void noihaidanhdanh(List &l1,List l2) { l1.tail->next=l2.head; l1.tail=l2.tail; } void chenphantu(List &l,int x) // thay Quoc { cin>>x; if (l.head->info>=x) chennutvaodau(l,taonut(x)); else { Node*p=l.head; while (p->next->info<x && p->next!=NULL) p=p->next;

Page 122: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 121

if (p->next==NULL) chennutvaocuoi(l,taonut(x)); else { Node*q=taonut(x); q->next=p->next; p->next=q; } } } #include <conio.h> #include <stdlib.h> #include <iostream.h> #include <alloc.h> struct Node { int info; int tanso; struct Node *next; }; struct List { Node *head,*tail; }; void tansocacgiatri(List l) { for (Node *p=l.head;p!=NULL;p=p->next) p->tanso=1; for (p=l.head;p!=l.tail;p=p->next)

Page 123: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 122

for (Node *q=p->next;q!=NULL;q=q->next) if (p->info==q->info && p->tanso!=0) { p->tanso++; q->tanso=0; } for (p=l.head;p!=NULL;p=p->next) if (p->tanso!=0) cout<<p->info<<" xuat hien " <<p->tanso<<endl; }

Page 124: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 123

Chương 4. Cấu trúc cây //---------------In các nút ở mức k của cây--------------- void duyetnutmuck(tree root, int k) { if (root!=NULL) { duyetnutmuck(root->left,k); if (muccuanut(root1,root->info)==k) cout<<root->info<<" "; duyetnutmuck(root->right,k); } } //In các nút của cây theo từng mức từ mức 0 mức h-1 (với h là chiều cao của cây) void duyetnuttheotungmuc(tree root) { int h=chieucao(root); for (int i=0;i<h;i++) { duyetnutmuck(root,i); cout<<endl; } } //------------In các nút trên đường đi từ gốc đến nút có giá trị x---------- void duongditugoc(tree root, int x) { if (timkiem01(root,x)) { while (root->info!=x) {

Page 125: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 124

cout<<root->info<<" ->"; if (root->info>x) root=root->left; else if (root->info<x) root=root->right; } cout<<root->info; } else cout<<"Khong co duong di tu goc den "<<x; } //In đường đi từ gốc đến nút có mức sâu nhất void duongdidennucsaunhat(tree root) { if (root!=NULL) { duongdidennucsaunhat(root->right); if (muccuanut(root1,root->info)==chieucao(root1)-1) duongditugoc(root1,root->info); duongdidennucsaunhat(root->left); } } //---------------Độ lệch của cây--------------- int dolech(tree root) { return abs(chieucao(root->left) - chieucao(root->right)); } BT4-2.c.

Page 126: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 125

//---------------Dem so nut co dung 2 cay con--------------- int demnut2connt(tree root) { if (root==NULL) return 0; else { if (root->left!=NULL && root->right!=NULL && nguyento(root->info) && nguyento(root->left->info) && nguyento(root->right->info)) return 1+demnut2connt(root->left)+demnut2connt(root->right); return demnut2connt(root->left)+demnut2connt(root->right); } } BT4-2.d. void caynguyento(tree root) { int max=0; if (root!=NULL) { if (demnut(root)-demnutnguyento(root)>max) { max=demnut(root)-demnutnguyento(root); root2=root; } caynguyento(root->right); caynguyento(root->left); } } BT4-2.e. void caycontonglonnhat(tree root) { if (root!=NULL)

Page 127: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 126

{ if (tong(root)>max) { max=tong(root); root3=root; } caycontonglonnhat(root->left); caycontonglonnhat(root->right); } }

Page 128: Cấu Trúc Dữ Liệu SGU -ĐH Saigon

Bài tập cấu trúc dữ liệu và giải thuật–SGU2010 Trang 127

Tài liệu tham khảo [1]Nhập môn cấu trúc dữ liệu và giải thuật

Trần Hạnh Nhi–Dương Anh Đức–ĐHKHTN-ĐHQGTPHCM [2]CTDL, phân tích giải thuật và phát triển phần mềm, NXB Giáo dục, 2007.

Hồ Thuần, Hồ Cẩm Hà, Trần Thiên Thành [3]Cấu trúc dữ liệu và giải thuật

Đỗ Xuân Lôi - ĐHBK – ĐHQG Hà Nội [4]Cấu trúc dữ liệu và giải thuật, ĐHQG Hà Nội, 2007 Đinh Mạnh Tường, [5]Những viên ngọc trong kỹ thuật lập trình.

Jhon Bentley, [6]Bộ đề thi môn Cấu trúc dữ liệu và giải thuật dành cho các lớp đại học

Khoa Công nghệ thông tin, trường Đại học Sài Gòn. [7]Bộ đề thi hoàn chỉnh đại học môn cấu trúc dữ liệu Đại học Khoa học tự nhiên, ĐHQG TPHCM