1 1 BÀI 3 DANH SÁCH 3 1 Khái niệm danh sách 3 2 Danh sách và mảng 3 3 Danh sách liên kết đơn 3 4 Cách dạng danh sách liên kết khác 2 3 1 Khái niệm danh sách 3 1 1 Nhắc lại về mảng 3 1 2 Khái niệm danh.
Trang 13.1.2 Khái niệm danh sách
3.1.3 Biểu diễn danh sách
Trang 23.1.1 Nhắc lại về mảng
phần tử cùng kiểu Không có phép bổ sung phần tử hoặc
loại bỏ phần tử được thực hiện đối với mảng Thường
chỉ có các phép tạo lập (create) mảng, tìm kiếm
(retrieve) và lưu trữ (store) 1 phần tử của mảng Ngoài
giá trị, 1 phần tử của mảng còn được đặc trưng bởi chỉ
số (index) thể hiện thứ tự của phần tử đó trong mảng
– Vectơ, dãy số là mảng 1 chiều, mỗi phần tử aiứng với 1 chỉ số i.
– Ma trận là mảng 2 chiều, mỗi phần tử aijứng với 2 chỉ số i và j.
– Tương tự với mảng 3 chiều, mảng 4 chiều,…, mảng n chiều.
4
Cấu trúc lưu trữ của mảng
• Cấu trúc lưu trữ đơn giản nhất dùng địa chỉ tính được để thực hiện
lưu trữ và tìm kiếm phần tử là mảng 1 chiều hay vectơ
• Cấu trúc lưu trữ của mảng: Thông thường 1 số ô nhớ kế tiếp sẽ
được dành ra để lưu trữ các phần tử của mảng, ta gọi là cách lưu
trữ kế tiếp – (sequential storage allocation).
• Xét t/h mảng 1 chiều có n phần tử và mỗi phần tử của nó có thể
lưu trữ được trong 1 (ô nhớ) thì cần phải dành cho nó n ô nhớ kế
tiếp nhau Do kích thước của mảng đã được xác định nên không
gian nhớ dành ra cũng đã được ấn định trước.
Trang 3Cấu trúc lưu trữ của mảng
được lưu trữ trong cn từ máy kế tiếp nhau như sau:
Loc(ai) = Lo + c * (i – 1) – Lo được gọi là địa chỉ gốc – đó là địa chỉ ô nhớ đầu tiên trong
miền kế tiếp dành để lưu trữ vectơ, gọi là vectơ lưu trữ.
– f(i) = c * (i – 1) gọi là hàm địa chỉ (address function)
được thực hiện tương tự, nghĩa là vẫn bằng 1 vectơ lưu
trữ kế tiếp như trên
major order), nghĩa là “hết hàng này đến hàng khác”
Loc(aij) = Lo + (i – 1) * m + (j – 1)
Khai báo mảng trong Turbo Pascal
kiểu dữ liệu phần tử là kiểu Ngay_sinh gồm có 3 trường
(Ngay, Thang, Nam)
Var {Khai báo mảng với KDL Ngay_sinh}
Sinh_ngay : Array [1 100] of Ngay_sinh;
Trang 4Ví dụ danh sách
• CTDL mảng rất thông dụng trong các bài toán tin học:
dãy số, vectơ, ma trận,… do tính đơn giản, dễ sử dụng,
khả năng truy cập trực tiếp và tốc độ truy cập các phần tử
rất nhanh thông qua tên và chỉ số, GT cài đặt đơn giản,…
• Tuy nhiên, trong 1 số t/h CTDL mảng không đáp ứng
được yêu cầu của bài toán, thường là về bộ nhớ lưu trữ
• Ta sẽ xem xét thông qua 2 ví dụ sau:
• Ví dụ 1: Quản lý 1 danh mục điện thoại khoảng 100 phần
tử, mỗi phần tử ứng với 1 đơn vị thuê bao, gồm 3 trường:
– Tên đơn vị hoặc chủ hộ thuê bao.
– Địa chỉ.
– Số điện thoại.
8
Bài toán danh mục điện thoại
• Khai báo danh mục điện thoại bằng CTDL mảng 1 chiều:
Type
Thue_bao = record {Kiểu bản ghi}
Ten, DC, DT : String;
End;
Var {Khai báo mảng với KDL Danh_muc}
Danh_muc : Array [1 100] of Thue_bao;
• Tuy nhiên, nếu dùng NNLT Turbo Pascal, máy sẽ báo lỗi:
“Error 22: Structure too large” : Cấu trúc quá lớn
• Vấn đề đặt ra: Làm sao quản lý được danh mục điện thoại
như vậy Không những thế, số lượng thuê bao thực tế còn
lớn hơn, và mỗi thuê bao còn bao gồm nhiều trường dữ
liệu hơn: Giới tính, tuổi,…
Trang 5Bài toán cộng 2 đa thức
• Ví dụ 2: Xét bài toán tính (cộng, trừ, nhân, chia) đa thức
của x, y Chẳng hạn cộng 2 đa thức:
(3x2– xy+ y2– x+ 2y ) + (x2+ 4xy- y2- y+ 1)
= (4x2+ 3xy– x + y+ 1)
Khi thực hiện cộng 2 đa thức, phải tìm kiếm từng số hạng, phải
phân biệt được các biến, hệ số, số mũ trong từng số hạng đó.
• Vấn đề đặt ra: Phải biểu diễn đa thức như thế nào để phép
toán trên được thực hiện thuận tiện và có hiệu quả?
• Có thể sử dụng mảng 2 chiều để biểu diễn đa thức:
– Bài toán cộng 2 đa thức trở thành bài toán cộng 2 ma trận.
– Hệ số của số hạng xiyjsẽ được lưu trữ ở phần tử thuộc hàng i+1,
và cột j+1 của ma trận (mảng 2 chiều).
Bài toán cộng 2 đa thức
• Khai báo mảng 2 chiều Da_thuc:
Type {Khai báo KDL Da_thuc là mảng 2 chiều}
Da_thuc = Array [1 100,1 100] of Shortint;
Var {Khai báo các biến với KDL Da_thuc}
a, b, kq : Da_thuc;
• Kết quả:
– Đưa bài toán đa thức về dạng đơn giản, dễ xử lý.
– GT của bài toán được cài đặt đơn giản:
Trang 6– Nếu hạn chế kích thước ma trận là 4x4 thì số mũ cao nhất của x,
y chỉ có thể là 3, nghĩa là chỉ xử lý được đa thức bậc 3 của x, y.
– Nếu các hệ số không vượt quá 127 (shortint) thì kích thước ma
trận cũng không thể vượt quá 260x260.
– Nếu các hệ số vượt quá 32767 (Integer) thì kích thước ma trận
cũng không thể vượt quá 200x200.
– Trong t/h cộng 2 đa thức:
(x150 – y) + (y - x150) = 0 Khi đó: Ma trận kích thước [151 x 151] gồm toàn các phần tử = 0!
– Không thể cộng được 2 đa thức sau:
(x300– 1) + (y300 + 1) = ???
Trang 7Kết luận
1 CTDL mảng có tốc độ xử nhanh, dễ xử lý Tuy nhiên,
trong rất nhiều bài toán, CTDL mảng không thể đáp ứng
được yêu cầu, đặc biệt trong các bài toán ma trận thưa
(ví dụ bài toán cộng 2 đa thức), hoặc các bài toán có kích
thước dữ liệu lớn, trong khi bộ nhớ có giới hạn (ví dụ bài
toán danh mục điện thoại)
2 Trong những trường hợp như vậy, CTDL danh sách mà
chúng ta sẽ nghiên cứu sau đây tỏ ra thích hợp hơn so
với CTDL mảng
3.1.2 Khái niệm danh sách
1 số biến động (không cố định) các phần tử cùng kiểu
Phép bổ sung và phép loại bỏ 1 phần tử là các phép
thường xuyên tác động lên danh sách
– Chú ý: Mảng còn được gọi là danh sách đặc.
1 Danh sách: Tập hợp các người đến khám bệnh cho ta hình ảnh
1 danh sách Họ sẽ được khám theo 1 thứ tự nào đó.
2 Phép bổ sung: Số người có lúc tăng lên (do có người mới đến).
3 Phép loại bỏ: Số người có lúc giảm đi (đã khám xong, hoặc bỏ
về vì chờ lâu).
(record), gồm 1 hoặc nhiều trường (fields)
– Ví dụ: Danh mục điện thoại là 1 DSTT, mỗi phần tử là 1 thuê
bao, gồm 3 trường: Tên, Địa chỉ và Điện thoại.
Trang 8Danh sách tuyến tính
• Danh sách tuyến tính (DSTT - linear list): Là danh sách
mà quan hệ lân cận giữa các phần tử được hiển thị ra
– Mảng 1 chiều chính là t/h đặc biệt của DSTT, đó là hình ảnh của
DSTT xét tại 1 thời điểm nào đó.
– DSTT là 1 danh sách hoặc rỗng (không có phần tử nào) hoặc có
dạng (a1, a2, …, an) với ai(1 ≤ i ≤ n) là các phần tử của danh
sách Trong DSTT luôn tồn tại 1 phần tử đầu a1và 1 phần tử
cuối an.
– Đối với mỗi phần tử aibất kỳ với 1in-1 thì có 1 phần tử ai+1
gọi là phần tử sau ai, và với 2in thì có 1 phần tử ai-1gọi là
phần tử trước ai.
– aiđược gọi là phần tử thứ i của DSTT, n được gọi là độ dài hoặc
kích thước của danh sách, n có giá trị thay đổi.
16
Các phép toán trong danh sách
phép sau đây cũng hay được tác động lên danh sách:
1 Ghép 2 hoặc nhiều danh sách
2 Tách 1 danh sách thành nhiều danh sách
Trang 9Danh sách và tập tin
• Khái niệm: Tập tin (file) là 1 loại danh sách có kích
thước lớn được lưu trữ ở bộ nhớ ngoài Mỗi phần tử của
tập tin là 1 bản ghi (record), bao gồm nhiều trường (field)
dữ liệu, tương ứng với các thuộc tính khác nhau
– Ví dụ: Tập tin hồ sơ nhân sự của cán bộ trong 1 cơ quan lớn
Phần lý lịch của mỗi cán bộ là 1 bản ghi, nó bao gồm các trường
dữ liệu tương ứng với các thuộc tính như: Họ và tên, ngày sinh,
nơi sinh, quê quán, trình độ văn hoá,…
• Khác với bộ nhớ trong, bộ nhớ ngoài có những đặc điểm
riêng, do đó khi xử lý tập tin cần có những kỹ thuật riêng
• Môn học CTDL và GT chủ yếu nghiên cứu đối với danh
sách có liên quan đến bộ nhớ trong
3.1.3 Biểu diễn danh sách
thể mà máy tính hiểu được để biểu diễn DS, nói cách
khác là lưu trữ các phần tử của DS, đồng thời viết các
đoạn chương trình con mô tả các thao tác cần thiết
tử trong danh sách
1 Cài đặt danh sách bằng mảng 1 chiều
• Có 1 biến nguyên n lưu số phần tử hiện có trong danh sách
Các phần tử trong danh sách được cất giữ trong mảng bằng các
phần tử được đánh số từ 1 tới n
2 Cài đặt danh sách bằng danh sách liên kết
• Cách này lưu trữ địa chỉ của 1 phần tử ở 1 chỗ nào đó trong bộ
nhớ, khi cần xác định sẽ lấy ở đó ra Loại địa chỉ này được gọi
là con trỏ (pointer) hoặc mối nối (link).
Trang 10Các loại danh sách
DSTT thành 2 loại như sau:
1 Danh sách đặc: Là DSTT mà địa chỉ của các phần tử
trong danh sách được lưu trữ bằng mảng 1 chiều
2 Danh sách liên kết: Là DSTT mà địa chỉ của các phần tử
trong danh sách được lưu trữ bằng con trỏ hay mối nối
liên kết Tuỳ theo cách liên kết mà sẽ có danh sách liên
kết đơn, liên kết vòng, liên kết đôi,…
người ta còn đưa ra 2 loại danh sách đặc biệt là:
Trang 113.2.1 Lưu trữ kế tiếp của DSTT
1 chiều để lưu trữ địa chỉ các phần tử của danh sách
tuyến tính cũng được gọi là lưu trữ kế tiếp
là kích thước n thay đổi, còn số phần tử của mảng lưu
trữ là m cố định, nên việc lưu trữ chỉ có thể đảm bảo
được nếu m = Max(n) Nhưng điều này thường không dễ
xác định chính xác mà chỉ là dự đoán
Nhược điểm của lưu trữ kế tiếp
nhiều vì có hiện tượng “giữ chỗ để đấy” mà không dùng
loại bỏ phần tử trong danh sách, mà không phải là phần
tử cuối cùng sẽ đòi hỏi phải dồn hoặc dãn danh sách,
nghĩa là phải dịch chuyển 1 số phần tử lùi xuống (để lấy
chỗ bổ sung) hoặc tiến lên (để lấp chỗ của phần tử đã bị
loại bỏ) và điều này sẽ gây tổn phí nhiều thời gian nếu
các phép toán này được thực hiện thường xuyên
truy cập lại rất rõ
Trang 12Chèn phần tử vào DSTT
nguyên thứ tự các phần tử còn lại, ta phải:
1 Dồn tất cả các phần tử từ vị trí p + 1 tới vị trí n lên trước
1 vị trí:
2 Giảm n đi 1:
Trang 133.2.2 Stack
trong các bài toán tin học là: Ngăn xếp và hàng đợi
mà phép bổ sung và phép loại bỏ luôn luôn thực hiện ở 1
đầu gọi là đỉnh (top)
súng tiểu liên Lắp đạn hay lấy đạn ra cũng chỉ ở 1 đầu
hộp Viên đạn mới nạp vào sẽ nằm ở đỉnh (top), còn viên
nạp vào đầu tiên nằm ở đáy (bottom) Viên đạn nạp vào
sau cùng lại chính là viên đạn lên nòng súng trước tiên
gọi khác: danh sách kiểu LIFO (Last In First Out)
Mô tả Stack
• Stack có thể rỗng hoặc bao gồm 1 số phần tử Minh hoạ
stack như hình vẽ sau:
K 6 5 4 3 2 1
… 5 4 3 2 1
Thêm 1
pt vào Stack
Loại bỏ 1
pt khỏi Stack
T
T
T
Trang 14Lưu trữ Stack bằng mảng
nhớ kế tiếp Nếu T là địa chỉ của phần tử đỉnh của stack
thì T sẽ có giá trị biến đổi khi stack hoạt động (vì vậy
người ta gọi T là 1 biến trỏ - variable pointer)
khi stack rỗng T = 0
phần tử bị loại khỏi stack, T sẽ giảm đi 1 Có thể thấy
cấu trúc lưu trữ của stack như hình vẽ sau:
1 Việc bổ sung 1 phần tử vào Stack tương đương với việc
thêm 1 phần tử vào cuối mảng
• Thủ tục Push
2 Việc loại bỏ một phần tử khỏi Stack tương đương với
việc loại bỏ một phần tử ở cuối mảng
Trang 15Procedure Push(V: Integer); {Đẩy 1 giá trị V vào Stack}
Begin {Nếu Stack đã đầy thì không đẩy được thêm vào nữa}
If Last = max Then WriteLn('Stack is full')
Else begin{Nếu không thì thêm một phần tử vào cuối mảng}
Inc(Last); Stack[Last] := V; end;
End;
GT ứng với Stack
{Lấy 1 giá trị ra khỏi Stack, trả về kết quả trong tên hàm}
Function Pop: Integer;
Begin {Stack đang rỗng thì không lấy được}
If Last = 0 then WriteLn('Stack is empty')
Else begin{Lấy phần tử cuối ra khỏi mảng}
Trang 163.2.3 Ứng dụng của stack
nguyên tắc “vào sau ra trước” của Stack, người ta ứng
dụng nó trong một số bài toán điển hình như sau:
1 Bài toán đổi cơ số
2 Bài toán định giá biểu thức số học theo ký pháp nghịch
đảo Balan
Bài toán tính N!, bài toán tháp Hà Nội, …
32
Bài toán đổi cơ số
• Bài toán: Dữ liệu lưu trữ trong bộ nhớ của MTĐT đều
được biểu diễn nhị phân Nghĩa là các số xuất hiện trong
chương trình đều phải chuyển từ thập phân sang nhị phân
trước khi thực hiện các phép xử lý
– Đối với hệ thập phân: Khi ta viết 26 thì nó biểu diễn con số mà
giá trị là: 2 * 101+ 6 * 100
– Đối với hệ nhị phân: Khi ta viết 11010 thì nó biểu diễn số thập
phân có giá trị là:
1*24+ 1*23+ 0*22+ 1*21+ 0*20= (26)10
• Quy tắc chuyển đổi: Khi đổi 1 số nguyên từ thập phân
sang nhị phân thì ta dùng phép chia liên tiếp cho 2 đến khi
thương là 0 và viết các số dư (là các chữ số nhị phân 0, 1)
theo chiều ngược lại
Trang 17Bài toán đổi cơ số
được hiển thị trước Cơ chế sắp xếp này chính là cơ chế
của stack
qua từng phép chia: Khi thực hiện phép chia số thập
phân thì nạp số dư vào stack, sau đó lấy chúng lần lượt
từ stack ra và được số nhị phân
1 Thực hiện phép chia 26 liên tiếp cho 2 đến khi thương = 0
2 Đẩy các số dư thu được vào Stack
3 Lấy các số dư thu được khỏi Stack và viết ra
Trang 18R := N mod 2; {Tính số dư R trong phép chia N cho 2}
PUSH(R); {Nạp R vào đỉnh stack}
N := N div 2; {Thay N bằng thương của N div 2}
Bài toán ký pháp nghịch đảo Balan
• Bài toán: Nhiệm vụ của bộ dịch là tạo ra các chỉ thị máy
cần thiết để thực hiện các lệnh của chương trình nguồn,
viết bằng NNLT cấp cao Một phần trong nhiệm vụ này là
tạo ra các chỉ thị định giá các biểu thức số học Chẳng hạn
với câu lệnh gán:
X := A * B + C
• Bộ dịch phải tạo ra các chỉ thị máy tương ứng như sau:
– LOA A: Tìm giá trị A lưu trữ trong bộ nhớ, tải nó vào thanh ghi.
– MUL B: Tìm giá trị B và nhân nó với giá trị đang ở thanh ghi.
– ADD C: Tìm giá trị C và cộng nó với giá trị trong thanh ghi.
– STO X: Đưa giá trị trong thanh ghi vào lưu trữ ở vị trí tương
ứng của X, trong bộ nhớ
Trang 19Bài toán ký pháp nghịch đảo Balan
• Trong các NNLT, biểu thức số học được viết như dạng
thông thường của toán học, nghĩa là theo ký pháp trung
tố Mỗi ký hiệu của phép toán 2 ngôi được đặt giữa 2 toán
hạng, có thể có thêm dấu ngoặc
– Dấu ngoặc là cần thiết vì nếu viết: 5 * 7 – 3
– Thì theo quy ước về thứ tự ưu tiên của phép toán (mà các NNLT
đều chấp nhận) thì biểu thức trên có nghĩa là:
(5 * 7) – 3
• Nhà logic học Ba Lan Lukasiewicz đã đưa ra dạng biểu
thức số học theo ký pháp hậu tố và tiền tố, được gọi là
dạng ký pháp Ba Lan
Bài toán ký pháp nghịch đảo Balan
• Ở dạng hậu tố, các toán tử đi sau các toán hạng Như biểu
• Còn ở dạng tiền tố, các toán tử đi trước các toán hạng
• Ông cũng khẳng định rằng đối với các dạng ký pháp này
dấu ngoặc là không cần thiết
• Nhiều bộ dịch khi định giá biểu thức số học thực hiện:
– B1: Chuyển các bt dạng trung tố có dấu ngoặc sang hậu tố
– B2: Tạo ra các chỉ thị máy để định giá bt ở dạng hậu tố.
– Chú ý: Việc biến đổi từ dạng trung tố sang hậu tố không khó
khăn gì, còn việc định giá theo dạng hậu tố thì dễ dàng hơn,
“máy móc” hơn so với dạng trung tố
Trang 20Bài toán ký pháp nghịch đảo Balan
• Cách chuyển từ dạng trung tố sang dạng hậu tố:
Trung tố: th1 {tt} th2 Hậu tố: th1 th2 {tt}
• Ví dụ: Xét các định giá của biểu thức sau:
1 5 + 8 4 1 - - *Tương ứng với bt trung tố: (1 + 5) * (8 – (4 – 1))
• Bt này được đọc từ trái sang phải cho tới khi tìm ra 1 toán
tử 2 toán hạng được đọc cuối cùng, trước toán tử này, sẽ
được kết hợp với nó
• B1: Trong bt trên, toán tử đầu tiên được đọc là + và 2 toán
hạng tương ứng với nó là 1 và 5, sau khi kết hợp, bt con
này có giá trị là 6, thay vào bt ban đầu ta có bt rút gọn là:
6 8 4 1 - - *
40
Bài toán ký pháp nghịch đảo Balan
• B2: Toán tử đầu tiên được đọc là - và 2 toán hạng tương
ứng với nó là 4 và 1, sau khi kết hợp, bt con này có giá trị
là 3, thay vào bt ban đầu ta có bt rút gọn là:
6 8 3 - *
• B3: Toán tử đầu tiên được đọc là - và 2 toán hạng tương
ứng với nó là 8 và 3, sau khi kết hợp, bt con này có giá trị
là 5, thay vào bt ban đầu ta có bt rút gọn là:
Trang 21Bài toán ký pháp nghịch đảo Balan
• Phương pháp định giá biểu thức hậu tố như trên đòi hỏi
phải lưu trữ các toán hạng cho tới khi 1 toán tử được đọc
Tại thời điểm này, 2 toán hạng cuối cùng phải được tìm ra
và kết hợp với toán tử này
• Như vậy, ở đây đã xuất hiện cơ chế hoạt động: “vào sau ra
trước”, nghĩa là ta sẽ phải sử dụng tới stack để lưu trữ các
toán hạng
• Cứ mỗi lần đọc được 1 toán tử thì 2 giá trị sẽ được lấy ra
từ stack để áp đặt toán tử đó lên chúng và kết quả lại được
đẩy vào stack
• Ví dụ: Minh hoạ cơ chế hoạt động Stack đối với biểu thức
hậu tố 5 7 3 - * (tương ứng với biểu thức 5 * (7 – 3))
Minh hoạ cơ chế hoạt động stack
Phần tử X tiếp theo là 1 toán tử (-)
- * 4 T Lấy 3 và 7 từ stack Thực hiện (7 – 3)
Rồi đẩy kết quả vào stack
Phần tử X tiếp theo là 1 toán tử (*)
* 20 T Lấy 4 và 5 từ stack, thực hiện (5 * 4)
Rồi đẩy kết quả vào stack
Trang 22<Đọc phần tử X tiếp theo trong biểu thức>;
If <X là toán hạng> then PUSH(X)
• Khái niệm: Queue (hàng đợi) là kiểu DSTT mà phép bổ
sung được thực hiện ở 1 đầu, gọi là lối sau (rear), và phép
loại bỏ thực hiện ở 1 đầu khác, gọi là lối trước (front)
• Cơ cấu của queue giống như 1 hàng đợi, chẳng hạn mua
vé xe lửa, khách hàng nào xếp hàng trước sẽ được mua vé
trước, vào ở 1 đầu và ra ở 1 đầu khác
• Vì nguyên tắc “vào trước thì ra trước” như vậy, queue còn
được gọi là danh sách kiểu FIFO (First In First Out)
Last First
Trang 23Lưu trữ Queue bằng mảng
• Khi mô tả Queue bằng mảng, ta có hai chỉ số First và Last, First
lưu chỉ số phần tử đầu Queue còn Last lưu chỉ số cuối Queue.
• Khởi tạo Queue rỗng: First := 1 và Last := 0;
• Để thêm 1 phần tử vào Queue, ta tăng Last lên 1 và đưa giá trị đó
vào phần tử thứ Last.
• Để loại một phần tử khỏi Queue, ta lấy giá trị ở vị trí First và tăng
First lên 1.
• Khi Last tăng lên hết khoảng chỉ số của mảng thì mảng đã đầy,
không thể đẩy thêm phần tử vào nữa.
• Khi First > Last thì tức là Queue đang rỗng.
• Như vậy chỉ 1 phần của mảng từ vị trí First tới Last được sử dụng
làm Queue
GT ứng với Queue
Program QueueByArray;
Const max = 10000;
Var Queue: Array[1 max] of Integer;
First, Last: Integer;
Procedure QueueInit; {Khởi tạo Queue rỗng}
Begin
First :=1; Last := 0;
End;
Procedure Push(V: Integer); {Đẩy một giá trị V vào Queue}
Begin {Nếu Queue đã đầy thì không đẩy được thêm vào nữa}
If Last = max Then WriteLn(‘Queue is full')
Else begin{Nếu không thì thêm một phần tử vào cuối mảng}
Inc(Last); Queue[Last] := V; end;
End;
Trang 24GT ứng với Queue
{Lấy 1 giá trị ra khỏi Queue, trả về trong kết quả hàm}
Function Pop: Integer;
Begin {Queue đang rỗng thì không lấy được}
If First > Last then WriteLn(‘Queue is empty')
Else begin{Lấy phần tử đầu ra khỏi mảng}
Nhược điểm khi lưu trữ Q bằng mảng
• Xem lại chương trình cài đặt Stack bằng 1 mảng kích
thước tối đa 10000 phần tử, ta thấy rằng nếu như ta làm
6000 lần Push, rồi 6000 lần Pop, rồi lại 6000 lần Push thì
vẫn không có vấn đề gì xảy ra Lý do là vì chỉ số Last lưu
đỉnh của Stack sẽ được tăng lên 6000 rồi lại giảm đến 0
rồi lại tăng trở lại lên 6000
• Nhưng đối với cách cài đặt Queue như trên thì sẽ gặp
thông báo lỗi tràn mảng, bởi mỗi lần Push, chỉ số cuối
hàng đợi Last cũng tăng lên và không bao giờ bị giảm đi
cả Đó chính là nhược điểm mà ta nói tới khi cài đặt: Chỉ
có các phần tử từ vị trí First tới Last là thuộc Queue, các
phần tử từ vị trí 1 tới First - 1 là vô nghĩa
Trang 25Khắc phục nhược điểm
• Để khắc phục, mô tả Queue bằng 1 danh sách vòng (biểu
diễn bằng mảng hoặc cấu trúc liên kết), coi như các phần
tử của Queue được xếp quanh vòng theo 1 hướng nào đó
– Các phần tử nằm trên phần cung tròn từ vị trí First tới Last là
các phần tử của Queue.
– Có thêm 1 biến n để lưu số phần tử của Queue.
– Việc thêm 1 phần tử vào Queue tương đương với việc ta dịch chỉ
số Last theo vòng 1 vị trí rồi đặt giá trị mới vào đó.
– Việc loại bỏ 1 phần tử trong Queue tương đương với việc lấy ra
phần tử tại vị trí First rồi dịch chỉ số First theo vòng.
• Lưu ý là trong thao tác Push và Pop phải kiểm tra Queue
tràn hay Queue cạn nên phải cập nhật lại biến n
Mô tả Queue bằng danh sách vòng