MỐI LIÊN HỆ Theo cách tiếp cận của lập trình cấu trúc, Niklaus Wirth đưa ra công thức thể hiện được mối liên hệ giữa cấu trúc dữ liệu và thuật toán: THUẬT TOÁN + CẤU TRÚC DỮ LIỆU = CHƯƠN
Trang 5Chương I
1 THUẬT TOÁN
1.1 KHÁI NIỆM THUẬT TOÁN
Thuật ngữ thuật toán (Algorithm) xuất phát từ tên một nhà toán
học Ảrập Abu Ja'far Mohamed ibn Musa al'Khwarizmi, thường gọi là
arKhwarizmi ông là tác giả một cuốn sách về số học, trong đó ông đã
dùng phương pháp mô tả rất rõ ràng, mạch lạc cách giải những bài toán
Sau này, phương pháp mô tả cách giải của ông đã được xem là một chuẩn
mực và được nhiều nhà toán học khác tuân theo Thuật ngữ algorithm ra
đời từ đó dựa theo cách phiên âm tên của ông Cùng với thời gian khái niệm thuật toán được hoàn chỉnh dần và khái niệm hình thức chính xác của thuật toán được định nghĩa thông qua mô hình máy Turing Giáo trình này không đi sâu vào những khía cạnh lí thuyết của thuật toán nên chỉ trình bày khái niệm không hình thức của thuật toán;
Thuật toán là một hệ thống các quy tắc chặt chẽ và rỗ ràng nhằm xác định một dãy thao tác trên những đối tượng sao cho sau một số hữu hạn bước thực hiện các thao tác thì đạt được mục tiêu định trước.
1.2 CÁC ĐẶC TRƯNG CỦA THUẬT TOÁN
Một thuật toán thông thường có các đặc trưng cơ bản sau:
Để các quy tắc trên có tứih dừng, sửa lại:
Bước 3 Lặp lại bước 2 khi ỉ < 10
Tính dừng của thuật toán thực sự có ý nghĩa khi thòi gian hoàn thành thuật toán nằm trong giới hạn "chấp nhận được" Với tốc độ máy túủi như hiện nay, nhiều thuật toán được chứng minh là dừng nhưng thời gian hoàn thành là vài trăm năm, chẳng hạn như cácthuật toán về giải mã văn bản mà không biết khoá giải mã
Trang 61.2.2 Tính xác định
Thuật toán yêu cầu ỏ mỗi bước các thao tác phải hết sức rõ ràng, không gây nên sự nhập nhằng, lẫn lộn, tuỳ tiện Khi thực hiện thuật toán, với cùng một dữ liệu vào thì cho cùng một kết quả
1.2.3 Tính ph ổ dụng
Thuật toán phải có thể giải được một lớp các bài toán Mỗi thuật toán có thể làm việc với những dữ liệu khác nhau trong một miền xác định Việc cung cấp dữ liệu vào là một cách thức để thuật toán có thể giải được bài toán tổng quát thay vì giải các bài toán với
dữ liệu cụ thể
1.2.4 Dữ liệu vào
Mỗi thuật toán thường có những đại lượng vào gọi là dữ liệu vào để cung cấp dữ
liệu cho thuật toán Tuy nhiên, cũng có những thuật toán không có dữ liệu vào
1.2.5 Dữ liệu ra
Sau khi kết thúc thuật toán, tuỳ vào*chức năng của thuật toán mà thu được một số
đại lượng xác định gọi là đại lượng ra hay dữ liệu ra.
1.2.6 Tính hiệu quả
Vói dữ liệu vào, sau một số hữu hạn bước thực hiện thuật toán sẽ dừng và cho đúng kết quả mong muốn với thời gian chấp nhận được Cho đến nay, mặc dù máy tính có tốc độ túih toán rất nhanh nhxmg vẫn còn nhiều bài toán mà thời gian tính toán rất lớn Do đó con người vẫn luôn phải tìm cách để cải thiện hiệu quả thuật toán và tăng tốc độ tính toán của
hệ thống máy tính
1.3 TIÊU CHUẨN ĐÁNH GIÁ THUẬT TOÁN
Một bài toán có thể có nhiều thuật toán giải, mỗi thuật toán có những ưu, nhược điểm riêng Để quyết định chọn thuật toán nào thông thường dựa vào hai tiêu chuẩn
cơ bản sau:
Tiêu chuẩn 1: Thuật toán đơn giản, dễ hiểu, dễ cài đặt.
Tiêu chuẩn 2: Thuật toán sử dụng tiết kiệm các tài nguyên của hệ thống máy tính
như bộ nhớ, thời gian chiếm dụng CPU và đặc biệt là thời gian chạy
Trong trường hợp chương trình ít sử dụng và giá viết chương trình vượt xa giá chạy chương trình thì tiêu chuẩn 1 được ưu tiên Với những chưoỉng trình thưòng dùng như trong các thư viện chương trình, các chương trình hệ thống thì tiêu chuẩn 2 được ưu tiên, ở đây ta không đi sâu vào tiêu chuẩn 1
Trong tiêu chuẩn 2, tính hiệu quả của thuật toán bao gồm hai yếu tố:
- Dung lượng không gian nhớ cần thiết để lưu dữ liệu và các kết quả trung gian để giải bài toán (tổ chức dữ liệu cho bài toán)
- Thời gian cần thiết để thực hiện thuật toán (thời gian chạy)
Trang 7Hai yếu tố trên thường mâu thuẫn và ảnh hưởng qua lại lẫn nhau Thường khi chọn thuật toán ta quan tâm đến thời gian thực hiện Mặc dù hiện nay tốc độ máy tmh ngày được cải thiện đáng kể, có thể thực hiện hàng trăm triệu phép tmh trên một giây nhưng vẫn chưa đáp ứng được cho một số thuật toán có thời gian chạy rất lớn.
1.4 Đ ộ PHỨC TẠP CỦA THUẬT TOÁN
Việc đánh giá thời gian thực hiện của thuật toán phụ thuộc vào nhiều yếu tố:
- Dữ liệu vào
- Tốc độ của máy tính
- Chương trình dịch và hệ điều hành dùng để thực hiện chương trình
Do đó việc đo, đếm chính xác thời gian thực hiện thuật toán là bao nhiêu đơn vị thời gian gần như không thể thực hiện được Để có thể so sánh thời gian chạy của các thuật toán, trên phương diện lí thuyết, thời gian thực hiện thuật toán được đánh giá là một hàm
phụ thuộc vào kích thước của dữ liệu vào gọi là độ phức tạp thuật toán.
Để đánh giá độ phức tạp của thuật toán thông thường người ta tính số phép toán cơ bản mà thuật toán thực hiện Một phép toán được gọi là cơ bản nếu thời gian thực hiện thuật toán tỉ lệ với số phép toán đó Tuỳ từng thuật toán cụ thể, dễ dàng xác định được phép toán nào là cơ bản Các phép toán cơ bản thường dùng có thể là các phép toán: +, *, /, cácphép so sánh, phép gán, thao tác đọc, ghi tệp,
Tuỳ thuộc vào thuật toán, độ phức tạp là một hàm phụ thuộc vào kích thước của dữ
liệu vào Thông thường ta dùng một số nguyên n để đặc trưng cho kích thước của dữ liệu
vào Kí hiệu;
D„ là tập các dữ liệu vào có kích thước n.
Với mỗi bộ dữ liệu d e D„, kí hiệu costẶd) là số phép toán cơ bản mà thuật toán A phải thực hiện với bộ dữ liệu vào d (trong trường hợp không gây nhầm lẫn ta có thể kí hiệu
cost{d) thay cho costẬd)).
Độ phức tạp trong trường hợp tốt nhất: được đánh giá trong trường hợp bộ dữ liệu
vào mà thuật toán thực hiện ít phép toán cơ bản nhất, kí hiệu Min^(n)
Min^(n)= MỊn{cost^(ú?)},
¡JeD„
Độ phức tạp trong trường hợp xấu nhất: được đánh giá trong trường hợp bộ dữ liệu
vào mà thuật toán thực hiện nhiều phép toán cơ bản nhất, kí hiệu Max^(n)
Max^(«)= Max{cost^(í/)}
Trong giáo trình này, khi nói đến độ phức tạp, ngầm định được đánh giá trong trường hợp xấu nhất Khi đó kí hiệu T(n) là độ phức tạp của thuật toán với dữ liệu vào có
kích thước n.
Trang 8Ví dụ 1 Thuật toán tìm số nhỏ nhất trong một mảng các số nguyên a{ì n]\
+ Phép toán cơ bản của thuật toán là phép so sánh trong biểu thức Min > a[ĩ\.
+ Số phép so sánh trong trưòfng hợp tốt nhất và xấu nhất đều là n - 1
+ Vậy độ phức tạp thuật toán tìm số nhỏ nhất trong mảng có n phần tử là T(«) = n - \
Trong trưcmg hợp thuật toán thực hiện nhiều phép toán cơ bản, ta có thể đánh giá độ phức tạp trên từng loại phép toán hoặc tổng hợp của các phép toán bằng cách gán trọng số cho từng phép toán
Ví dụ 2 Thuật toán nhân hai ma trận a„,x„ và b„^p Kết quả lưu vào ma trận c„,xpi
Đánh giá độ phức tạp của thuật toán:
+ Đặc trưng kích thước dữ liệu vào: giả sử n là số lớn nhất trong ba số m, n, p Khi
đó ta lấy n là đại lượng đặc trưng cho dữ liệu vào.
+ Phép toán cơ bản: thuật toán sử dụng hai phép toán cơ bản là phép cộn^ (+) và phép nhân (*) Dễ tíiấy độ phức tạp được tính trong trường hợp tốt nhất và xấu nhất là như nhau, cụ thể:
+ Số phép cộng: Tl(n) = m.(n - 1 ).p\
+ Số phép nhân: T2{n) = m.n.p-,
+ Không mất túih tổng quát ta có thể giả thiết m = n= p v ằ xem phép nhân là phép
toán cơ bản, ta có thể đánh giá độ phức tạp của thuật toán nhân hai ma trận vuông có kích
thước nxn là: T{n) = n^.
Ví dụ 3 Thuật toán tìm một phần tử X trong danh sách L e ó n phần tử bằng cách tìm
tuần tư
Trang 9+ Do phiic tap duac danh gia qua s6' l^n thiic hien phep so sanh L[/]=jf.
+ Truofng.hop tot nhat: so Ian so sanh la 1, trong tnicmg hop ph^n tu can tim x la
ph^n tu dau tien cua danh sach Do do Min(n) =1
+ TnJotig hop xau nhat la ph^n tir c^n tim khong c6 trong danh sach Khi do
Do phlic tap cua thuat toan diioc danh gia trdn hai phep toan cd ban la phep so s^nh
trong bieu thurc di^u kien cua lenh //v a phep gan, ki hieu tuong ling la C{n) va M(n) Do phlic tap duoc danh gia trong trucmg hop "xa'u" nhat cua dii lieu vao la day so b tinh trang
thii tu giam Khi do ta tinh dugc:
Trang 101.5 ĐỘ PHỨC TẠP TRUNG BÌNH
Việc đánh giá độ phức tạp của thuật toán trong trường hợp xấu nhất hay tốt nhất trong nhiều trường hợp không thể hiện được tính "phức tạp" của thuật toán vì căn cứ vào những trường hợp dữ liệu vào có tính đặc thù
Độ phức tạp trung bình: gọi piđ) là xác suất để bộ dữ liệu d e D„ được chọn Độ
phức tạp trung bình của thuật toán A trong tnicmg hợp kích thước dữ liệu vào là n, kí hiệu
i) Min^(n) < Avg^(n) < Max^(rt) (điều này luôn đúng)
ii) Phân bố của dữ liêu vào là phân bố đều, khi đó p{d) = Do đó
D
A v g » = ^ co st^ (tí?)
deD„
iii) Giả sử D„ được phân hoạch thành D „ J u D„2 u u D„„„ trong đó D „J là tập
các bộ dữ liệu mà độ phức tạp của thuật toán A trên các bộ dữ liệu này đều bằng nhau và được kí hiệu là cost^(D„y) Gọi p(D„j) là xác suất để một bộ dữ liệu của D„ thuộc vào D„J Khi đó ta có:
Avg^(n)= X P(D„j) cosị^{D„j).
Ví dụ Tính độ phức tạp trung bình của thuật toán tìm kiếm tuần tự
Gọi:
q là xác suất để X có mặt trong danh sách Sự xuất hiện của X tại vị trí bất kì
của danh sách là như nhau
1 - ợ là xác suất để X không có mặt trong danh sách.
Phân hoạch D„ thành các tập:
Z)„o là tập các bộ dữ liệu thuộc D„ mà không chứa phần tử cần tìm X.
là tập các bộ dữ liệu thuộc D„ mà phần tử đầu tiên bằng JC là phần tử thứ nhất.
D„J là tập các bộ dữ liệu thuộc D„ mà phần tử đầu tiên bằng X là phần tử thứ j.
D„„ là tập các bộ dữ liệu thuộc D„ mà phần tử đầu tiên bằng X là phần tử thứ n.
Khi đó:
Trang 11Cho /(n), g{n) là hai hàm xác định trên tập số nguyên không âm (tập số tự nhiên N)
có miền giá trị là R”", tập các số thực không âm, có nghĩa/, N w Ta nói f{n) là 0-lớn
của g{n), kí hiệu /(n) = 0(g(n)), nếu và chỉ nếu tồn tại các hằng số dương c và n,) sao cho
với mọi n > «0 ta có f(n) < c.g(n).
Ví dụ Nếu f(n) = 3/í' + 2« - 10 thì/(«) = 0(n').
Kí hiệu O-lớn cho ta phân lớp các hàm theo độ tăng của nó Một số lớp thường dùng trong đánh giá độ phức tạp thuật toán qua kí hiệu 0-lớn
Trang 12K í hiệu 0-1 ớn Tên g ọ i thường dùng
0(1)O(logn)0(n)O(nlogn)
0 {rĩ)
0(2")
hằng lôgarit tuyến tính nlognbình phương mũ
Độ tăng của các hàm thể hiện qua đồ thị sau:
n
Một số quy tắc tính độ phức tạp qua kí hiệu O-lớn:
a) Quy tắc tổng
Nếu thuật toán gồm hai phần thực hiện tuần tự, phần thứ nhất có độ phức tạp được
đánh giá là T,(n) = 0(/ì(aỉ)) và phần thứ hai được đánh giá là TịÍ«) = oự^in)) thì độ phức
tạp của toàn bộ thuật toán là T(«) = Ti(«) + T2(rt)= 0(max(/',(/ỉ),/2(rt))
b) Quy tắc nhân
Nếu một thuật toán gồm f(n) lần thực hiện công việc c, trong đó công việc c thực
hiện g(n) lần phép toán cơ bản, khi đó độ phức tạp của thuật toán là:
T(n) =fin)xg(n).
Trường hợp/(n) = 0(/i|(«)) và g(n) = 0(/í2(«)), ta có:
T(n) = ự.g)(n) = 0(/i,(rt)./ỉ2(/ĩ))
12
Trang 132 KIỂU D ữ LIỆU VÀ CẤU TRÚC DỮ LIỆU
2.1 KIỂU DỮ LIỆU
Mọi dữ liệu lưu trữ trong máy tính đều được biểu diễn dưới dạng mã nhị phân Việc
sử dụng trực tiếp các số nhị phân trong máy tính là một công việc khó khăn cho người lập trình Chính vì lí do này mà các ngôn ngữ lập trình bậc cao đã xây dựng các kiểu dữ liệu Một kiểu dữ liệu là sự trừu tượng hoá các thuộc tính bản chất của các đối tượng trong thực
tế và phù hợp với cách tổ chức thông tin trên máy tính, chẳng hạn như các kiểu số nguyên,
số thực, logic,
Một kiểu dữ liệu T là một bộ T = <v, 0>, trong đó V là tập các giá trị hợp lệ của
kiểu T và ơ là tập các phép toán trên kiểu T
Ví dụ Kiểu dữ liệu Byte = <ì'^Byic>
với = {0, 1, 255}, ỠBy„ = {+, *, div, mod, >, >=, <, <=, =, <>}
2.2 CẤU TRÚC DỮ LIỆU
Các kiểu dữ liệu cơ sở không đủ để mô tả các đối tượng của thế giới thực nên các ngôn ngữ lập trình bậc cao cho phép kết hợp các kiểu dữ liệu cơ sở để tạo nên kiểu dữ liệu mới phức tạp hơn được gọi là kiểu dữ liệu có cấu trúc Các kiểu dữ liệu có cấu trúc thưòng dùng trên các ngôn ngữ lập trình bậc cao như; Aưay, String, Record, File, là các cấu trúc
dữ liệu thường dùng
2.3 MÔ HÌNH DỮ LIỆU
Các bài toán thực tế cần phải giải trên máy tính ngày càng phức tạp và đa dạng, do
đó trước khi tổ chức các cấu trúc dữ liệu mô tả bài toán, người lập trình thường phải dùng các mô hình toán học để biểu diễn các đối tượng của bài toán và mối liên hộ giữa các đối tượng Việc sử dụng các mô hình toán học cho phép người lập trình mô tả chính xác bản chất của các đối tượng trong bài toán và việc sử dụng toán học như một công cụ giúp việc giải các bài toán dễ dàng, chính xác hơn trước khi giải bài toán trên máy tính bằng chương trình Mô hình toán học có thể biểu diễn được trên máy tính gọi là mô hình dữ liệu Mô hình dữ liệu muốn cài đặt được trên máy tính phải có một cách tổ chức dữ liệu phù hợp Các mô hình dữ liệu thường được sử dụng trong các bài toán tin học là danh sách, cây, đồ thị, bảng băm, quan hệ,
2.4 CÁC TIÊU CHUẨN CỦA CẤU TRÚC DỮ LIỆU
Khi tổ chức dữ liệu cho một bài toán thường dựa vào các tiêu chuẩn sau để lựa chọn cách tổ chức dữ liệu tối ưu
Phản ánh đúng thực tế: Đây là tiêu chuẩn quan trọng nhất, quyết định túih đúng
đắn của toàn bộ quá trình giải bài tõán Trong khi tổ chức dữ liệu cũng dự tính các trạng thái biến đổi của dữ liệu trong tương lai để đảm bảo cho chương trình hoạt động được lâu dài
Trang 14Các thao tác phù hợp: Mỗi cấu trúc dữ liệu có thể biểu diễn được một tập các đối
tượng và có một tập các phép toán phù hợp Việc chọn cách tổ chức dữ liệu không chỉ biểu diễn được các đối tượng của bài toán mà còn phải phù hợp với các thao tác trên đối tượng
đó, có như vậy ta mới xây dựng được thuật toán giải bài toán đơn giản hơn
Tiết kiệm tài nguyên hệ thống: Khi tổ chức dữ liệu chỉ nên sử dụng tài nguyên hộ
thống vừa đủ đáp ứng được yêu cầu công việc, tránh lãng phí Có hai loại tài nguyên quan trọng của hộ thống là bộ nhớ và thời gian chiếm dụng CPU để thực hiện các thao tác trên
dữ liệu Thông thưòng hai loại tài nguyên này thường mâu thuẫn nhau trong khi giải các bài toán Tuy nhiên, nếu tổ chức khéo léo chúng ta cũng có thể tiết kiệm được cả hai loại tài nguyên
3 MỐI LIÊN HỆ GIỮA CÂU TRÚC DỮ LIỆU VÀ THUẬT TOÁN
Trong khi giải một bài toán, thông thưcíng ta chỉ chú trọng đến thuật toán (hay cách giải của bài toán) mà ít khi quan tâm đến việc tổ chức dữ liệu Tuy nhiên giữa việc tổ chức
dữ liệu và thuật toán có mối liên hệ chặt chẽ với nhau
3.1 MỐI LIÊN HỆ
Theo cách tiếp cận của lập trình cấu trúc, Niklaus Wirth đưa ra công thức thể hiện được mối liên hệ giữa cấu trúc dữ liệu và thuật toán:
THUẬT TOÁN + CẤU TRÚC DỮ LIỆU = CHƯƠNG TRÌNH
(Algorithms + Data Structures = Programs)Một thuật toán giải' bài toán bao giờ cũng thao tác trên một cách tổ chức dữ liệu (cấu trúc dữ liệu) cụ thể và các thao tác phải được cấu trúc dữ liệu đó hỗ trợ
Khi tổ chức dữ liệu cho bài toán thay đổi thì thuật toán cũng phải thay đổi theo cho phù hợp với cách tổ chức dữ liệu mới Ngược lại, trong quá trình xây dựng, hoàn thiện Ihuật toán cũng gợi mở cho người lập trình cách tổ chức dữ liệu phù hợp với thuật toán và tiết kiệm tài nguyên hệ thống Chẳng hạn, dùng thêm các ô nhớ phụ để lưu các kết quả trung gian và giảm thời gian tính toán
Quá trình giải một bài toán trên máy tính là một quá trình hoàn thiện dần cách tổ chức dữ liệu và thuật toán để tiết kiệm tài nguyên của hệ thống
3.2 MỘT SỐ Ví DỤ MINH HOẠ
Ví dụ 1 Xét bài toán đổi giá trị hai biến X, >»
Với bài toán này ta có hai phương án giải như sau:
Phương án 1 Dùng ô nhớ trung gian
t g : = x ;
x : = y ;
y := tg;
14
Trang 15Phương án 2 Không dùng biến trung gian.
Ví dụ 2 Xét bài toán tmh số tổ hợp chập k của n phần tử C *.
Phương án 1 Dùng đinh nghĩa C* = -— Giả sử gt(n) là hàm trả về n! Ta
Nhận xét: Với thuật toán này các chương trình chỉ tính được với các số n, k nhỏ
vì phải lưu các kết quả trung gian rất lớn là n
-Phương án 2 Dùng công thức C* = c*r,' + c*_|, với c “ =l, c ,'= l Hàm tính C*
được viết dưới dạng đệ quy như sau:
Nhận xét: Với thuật toán này khắc phục việc phải lưu các giá trị trung gian lớn
nhưng hạn chế của thuật toán là phải tính lại nhiều lần các giá trị đã túih ở bước trước, chẳng hạn để tính cị chương trình phải lặp lại hai lần tính c l, ba lần tmh c\
Phương án 3 Dùng một mảng hai chiều C[0 n,0 ^] mỗi phần tử có kiểu Longlnt
để lưu các kết quả trung gian trong khi tính C *, với C[i, J]= c i (0 < j < i, j < k, i < n) Từ
công thức C* = c*r,' + c*_,, ta có cụ, J]= C [i-ỉ,j -1]+C[/ -1 , /Ị Bảng dưới đây minh hoạ
mảng c duiỉg để tính Cj.
Trang 16Nhận xét: Phương án này còn nhiểu ô nhớ của mảng không dùng, cụ thể là các ô
nằm ở phần trên đường chéo chính Vì mục đích của bài toán là tính giá trị mà khôngcần các giá trị trung gian nên ta có thể tổ chức bộ nhớ tiết kiệm hơn bằng cách dùng một mảng một chiều
Phương án 4 Dùng mảng một chiều H[ồ k] để lưu các giá trị của từng dòng trong
mảng c của phưoỉng án trên Mảng H được tính qua n bước, tại bước thứ ỉ, H là dòng thứ i
của mảng c, cụ thể tại bước thứ i, //[/]= c/ , với j < i.
Trang 17f o r i : =k d o w n t o 1 d o
H [ i ] : = H [ i ] + H [ i - l ] ;
H e s o : = H [ k ] ;
E n d ;
Nhận xét: Với phương án này vừa tiết kiệm được bộ nhớ vừa tăng khả năng tính
toán với n, k lớn hofn các phương án khác.
Khi phát triển một chương trình ta thưcmg tiến hành qua các bước sau:
toán cần giải và nếu có thể hãy diễn đạt lại bài toán bằng các kí hiệu toán học hoặc các công thức
Hình thành ý tưởng về cách giải bài toán: Trước khi giải một bài toán bằng máy
tữih, chúng ta phải phác thảo cách giải dưới dạng ý tưởng Ý tưcmg này được hình thành dựa trên những kiến thức về giải các bài toán tương tự hoặc các phương pháp thiết kế thuật toán đã biết Ý tưởng về cách giải này sẽ được cụ thể dần ở các bước sau và cũng có thể không thực thi được sau khi đã cụ thể hoá Khi đó phải hình thành ý tưởng khác (cách giải khác)
Cụ thể ý tưởng bằng mô tả thuật toán dưới dạng "thô": Ý tưcmg của thuật toán
được mô tả bằng một thuật toán dạng thô hay còn gọi là thuật toán mô tả ở mức 0 Việc mô tả này thưòfng dùng ngôn ngữ tự nhiên vì còn nhiều điều trong thuật toán chưa tường minh Việc mô tả ở bước này là cần thiết để tránh việc người lập trình quá quan tâm đến chi tiết các thao tác làm phân tán tư tưởng cũng như định hướng ban đầu Trong bước này, dùng chiến thuật "chia để trị", các chức năng của thuật toán được mô tả hình thức dưới dạng các thao tác chưa tường minh
Tỉnh chế dẩn từng bước thuật toán: Thuật toán mô tả dạng thô ở bước trên được
tinh chế dần từng bước cho đến khi có thể lập trình được Các mô tả chưa tường minh trong thuật toán sẽ được chi tiết dần trong bước này Việc chi tiết được thực hiện từng bước, qua mỗi bước thuật toán sẽ được cụ thể dần bằng các thao tác, gần với ngôn ngữ lập trình Quá trình này được lặp lại nhiều lần và chỉ dừng lại khi tất cả các bước của thuật toán đã được tường minh với việc cài đặt là
dễ dàng
Cài đặt chương trình cho thuật toán' Đây là bước cụ thể hoá thuật toán đã được
tường minh ở bước trên bằng chương trình Bước này sẽ dễ dàng nếu ở bước trước ta mô tả thuật toán bằng giả mã hoặc bằng một ngôn ngữ lập trình cụ thể
Tinh chế còn được gọi là làm mịn.
Trang 18ở bước này, công việc chủ yếu là tổ chức chương trình thành các đơn thể (đối với lập trình cấu trúc) hoặc các lớp đối tượng (đối với lập trình hướng đối tượng).
Xét một ví dụ về sắp xếp dãy số a[\ n\ theo thứ tự không giảm.
a[ỉ]< a[2]< .< a [ n ị
Ý tưởng giải bài toán: Tìm phần tử nhỏ nhất trong từng dãy con và đưa lên đầu
dãy
Mô tả thuật toán:
Với mỗi dãy con a[i n] (ỉ = 1, 2, , n - 1 ) thực hiện:
o Tìm vị trí k ự < k < n) sao cho a[k] là giá trị nhỏ nhất trong 4ãy con
o So sánh giá trị nhỏ nhất tạm thời với các số a [/+ l «] nếu có aự\ nhỏ hơn
giá trị nhỏ nhất tạm thời thì đổi vị trí giá trị nhỏ nhất tạm thời là j.
Tinh chế bước 3: tiếp tục chi tiết chức năng tìm vị trí nhỏ nhất của dãy a[ỉ n] ■
Trang 19Bài 1 Cho n điểm trong không gian hai chiềụ Cần tìm hình chữ nhật có diện tích nhỏ nhất
và có các cạnh song song với các trục toạ độ chứa n điểm trên Hãy tổ chức dữ liệu,
trình bày thuật toán và lập trình giải bài toán trên
Bài 2 Cho một dãy số ũ ị , ỚỊ, , a„ Hãy trình bày hai thuật toán c h u y ể n p h ầ n tử đầu tiên
ra cuốị Nghĩa là sau khi chuyển ta được dãy a„, ũị, ậ Yêu cầu về tổchức dữ liệu là không được dùng mảng trung gian mà chỉ dùng một ô nhớ trung gian Đánh giá độ phức tạp của thuật toán Có thể cải tiến để có thuật toán tốt hơn về độ phức tạp không?
Bài 3 Một danh sách học sinh gồm họ tên và điểnỊ trung bình Hãy tổ chức dữ liệu và
trình bày thuật toán xếp thứ hạng học sinh, uầnh giá độ phức tạp của thuật toán Cài đặt bằng chương trình cụ thể
Bài 4 Cho một dãy số nguyên, hãy trình bày thuật toán Uột kê các phần tử khác nhau của
dãy số trên Độ phức tạp của thuật toán? Cài đặt bằng chương trình? Có thể cải tiến thuật toán để đơn giản hơn không?
Bài 5 Cần chia hết m phần thưởng cho n học sinh sắp theo thứ tự từ giỏi trở xuống sao cho
mỗi học sinh không nhận ít phần thưởng hơn bạn xếp sau mình Hãy đề xuất các cách tổ chức dữ liệu và trình bày thuật toán tính số cách chia thưởng, với mỗi cách, phân tích những ưu, nhược điểm Viết các thủ tục tương ứng cho từng cách tổ chức
dữ liêụ
Trang 20Chương II
1 DANH SÁCH, NGĂN XẾP, HÀNG ĐỢI
1.1 KHÁI NIỆM DANH SÁCH VÀ CÁC THAO TÁC
1.1.1 Định nghĩa danh sách
Danh sách là một dãy hữu hạn các phần tử cùng loại được xếp theo một thứ tự tuyến tính.
Danh sách L gồm các phần tử ứị, (32, •••, a„ được kí hiệu: L = (ữ|, <32 ••• <3„)
trong đó, n được gọi là chiều dài của danh sách,
a, gọi là phần tử thứ i của danh sách,
a, được gọi là phần tử đầu tiên của danh sách,
a„ gọi là phần tử cuối cùng của danh sách.
Nếu rt = 0 thì L được gọi là danh sách rỗng
Một tính chất quan trọng của danh sách là các phần tử được sắp xếp tuyến tính theo
vị trí của chúng trong danh sách Với n > ì, i =1, 2, , n -1 , phần tử a, là phần tử ngay
Trong một danh sách các phần tử có thể giống nhau
1.1.2 Các thao tác trên danh sách
Tuỳ thuộc từng loại danh sách sẽ có các thao tác đặc trưng riêng Trên danh sách ứiường thực hiện các thao tác cơ bản sau:
20
Trang 21Khởi tạo danh sách: tạo một danh sách rỗng
Thêm một phần tử vào danh sách
Loại bỏ một phần tử khỏi danh sách
Sắp thứ tự danh sách theo một khoá nào đó
Tim kiếm một phần tử trong danh sách
Ghép nhiều danh sách thành một danh sách
Tách một danh sách thành nhiều danh sách
Sao chép một danh sách
1.2 BIỂU DIỄN DANH SÁCH BẰNG MẢNG
Mảng là một cấu trúc dữ liệu cơ bản, thường dùng và được các ngôn ngữ lập trình bậc cao hỗ trợ Mảng là một dãy cố định các ô nhớ chứa các phần tử cùng kiểu Mỗi ô nhớ của mảng được xác định bởi chỉ số Mô hình danh sách có những tmh chất gần giống với cấu trúc dữ liệu kiểu mảng nên ta có thể dùng mảng để biểu diễn mô hình danh sách, trong
đó các phần tử của danh sách được lưu vào các ô nhớ liên tục của mảng
Điểm khác biệt giữa cấu trúc mảng và mô hình danh sách là số phần tử của mảngxố định trong khi số phần tử của danh sách thay đổi theo các thao tác thêm và xoá
1.2.1 Tổ chức dữ liệu
Giả sử có danh sách L = (a,, a„) trong đó mỗi phần tử có kiểu Elemenữype.
Khi đó tổ chức dữ liệu kiểu mảng để lưu danh sách L gồm hai thành phần:
+ Thành phần element là một mảng lưu các phần tử của danh sách.
+ Thành phần count là vị trí của ô nhớ lưu phần tử cuối cùng của danh sách và cũng
là số phần tử hiện tại của danh sách
Để đơn giản ta quy định các phần tử của mảng có chỉ số từ 1 đến maxlength, các phần tử của danh sách lưu vào mảng từ vị trí đầu tiên đến vị trí count Khi đó các vị trí của mảng từ vị trí count + 1 đến maxlength chưa sử dụng, những phần tử này sẽ được sử dụng
khi thưc hiên các thao tác thêm vào danh sách
Trang 22e l e m e n t : A r r a y [ 1 M a x L e n g t h ] O f DIENTHOAI;
C o u n t : 0 M a x L e n g t h ;
E n d ;
V a r d b : DANHBA;
1.2.2 Các thao tác trên danh sách
Khỏi tạo danh sách
Vì số phần tử của danh sách được lưu vào thành phần count nên để khởi tạo danh sích rỗng ta chỉ cần thực hiện phép gán count := 0.
Trang 23Kiểm tra danh sách đầy
Khi biểu diễn danh sách bằng mảng sẽ phải khống chế số lượng tối đa các phần tử của danh sách Do đó, có thể đến một lúc nào đó không đủ ô nhớ để thêm các phần tử vào danh sách Trường hợp này gọi là danh sách đầy Như vậy, danh sách đầy khi số phần tử của danh sách bằng kích thước của mảng
Thêm một phần tử vào danh sách
Cho danh sách L, cần thêm vào trước phần tử thứ k trong danh sách một phần tử X
+ Di chuyển các phần tử từ vị trí thứ k đến cuối danh sách ra sau một vị trí
+ Đưa phần tử cần thêm X vào vị trí k
+ Tăng thành phần count lên 1
Thủ tục thêm phần tử X vào danh sách L tại vị trí k:
Trang 24Loại bỏ một phần tử khỏi danh sách
Giả sử cần xoá phần tử thứ k trong danh sách L.
Thuật toán
+ Dồn các phần tửtừ yị trí k+1 đến cuối danh sách về trước một vị trí
+ Giảm số phần tử của danh sách đi 1
Với danh sách có n phần tử, dễ thấy độ phức tạp thuật toán thêm một phần tử và
thuật toán xoá một phần tử có độ phức tạp là 0(«)
Ghép hai danh sách
Cho hai danh sách L| và L 2 Ghép hai danh sách này thành danh sách L.
Thủ tục ghép hai danh sách Lj, Lj theo thứ tự vào danh sách L:
Trang 25L e l e m e n t [ i + L l c o u n t ] : = L 2 e l e m e n t [ i ] ;
L c o u n t : = L l c o u n t + L 2 c o u n t ;
End;
Sắp xếp danh sách
Cho danh sách L, sắp xếp danh sách theo thứ tự tăng của trường khoá Key (Key là
một trường trong kiểu dữ liệu phần tử của danh sách)
Có nhiều thuật toán sắp xếp danh sách tổ chức bằng mảng Trong phần này trình bày thuật toán sắp xếp bằng chọn trực tiếp
Thuật toán
Duyệt lần lượt từng phần tử của danh sách, với phần tử thứ i thực hiện:
+ Tìm phần tử ở vị trí k có khoá nhỏ nhất trong danh sách con L[i count]
+ Đổi giá trị phẩn tử thứ k với phần tử thứ i
Tim kiếm trong danh sách
Cho danh sách L, tìm trong danh sách phần tử có khoậ của trường Key là j: Nếu tìm
thấy thì cho biết vị trí của phần tử đầu tiên tìm được
Thuật toán tỉm kiếm tuần tự
Duyệt lần lượt từng phần tử của danh sách, với mỗi phần tử thực hiện;
+ Nếu phần tử thứ i có khoá trùng với X thì phần tử thứ i là phần tử cần tìm, dừng thuật toán và kết quả tìm thấy
+ Nếu đã duyệt hết danh sách thì kết luận không tìm thẩy
Trang 26Thủ tục Locate tìm trong danh sách L một phần tử có khoá là X Nếu tìm thấy, kết
quả được trả về qua tham biến found kiểu boolean và vị trí của phần tử tìm được đầu tiên qua tham biến id.
Đô phức tạp thuật toán tìm kiếm tuần tự là 0(«) với n là số phần tử của danh sách
Để giảm độ phức tạp thuật toán tìm kiếm ta có thể dùng thuật toán tìm kiếm nhị phân Yêu cầu để thực hiện được thuật toán tìm kiếm nhị phân là danh sách phải được sắp trên trường khoá cần tìm
Giả sử danh sách L đã sắp theo thứ tự tăng của trường Key.
Thuật toán tìm kiếm nhị phân
Lặp lại trong khi danh sách còn ít nhất một phần tử, mỗi lần lặp thực hiện:
- So sánh khoá cần tìm với khoá của phần tử ỏ vị trí giữa của danh sách:
+ Nếu khoá của phần tử giữa lớn hơn khoá cẩn tìm thì tìm trong nửa đầu của danh sách
+ Nếu khoá của phần tử giữa nhỏ hơn khoá cần tìm thì tìm trong nửa sau của danh sách
+ Nếu khoá của phần tử giữa bằng khoá cẩn tìm thì thuật toán kết thúc và kết quả là tìm thấy
Nếu danh sách không có phần tử nào thì kết quả là không tìm thấy
Trang 27Nhận xét về cách biểu diễn danh sách bằng mảng
Với cách tổ chức dữ liệu cho mô hình danh sách bằng mảng như trên ta có một số nhận xét về ưu, nhược điểm của cách tổ chức dữ liệu này như sau;
Tổ chức danh sách bằng mảng thuận lợi khi truy xuất trực tiếp các phần tử của danh sách theo vị trí Điều này thuận lợi cho một số thuật toán như tìm kiếm nhị phân
Khi dùng mảng phải cố định kích thước, trong khi đó các thao tác trên danh sách luôn làm cho số phần tử của danh sách thay đổi Điều này dẫn đến hai xu hướng, nếu khai báo mảng với kích thước lớn thì gây lãng phí vì nhiều ô nhớ không sử dụng hoặc khai báo kích thước nhỏ để ít lãng phí thì danh sách sẽ nhanh chóng đầy khi thêm
Các thao tác thêm, xoá, cài đặt trên danh sách tổ chức bằng mảng có độ phức tạp là 0(«) nên không phù hợp với các danh sách thường xuyên sử đụng các thao tác này
1.3 DANH SÁCH LIÊN KẾT ĐƠN'
Một trong những đặc trưng của danh sách là số phần tử không cố định mà thay đổi tuỳ thuộc vào thao tác trên nó Điều này buộc cách tổ chức dữ liệu biểu diễn cho mô hình danh sách cũng phải có đặc trưng này, nghĩa là bộ nhớ phải được cấp phát động (dùng mảng không đáp ứng được yêu cầu này) Mặt khác, trong danh sách các phần tử được sắp xếp tuyến tính do đó việc tổ chức dữ liệu cấp phát động phải được tổ chức sao cho thể hiện được thứ tự tuyến tính của các phần tử trong danh sách, một trong những cách thường dùng
là danh sách liên kết đơn Trong danh sách liên kết đơn, mỗi phần tử phải quản lí địa chỉ ô nhớ lưu phần tử ngay sau nó Để thuận lợi cho các thao tác với bộ nhớ cấp phát động, phần sau nhắc lại một số thao tác với bộ nhớ cấp phát động thông qua biến con trỏ của Pascal
Liên kết đơn còn được gọi là mốc nối đơn
Trang 281.3.1 Cấp phát động, biến con trỏ và các thao tác
Ô nhớ cấp phát động là những ô nhớ được cấp phát và thu hồi bằng lệnh trong chương trình Để quản lí các ô nhớ cấp phát động cần sử dụng biến kiểu con trỏ Biến con trỏ chứa địa chỉ của ô nhớ động được cấp phát Mỗi biến con trỏ chỉ có thể quản lí một ô nhớ cấp phát động có kiểu xác định
Khai báo kiểu và biến con trỏ:
Sử dụng ô nhớ do biến con trỏ quản lí:
Mặc dù biến con trỏ chỉ quản lí địa chỉ của ô nhớ cấp phát động, tuy nhiên trong trường hợp cần thao tác với nội dung của ô nhớ ta có thể dùng cú pháp:
Hằng con trỏ NU dùng để gán cho con trỏ có kiểu bất kì, thường dùng khi chưa xác
định địa chỉ mà biến con trỏ quản lí
Thu hồi ô nhớ của một biến con trỏ
Khi cần thu hồi ô nhớ do bỉến con trỏ quản lí ta dùng thủ tục Dispose.
D i s p o s e ( b i ế n _ c o n _ t r ỏ ) ;
Khi đó vùng nhớ mà biến con trỏ p quản lí đã được thu hồi và có thể dùng cho việc khác Sau khi thu hồi ô nhớ của một biến con trỏ thì nên gán cho nó giá trị NU để tránh
những sai sót khi thao tác với biến con trỏ này
Ta cũng có thể thu hồi một số ô nhớ cấp phát bằng cách dùng cặp thủ tục Markip)
và Releaseip) Thủ tục Markịp) (với p là một biến con trỏ) dùng để đánh dấu vị trí đầu của một vùng nhớ mà khi cần có thể thu hồi lại Thủ tục Releaseip) thu hồi vùng nhớ bắt đầu từ
vị trí được đánh dấu bằng lệnh Markip).
28
Trang 291.3.2 Khái niệm danh sách liên kết
Danh sách liên kết là một cách tổ chức dữ liệu cho mô hình danh sách trong đó các phần tử được móc nối với nhau nhờ vào vùng liên kết
Danh sách liên kết sử dụng cơ chế cấp phát động nên thích hợp với các thao tác thêm vào, loại bỏ, ghép nhiều danh sách
1.3.3 Tổ chức danh sách liên kết
Mỗi phần tử của danh sách liên kết gồm hai thành phần:
+ Phần Data chứa dữ liệu thực sự của từng phần tử trong danh sách.
+ Phần Link dùng để iiên kết một phần tử với phần tử ngay sau nó.
Từ mỗi phần tử ta chỉ duy trì một liên kết đến phần tử ngay sau nó và danh sách liên kết được tổ chức như vậy được gọi là danh sách liên kết đơn Trong phần này ta chỉ xét cấu trúc dữ liệu danh sách liên kết đơn và không gây nhầm lẫn khi ta gọi là danh sách liên kết
Hình ảnh một danh sách liên kết biểu diễn danh sách L = («1, «2- •••> ^«) như sau:
Hình 2.4 Hình dnh danh sách liên kết đơn
Để quản lí danh sách biểu diễn bởi danh sách liên kết ta chỉ cần quản lí phần tử đầu tiên của danh sách Từ phần tử này ta có thể thao tác được với các phần tử của danh sách nhờ liên kết giữa các phần tử Khai báo danh sách liên kết có dạng:
Trang 30í 3.4 Các phép toán trên danh sách liên kết
Khởi tạo danh sách liên kết
P r o c e d u r e I n i t ( v a r H e a d : L i s t L i n k ) ;
B e g i n
H e a d : = N i l ;
E n d ;
Thêm một phần tử vào danh sách
Thêm phần tử X vào sau phần tử ở vị trí p trong danh sách có phần tử đầu là Head.
p Head
Hình 2.5 Thêm một phán lử vào danh sách liên kết
Thủ tục thêm một phần tử vào vị trí sau p Trong thủ tục ta xét trường hợp khi danh
sách rỗng thì phần tử thêm vào chính là phần tử duy nhất của danh sách Chi tiết thủ tục như sau:
Trang 31Loại bỏ một phân tử khỏi danh sách
Giả sử cần loại bỏ phần tử ngay sau phần tử b vị trí p trong danh sách có phần tử đầu là Head.
Hình 2.6 Xoá một phần tử trong danh sách liên kết
Thủ tục xoá một phần tử sau vị trí p trong danh sách:
Dễ thấy độ phức tạp của thao tác thêm và xoá trong danh sách liên kết là 0(1)
Tìm một phần tử trong danh sách liên kết
Cho danh sách liên kết có phần tử đầu là Head Cần tìm một phần tử có khoá Key
bằng một giá trị cho trước
Với cách tổ chức dữ liệu của danh sách liên kết, việc truy xuất các phần tử là tuần tựnên thao tác tìm kiếm phải dùng thuật toán tìm tuần tự
Thủ tục tìm khoá X trong danh sách Head, kết quả trả về qua giá trị f o u n d và vị trí
E n d ;
Trang 32Nối hai danh sách:
Cho hai danh sách có phần tử đầu tương ứng là Headì và Headl Ghép danh sách
Head! vào sau danh sách H ea d l.
Để ghép được danh sách 2 sau danh sách 1 phải tìm được vị trí của phần tử cuối danh sách 1 (nếu có) và liên kết với phần tử đầu của danh sách thứ 2 Trong trường hợp danh sách 1 rỗng thì kết quả ghép là danh sách 2
Hình 2.7 Ghép hai danh sách liên kết
E n d ;
E n d ;
1.3.5 So sánh cấu trúc dữ liệu danh sách liên kết đơn và mảng
Khi biểu diễn danh sách bằng mảng phải ước lượng số phần tử tối đa của danh sách Điều này có thể gây ra lãng phí bộ nhớ trong trường hợp danh sách có ít phần tử và sẽ không thêm phần tử vào danh sách được khi có nhiều phần tử Trong khi đó biểu diễn danh sách bằng danh sách liên kết đơn sử dụng cơ chế cấp phát động nên bộ nhớ dùng cho danh sách được sử dụng đúng với số phần tử của danh sách tại mọi thời điểm do đó ít gây lãng phí ô nhớ và danh sách chỉ đầy khi không còn không gian nhớ để cấp phát Tuy nhiên, danh sách liên kết phải dùng một phần bộ nhớ để liên kết các phần tử trong danh sách
Trong danh sách tổ chức bằng mảng, thao tác truy xuất các phần tử là truy xuất trực tiếp nhưng các thao tác thêm một phần tử, xoá một phần tử có thời gian tỉ lệ với số phần tử của danh sách Đối với danh sách liên kết đơn các thao tác thêm vào và xoá một phần tử được thực hiện với thời gian hằng trong khi thao tác với một phần tử là tuần tự Tuỳ thuộc vào ứng dụng, cụ thể của danh sách với các phép toán thường dùng của nó mà lựa chọn cách
tổ chức danh sách bằng mảng hay bằng danh sách liên kết
32
Trang 331.3.6 Một sô' dạng danh sách liên kết khác
Một trong những hạn chế của danh sách liên kết đơn là phải quản lí được phần tử đầu tiên của danh sách và những thao tác với một phần tử phải biết phần tử ngay trước nó Những hạn chế này có thể được khắc phục phần nào bởi việc thay đổi kiểu liên kết Trong phần này giới thiệu hai loại danh sách liên kết khác là liên kết vòng và liên kết kép
Danh sách liên kết vòng
Danh sách liên kết vòng là danh sách liên kết đofn với một thay đổi là phần tử cuối của danh sách liên kết với phần tử đầu tiên Hình ảnh của danh sách liên kết vòng như hình sau:
tHình 2.8 Danh sách liên kết vòng
Với cách tổ chức như trên, trong trường hợp không quan tâm đến thứ tự trước-sau của các phần tử, để quản lí danh sách ta chỉ cần quản lí phần tử bất kì trong danh sách mà không nhất thiết phải quản lí phần tử đầu tiên vì từ vị trí bất kì ta đều có thể truy xuất đến những phần tử còn lại của danh sách bằng cách duyệt tuần tự
Về tổ chức dữ liệu, danh sách liên kết vòng có tổ chức hoàn toàn giống danh sách liên kết đơn Để quản lí danh sách liên kết vòng ta dùng một biến con trỏ quản lí một phần
tử bất kì, phần tử này gọi là phần tử hiện tại của danh sách Trên danh sách liên kết vòng thường thực hiện các thao tác sau:
Thêm một phần tử vào ngay sau phần tử hiện tại của danh sách
Thêm một/phần tử vào ngay trước phần tử hiện tại của danh sách
Xoá phần tử ngay sau phần tử hiện tại
Duyệt danh sách
Cách tổ chức dữ liệu cho danh sách liên kết vòng giống như danh sách liên kết đơn Các thủ tục trên danh sách liên kết vòng được mô tả như sau:
Thêm vào sau
a) Thêm vào danh sách rỗng
L
b) Thêm vào danh sách khác rỗng
Hình 2.9 Thêm vào sau phần tử hiện tại trong danh sách lién kết vòng
Trang 34Thêm vào trước
Hình 2.10 Thêm vào trước phần tử hiện tại trong danh sách liên kết vòng
Thủ tục thêm vào trước được thực hiện bằng cách thêm vào sau và chuyển phần tử hiện tại của danh sách là phần tử vừa thêm vào
Trang 35Danh sách liên kết kép
Khi làm việc với những danh sách mà việc thao tác trên một phần tử của nó liên quan đến cả phần tử ngay trước và ngay sau nó thì việc tổ chức danh sách bằng liên kết đoìi không đáp ứng được Trong các trường hợp như vậy, mỗi phần tử của danh sách thường được dùng hai liên kết đến phần tử ngay trước và ngay sau nó, danh sách như vậy gọi là danh sách liên kết kép
Trang 36\
right
Hình 2.12 Danh sách liên kết kép
Vì danh sách liên kết kép có thể duyệt theo cả hai chiểu nên cần quản lí cả hai phần
tử đầu và cuối danh sách Khai báo dữ liệu cho danh sách liên kết kép như sau:
Trang 37b e g i n
L L i n k : = n i l ; R L i n k : = n i l ;
p l ^ R l i n k : = q ;
q ^ R L i n k : = p ; L L i n k : = q ;
e n d ; End;
Trang 38L R i g h t : =p'' L L i n k
e l s e
b e g i n
p ' ' L L i n k ^ R L i n k : =p^ R L i n k ; p'' RLink"^ L L i n k : =p'*' L L i n k ;
e n d ;
d i s p o s e ( p ) ;
E nd ;
Duyệt danh sách liên kết kép
Có thể duyệt từ trái sang phải hoặc ngược lại Thủ tục duyệt từ trái sang phải được thực hiện như sau:
1.4 NGĂN XẾP (STACK)
Trong một số trưòng hợp chúng ta sử dụng mô hình dữ liệu và chỉ dùng một số thao tác trên mô hình đó Khi đó mô hình dữ liệu cùng VỚỊ những phép toán cụ thể trên mô hình
đó gọi là kiểu dữ liệu trừu tượng (abstract data type) Trong phần sau ta xét hai kiểu dữ liệu
trừu tượng có ứng dụng nhiều trong các thuật toán: ngăn xếp và hàng đợi.
a,
Hlnh 2.15 Hình ảnh ngăn xếp
38
Trang 39Như vậy tfong một ngăn xếp, phần tử đưa vào sau sẽ được lấy ra trước nên còn gọi
là danh sách kiểu LIFO (Last In First Out) Vị trí của phần tử cuối cùng của ngăn xếp còn gọi là đỉnh (top) của ngăn xếp
Ngăn xếp là kiểu dữ liệu trừu tượng có nhiều ứng dụng trong tin học
Các ngôn ngữ lập trình bậc cao bao giờ cũng dành riêng một vùng nhớ gọi là Stack dùng để lưu lại các giá trị của biến, hằng, Mỗi khi có lời gọi thủ tục, các giá trị này được lấy lại mỗi khi có một lời gọi thực hiện xong Việc lưu các giá trị như trên phải theo nguyên tắc hoạt động của ngăn xếp vì lời gọi thủ tục cuối cùng sẽ kết thúc trước Do đó ngăn xếp là một cách tổ chức dữ liệu được dùng nhiều trong các chương trình chuyển từ đệ quy sang lặp
Các chương trình dịch (compiler) thường phải chuyển đổi các biểu thức trung tố thành các biểu thức tương đương ở dạng hậu tố Chẳng hạn biểu thức (3 + 4) * 2 được chuyển thành 3 4 + 2 * Việc chuyển các biểu thức từ trung tố thành hậu tố và tính giá trị các biểu thức hậu tố phải dùng cấu trúc dữ liệu kiểu ngăn xếp để lưu các kết quả trung gian Chi tiết các thuật toán này sẽ được trình bày trong phần sau
Như đã để cập ở khái niệm ngăn xếp, các thao tác trên ngăn xếp gồm hai thao tác
cơ bản là:
p u s h ( x , s ): đưa phần tử X vào ngăn xếp 5
p o p ( x ): lấy phần tử ở đỉnh ngăn xếp s ra và lưu vào biến X.
Ngoài ra, còn có các thao tác bổ sung;
I n i t ( S ): khởi tạo một ngăn xếp s rỗng
E m p t y ( S ): cho biết ngãn xếp s có rỗng không.
F u l l ( S ); cho biết ngăn xếp s có đầy không.
Vì thao tác thêm vào và lấy ra chỉ thực hiện ở đỉnh của ngăn xếp nên dùng một biến
để quản lí vị trí đỉnh của ngăn xếp Một ngăn xếp tổ chức bằng mảng bao gồm hai thành phần:
+ Một mảng để lưu các phần tử của ngăn xếp
+ Vị trí đỉnh của ngăn xếp
Hình ảnh ngăn xếp a 2 , , a„) tổ chức bằng mảng như sau:
Trang 40Các thao tác trên ngăn xếp
Khởi tạo: Đặt đỉnh ngăn xếp tại vị trí 0
P r o c e d u r e I n i t ( v a r s : S t a c k A r r ) ;
B e g i n
S t o p : = 0 ;
E n d ;
Hàm Empty kiểm tra đỉnh ngăn xếp, nếu top = 0 thì ngăn xếp rỗng.
Hàm Full kiểm tra đỉnh ngăn xếp nếu top = MaxLength thì ngăn xếp đầy