Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 128 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
128
Dung lượng
611,57 KB
Nội dung
Bài tập cấutrúcdữliệuvà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ÚCDỮLIỆUVÀGIẢITHUẬT
(lưu hành nội bộ)
Năm 2010
Bài tập cấutrúcdữliệuvàgiải thuật–SGU2010 Trang 1
Lời giới thiệu
Cấu trúcdữliệuvàgiảithuậ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ấutrúcdữliệu
và giải thuật, tìm kiếm, sắp xếp, cấutrúc danh sách liên kết vàcấutrú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ấutrúcdữliệuvàgiảithuậ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ấutrúcdữliệuvàgiảithuậ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Ả
Bài tập cấutrúcdữliệuvàgiải thuật–SGU2010 Trang 2
Chương 1
TỔNG QUAN VỀ CẤUTRÚCDỮLIỆUVÀGIẢITHUẬT
A.Tóm tắt lý thuyết
1.Cấu trúcdữliệuvàgiảithuậ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úcdữliệu
Cấu trúcdữ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ấutrúcdữ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ảithuậ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ảithuậ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ảithuật phải dừng sau một số hữu hạn bước.
Tính đúng đắn: Giảithuậ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ảithuậ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ậtvàcấutrúcdữliệu
Giải thuậtvàcấutrúcdữliệu có mối liên hệ chặt chẽ với nhau. Giảithuật
phản ánh các phép xử lý, còn đối tượng xử lý của giảithuậ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
Bài tập cấutrúcdữliệuvà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úcdữ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ảithuật
2.1.Phân tích thời gian thực hiện giảithuật
Với một bài toán chỉ có một giải thuật. Việc chọn lựa giảithuậ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ảithuật kia?
Rõ ràng thời gian thực hiện một giảithuậ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ảithuậ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ảithuật về mặt tốc độ. Nếu như thời gian thực hiện
của một giảithuật là T
1
(n)=Cn
2
và thời gian thực hiện giảithuật khác là T
2
(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ảithuật t
2
ít
hơn so với giảithuật T
1
, như vậy nếu nói thời gian thực hiện giảithuật T(n) tỉ lệ với
với n
2
hay tỉ lệ với n cũng cho ta ý niệm về tốc độ thực hiện giảithuậ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>
Bài tập cấutrúcdữliệuvà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à T
0
(n), thời gian thực hiện <lệnh 1> là
T
1
(n), thời gian thực hiện <lệnh 2> là T
2
(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 T
0
(n) + T
1
(n) và T
0
(n) + T
1
(n).
Trường hợp hay gặp là kiểm tra điều kiện chỉ cần O(1). Khi đó nếu T
1
(n) =
O(f(n)), T
2
(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à T
i
(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à T
0
(n). Như
vậy thời gian chạy của lệnh lặp là:
() ()()
∑
=
+
)(
1
0
nL
i
i
nTnT
Bài tập cấutrúcdữliệuvà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ảithuật
Nếu thời gian thực hiện một giảithuật là T(n)=Cn
2
(với C là hằng số) thì ta
nói: độ phức tạp tính toán của giảithuật này có cấp là n
2
(hay cấp độ lớn của thời
gian thực hiện giảithuật là n
2
) và ta ký hiệu T(n) = O(n
2
) - 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à n
o
sao
cho f(n) ≤ C.g(n) khi n ≥ n
o
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ảithuậ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ảithuậ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ử T
1
(n) và T
2
(n) là thời gian thực hiện của hai đoạn chương trình P
1
và
P
2
mà T
1
(n) = O(f(n)) và T
2
(n)=O(g(n)), thì thời gian thực hiện P
1
rồi P
2
tiếp theo sẽ
là T
1
(n)+ T
2
(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
Bài tập cấutrúcdữliệuvàgiải thuật–SGU2010 Trang 6
Nếu tương ứng với P
1
và P
2
là T
1
(n) và T
2
(n), T
1
(n) = O(f(n)) và
T
2
(n)=O(g(n)), thì thời gian thực hiện P
1
và P
2
lồng nhau là T
1
(n) * T
2
(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(n
2
).
Chú ý rằng có những trường hợp giảithuậ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ảithuậ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ảithuật qua
giá trị xấu nhất của T(n).
2.4.Sự phân lớp các giảithuật
Thông thường các hàm thể hiện độ phức tạp tính toán của giảithuật có dạng
hằng số, log
2
n, n, nlogn, n
2
, n
3
, 2
n
, n!, n
n
,…
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ư 2
n
, n!, n
n
được gọi là hàm mũ. Một giảithuậ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 log
2
n, n, nlogn,
n
2
, n
3
được gọi là các hàm loại đa thức. Giảithuậ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ảithuậtvà 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
Bài tập cấutrúcdữliệuvàgiải thuật–SGU2010 Trang 7
O(logn)
O(n)
O(nlogn)
O(n
2
)
O(n
3
)
O(2
n
)
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ấutrúcdữ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ảithuậ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
Bài tập cấutrúcdữliệuvà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ấutrúcdữ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ấutrúcdữ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;
Bài tập cấutrúcdữliệuvà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);
[...]... nhiên giảithuật này có độ phức tạp là O(n3) Giảithuật 2: Bài tập cấutrúcdữliệuvàgiảithuật SGU2010 Trang 17 Ta có thể cải tiến giảithuật trên để có giảithuậ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