You are on page 1of 52

Chương 4

Mảng và danh sách

LOGO
Nội dung

1. Mảng và danh sách


2. Ngăn xếp (stack)
3. Hàng đợi (queue)
4. Danh sách móc nối đơn
5. Danh sách móc nối vòng
6. Danh sách móc nối kép
7. Ngăn xếp và hang đợi móc nối
Mảng
Mảng là một tập có thứ tự gồm một số cố
định n phần tử thuộc cùng một kiểu dữ liệu.
Các phép toán:
 Tạo mảng
 Duyệt qua các phần tử mảng
 Tìm kiếm một phần tử của mảng
 Sắp xếp các phần tử trong mảng
Cấu trúc lưu trữ của mảng:
Cấu trúc lưu trữ kế tiếp hay cấu trúc lưu trữ tuần tự
Danh sách…

Danh sách khác với mảng: là một tập có


thứ tự nhưng bao gồm một số biến động
các phần tử
Lưu trữ kế tiếp của danh sách tuyến tính
Cách thức cài đặt danh sách:
 Mảng
 Danh sách bằng móc nối
Danh sách
Phép toán
 Bổ sung thêm phần tử mới
 Loại bỏ một phần tử cũ
 Tìm kiếm trong danh sách một phần tử mà một
thành phần nào đó có một giá trị ấn định.
 Cập nhật một phần tử.
 Sắp xếp các phần tử trong danh sách theo một thứ
tự ấn định.
 Ghép hai hoặc nhiều danh sách thàmh một danh
sách lớn.
 Tách một danh sách thành nhiều danh sách con.
 Sao chép một danh sách…
Nội dung

1. Mảng và danh sách


2. Ngăn xếp (stack)
3. Hàng đợi (queue)
4. Danh sách móc nối đơn
5. Danh sách móc nối vòng
6. Danh sách móc nối kép
7. Ngăn xếp và hang đợi móc nối
Ngăn xếp (stack)
Một stack là một cấu
trúc dữ liệu mà việc
thêm vào và loại bỏ
được thực hiện tại
một đầu (gọi là đỉnh –
top của stack).
Là một dạng vào sau
ra trước – LIFO (Last
In First Out)
Ví dụ
 Stack rỗng:

 Đẩy (push) Q vào:

 Đẩy A vào:

 Lấy (pop) ra một => được A:

 Lấy ra một => được Q và stack rỗng:


Lưu trữ stack bằng mảng
Đẩy một phần tử vào danh
sách
Giải thuật:
1. Nếu còn chỗ trống trong stack
1.1. Tăng vị trí đỉnh lên 1
1.2. Chứa giá trị vào vị trí đỉnh
của stack
1.3. Tăng số phần tử lên 1
Đẩy một phần tử vào danh
sách…
Giải thuật:
PUSH (S, T, X)
{
/* Hàm này thực hiện bổ sung phần tử X vào ngăn
xếp lưu trữ bởi mảng S có n phần tử. T là biến trỏ
tới đỉnh ngăn xếp */
if (T ≥ n-1)
printf(“Ngăn xếp TRÀN”);
else
{
T =T + 1; // tăng T lên vị trí mới
S[T] = X; // bổ sung phần tử mới X
}
}
Lấy phần tử trên đỉnh
stack…
Giải thuật:
1. Nếu còn phần tử trong stack
1.1. Giảm vị trí đỉnh đi 1
1.2. Giảm số phần tử đi 1
Lấy phần tử trên đỉnh stack

Giải thuật:
POP (S, T)
{
/* Hàm này thực hiện việc loại bỏ phần tử ở đỉnh
ngăn xếp S đang được trỏ bởi T. Phần tử bị loại sẽ
được thu nhận và đưa ra */
if (T < 0)
printf(“Ngăn xếp CẠN”);
else
{
T =T - 1; // giảm T đi một vị trí
return S[T+1]; // đưa phần tử bị loại ra
}
}
Định giá biểu thức số học

Các dạng biểu diễn:


 Dạng trung tố (infix): 5 * (7 - 3)
 Dạng hậu tố (postfix): 5 7 3 - *
 Dạng tiền tố (prefix): * 5 - 7 3
 Mô tả thuật toán:
 chuyển các biểu thức dạng trung tố có dấu
ngoặc sang hậu tố, sau đó mới tạo ra các chỉ thị
máy để định giá biểu thức ở dạng hậu tố
Định giá biểu thức số học
hậu tố
Mô tả bài toán:
 Các toán hạng được đọc vào trước và đẩy vào
stack
 Khi đọc vào toán tử, lấy hai toán hạng ra từ
stack, tính toán với toán tử này, rồi đẩy kết quả
vào stack
Tập lệnh:
 ‘?’: đọc một giá trị rồi đẩy vào stack
 Toán tử ‘+’, ‘-’, ‘*’, ‘/’: lấy 2 giá trị trong stack,
tính toán và đẩy kết quả vào stack
 Toán tử ‘=’: in đỉnh của stack ra
Định giá biểu thức số học
hậu tố
Định giá biểu thức số học hậu
tố
DINH_GIA_BIEU_THUC() //tác động phép toán X vào Y và
{//Giải thuật này sử dụng 1 ngăn xếp Z, rồi gán kết quả cho W
S, trỏ bởi T, lúc đầu T = -1 W = Z X Y;
do PUSH (S, T, W);
{ }
Đọc phần tử X tiếp theo }
trong biểu thức; while (chưa gặp dấu kết
if (X là toán hạng) thúc biểu thức);
PUSH (S, T, X); R = POP (S, T);
else printf(R);
{ }
Y = POP (S, T);
Z = POP (S, T);
Chuyển đổi biểu thức dạng
trung tố sang dạng hậu tố…
Ý tưởng:
1. Khởi tạo một ngăn xếp rỗng (S).
2. Đọc lần lượt các thành phần trong biểu thức dấu
ngoặc:
2.1. Nếu thành phần được đọc là toán hạng thì viết nó vào
biểu thức dạng hậu tố.
2.2. Nếu thành phần được đọc là phép toán (phép toán
hiện thời), thì thực hiện các bước sau:
• Nếu S không rỗng: nếu phần tử ở đỉnh S là phép toán có quyền
ưu tiên cao hơn hay bằng phép toán hiện thời, thì phép toán đó
được kéo ra khỏi S và viết vào biểu thức dạng hậu tố. Lặp lại
bước này.
• Nếu S rỗng hoặc phần tử ở đỉnh ngăn xếp là dấu mở ngoặc hoặc
phép toán ở đỉnh S có quyền ưu tiên thấp hơn phép toán hiện
thời, thì phép toán hiện thời được đẩy vào S.
Chuyển đổi biểu thức dạng
trung tố sang dạng hậu tố…
Ý tưởng:
2.3. Nếu thành phần được đọc là dấu mở ngoặc
thì nó được đẩy vào S.
2.4. Nếu thành phần được đọc là dấu đóng
ngoặc thì thực hiện các bước sau:
• (Bước lặp) Loại các phép toán ở đỉnh S và viết vào
biểu thức dạng hậu tố cho tới khi đỉnh S là dấu mở
ngoặc.
• Loại dấu mở ngoặc khỏi S.
3. Sau khi toàn bộ biểu thức dạng trung tố được
đọc, loại các phép toán ở đỉnh ngăn xếp và viết
vào biểu thức dạng hậu tố cho tới khi S rỗng
Chuyển đổi biểu thức dạng
trung tố sang dạng hậu tố

( Biểu thức dạng hậu tố:


( Biểu thức dạng hậu tố: * abc–d+
* a
( Biểu thức dạng hậu tố:
+ abc–d+*
- Biểu thức dạng hậu tố:
( ab
* / Biểu thức dạng hậu tố:
+ abc–d+*ef

+ Biểu thức dạng hậu tố:


Biểu thức dạng hậu tố:
( abc-
abc–d+*ef/+
*
Nội dung

1. Mảng và danh sách


2. Ngăn xếp (stack)
3. Hàng đợi (queue)
4. Danh sách móc nối đơn
5. Danh sách móc nối vòng
6. Danh sách móc nối kép
7. Ngăn xếp và hang đợi móc nối
Hàng đợi (Queue)
Queue là một cấu trúc dữ liệu mà việc
thêm vào được thực hiện ở một đầu và
việc lấy ra được thực hiện ở đầu còn lại
Phần tử vào trước sẽ ra trước – FIFO (First
In First Out)
Lưu trữ hàng đợi bằng mảng
Lưu trữ hàng đợi bằng mảng
Thêm phần tử vào hàng đợi

QUEUE_INSERT (F, R, Q, n, X)
if (F == -1)
{/* Cho F và R trỏ tới lối trước và lối sau
// Điều chỉnh con trỏ F khi bổ
của hàng đợi kiểu vòng tròn, được lưu
sung lần đầu
trữ bởi mảng Q có n phần tử. Hàm này
thực hiện bổ sung một phần tử mới X F = 0;
vào lối sau của hàng đợi. Ban đầu khi }
hàng đợi rỗng thì F = R = -1*/ }
if (R == n-1) // Chỉnh lại con trỏ R
R=0
else
R = R + 1;
if (F == R) // Kiểm tra hàng đợi TRÀN
printf(“Hàng đợi TRÀN”);
else // Bổ sung X vào
{
Q[R] = X;
Loại bỏ phần tử của hàng đợi

QUEUE_DELETE (F, R, Q, n)
else
{/* Cho F và R trỏ tới lối trước và lối sau của
hàng đợi kiểu vòng tròn, được lưu trữ bởi mảng
F = F +1
Q có n phần tử. Hàm này thực hiện việc loại bỏ return Y;
một phần tử ở lối trước của hàng đợi và đưa nội }
dung của phần tử đó ra */ }
if (F == -1) // Kiểm tra hàng đợi CẠN
printf(“Hàng đợi CẠN”);
else // Loại bỏ
{
Y = Q[F]; // Đưa phần tử cần loại bỏ ra biến Y
if (F == R) // Hàng đợi chỉ có một phần tử
F = R = -1;
else // Chỉnh lại con trỏ F sau phép loại bỏ
if (F == n-1)
F=0
Nội dung

1. Mảng và danh sách


2. Ngăn xếp (stack)
3. Hàng đợi (queue)
4. Danh sách móc nối đơn
5. Danh sách móc nối vòng
6. Danh sách móc nối kép
7. Ngăn xếp và hang đợi móc nối
Danh sách móc nối đơn…

 sử dụng con trỏ hoặc móc nối để tổ chức danh


sách tuyến tính, gọi là danh sách móc nối
 mỗi phần tử của danh sách được lưu trữ trong
một phần tử gọi là nút (node):
 DATA chứa dữ liệu của phần tử.
 NEXT chứa địa chỉ đến nút tiếp theo
 nút cuối cùng: NEXT phải chứa một
giá trị đặc biệt được gọi là NULL (trống, không có giá trị)
Danh sách móc nối đơn…
 Bổ sung một nút vào danh sách móc nối đơn
INSERT(L, Q, X)
{/* Cho L là con trỏ, trỏ tới nút đầu tiên của một danh sách móc nối đơn,
Q là con trỏ trỏ tới một nút đang có trong danh sách. Giải thuật này thực
hiện bổ sung vào sau nút trỏ bởi Q một nút mới mà thành phần DATA nhận
giá trị X */
P= malloc();//Cấp phát bộ nhớ
P->DATA=X;
P->NEXT= NULL;
if (L == NULL) // Danh sách rỗng
L= P;
else
{
P->NEXT= Q->NEXT;
Q->NEXT= P;
}
}
Danh sách móc nối đơn…
 Loại bỏ một nút ra khỏi danh sách móc nối đơn
DELETE(L, Q) else
{/* thực hiện loại bỏ nút trỏ bởi Q ra {// Tìm đến nút đứng trước
khỏi DS */ nút trỏ bởi Q
if (L == NULL) // Trường hợp DS P = L;
rỗng
while (P->NEXT != Q)
printf (“Danh sách rỗng”);
P = P->NEXT;
else
P->NEXT = Q->NEXT;
if (Q == L) // Loại bỏ nút đầu tiên
// Loại bỏ nút trỏ bởi Q
của DS
{ free(Q);
L= Q->NEXT; // Loại bỏ nút trỏ }
bởi Q }
free(Q); // giải phóng vùng nhớ
của nút mà Q trỏ vào
}
Danh sách móc nối đơn
 Ghép hai danh sách móc nối đơn
COMBINE (P, Q)
{/* Cho hai danh sách móc nối đơn lần lượt trỏ bởi P và Q. Giải thuật
này thực hiện ghép hai danh sách trên thành một danh sách (ghép Q
vào cuối P) */
if (Q != NULL) // Q khác rỗng mới tiến hành ghép
if (P == NULL) // Trường hợp danh sách trỏ bởi P rỗng
P= Q;
else
{ // Tìm đến nút cuối danh sách P
P1 = P;
while (P1->NEXT != NULL)
P1 = P1->NEXT;
P1->NEXT = Q; // Ghép danh sách Q vào cuối P
}
Nội dung

1. Mảng và danh sách


2. Ngăn xếp (stack)
3. Hàng đợi (queue)
4. Danh sách móc nối đơn
5. Danh sách móc nối vòng
6. Danh sách móc nối kép
7. Ngăn xếp và hang đợi móc nối
Danh sách móc nối vòng…
có thể truy nhập vào mọi nút trong danh
sách bắt đầu từ một nút nào cũng được,
không nhất thiết phải từ nút đầu tiên

nhược điểm rất rõ là trong xử lý, nếu không


cẩn thận thì sẽ dẫn tới một chu trình không
kết thúc.
Danh sách móc nối vòng
đoạn giải thuật bổ sung một nút vào
thành nút đầu tiên của một danh sách
móc nối vòng có “nút đầu danh sách” trỏ
bởi HEAD.
P=malloc();
P->DATA= X;
HEAD->NEXT = P;
P->NEXT = HEAD;
Nội dung

1. Mảng và danh sách


2. Ngăn xếp (stack)
3. Hàng đợi (queue)
4. Danh sách móc nối đơn
5. Danh sách móc nối vòng
6. Danh sách móc nối kép
7. Ngăn xếp và hang đợi móc nối
Danh sách móc nối kép…

Cấu trúc 1 nút:


 P_L là con trỏ trái, trỏ tới nút đứng trước.
 P_R là con trỏ phải, trỏ tới nút đứng sau.
DATA
 DATA chứa dữ liệu. P_L
P-R
Danh sách móc nối kép…
bổ sung một nút mới, mà dữ liệu chứa ở
X, vào trước nút trỏ bởi Q
DOUBLE_IN (L, R, Q, X)
P->P_R = Q;
{/* Cho con trỏ L và R lần lượt trỏ tới
nút cực trái và nút cực phải của một Q->P_L = P;
danh sách móc nối kép*/ L = P;
P= malloc(); // 1- Tạo nút mới }
P->DATA=X; else // 4- Bổ sung vào giữa
P->P_R= P->P_L= NULL; {
if (R == NULL) // 2- Trường hợp P->P_L = Q->P_L;
danh sách rỗng P->P_R = Q;
L = R = P; Q->P_L = P;
else P->P_L->P_R = P;
if (Q == L) // 3- Q trỏ tới nút }
cực trái }
{
Danh sách móc nối kép…
loại nút trỏ bởi Q ra khỏi danh sách
DOUBLE_DEL (L, R, Q) else
{ if (M == R) // Nút cực phải bị
if (R == NULL) //1- DS rỗng loại
printf(“Danh sách rỗng”); {
else // 2- Loại bỏ R= R->P_L;
{ R->P_R = NULL;
if (L== R) // Danh sách chỉ }
có một nút và Q trỏ tới nút đó else // Loại bỏ nút bên trong
L = R = NULL; {
else Q->P_L->P_R = Q->P_R;
if (Q == L) //Nút cực trái Q->P_R->P_L = Q->P_L;
bị loại
}
{ free(Q);
L = L->P_R; }
L->P_L = NULL; }
}
Danh sách móc nối kép

Giải thuật bổ sung nút chỉ có bước 1 (1- Tạo


nút mới) và bước 4 (4- Bổ sung vào giữa).
Giải thuật loại bỏ nút chỉ còn một bước:
Q->P_L->P_R = Q->P_R;
Q->P_R->P_L = Q->P_L;
free(Q);
Nội dung

1. Mảng và danh sách


2. Ngăn xếp (stack)
3. Hàng đợi (queue)
4. Danh sách móc nối đơn
5. Danh sách móc nối vòng
6. Danh sách móc nối kép
7. Ngăn xếp và hàng đợi móc nối
Stack móc nối
Phép thêm vào Stack móc
nối
 Giải thuật
 1. Tạo ra một node mới với giá trị cần thêm vào
 2. Trỏ nó đến đỉnh hiện tại của stack
 3. Trỏ đỉnh của stack vào node mới
Bỏ đỉnh của một stack móc
nối
 Giải thuật:
 1. Gán một con trỏ để giữ đỉnh của stack
 2. Trỏ đỉnh của stack vào node ngay sau đỉnh
hiện tại
 3. Xóa node cũ đi
Queue móc nối

Thiết kế:
Dùng hai con trỏ chỉ đến đầu và cuối của danh
sách dữ liệu (front và rear)

Khởi tạo rỗng: gán cả front và rear về NULL


Phép thêm phần tử vào một
queue móc nối
Giải thuật:
1. Tạo một node mới với dữ liệu cần thêm vào
2. Nếu queue đang rỗng
2.1. front và rear là node mới new_rear
3. Ngược lại
3.1. Nối node mới vào sau rear
3.2. rear chính là node mới. new_last
rear
front

front middle last


Bỏ phần tử khỏi một queue
móc nối
 Giải thuật:
1. Dùng một con trỏ để giữ lại front hiện tại
2. Nếu queue có một phần tử
2.1. Gán front và rear về NULL
3. Ngược lại
3.1. Trỏ front đến nút kế sau
4. Xóa nút cũ đi.
Q&A
Bài tập áp dụng
1. Cho hệ phương trình đại số tuyến tính có
dạng:
a11x1 = b1
a21x1 + a21x1 = b2
...
an1x1 + an2x2 +…+ annxn = bn
Để tiết kiệm, người ta lưu trữ ma trận hệ
số hệ phương trình dưới dạng mảng một
chiều. Như vậy chỉ cần n(n + 1)/2 phần từ
chứ không cần tới n2 phần tử. Hãy lập giải
thuật giải hệ phương trình trên ứng với
cấu trúc lưu trữ như đã định.
Bài tập áp dụng
2. Viết biểu thức sau đây dưới dạng hậu tố:
(A*B)/(C+D)
Dựng hình ảnh của ngăn xếp được sử
dụng để định giá trị biểu thức hậu tố này
ứng với A = 20; B = 4, C = 9, D = 7.
3. Viết giải thuật tính tổng hai đa thức sử
dụng danh sách móc nối đơn.
4. Cho một đa thức P(x) được tổ chức dưới
dạng một danh sách móc nối đơn. Gọi P là
con trỏ trỏ tới danh sách đó. Hãy lập giải
thuật tính giá trị của P(x) với giá trị x cho
biết.
Bài tập áp dụng
5. Lập các giải thuật thực hiện các phép sau đây đối
với DS móc nối đơn mà nút đầu tiên của nó được
trỏ bởi L.
a) Tính số lượng các nút của danh sách.
b) Tìm tới nút thứ k trong DS, nếu có nút thứ k thì in ra dữ
liệu của nút đó.
c) Bổ sung một nút vào sau nút thứ k.
d) Loại bỏ nút đứng trước nút thứ k.
e) Cho thêm con trỏ Q trỏ tới một nút có trong DS nói trên
và một DS móc nối đơn khác có nút đầu tiên trỏ bởi P.
Hãy chèn DS P này vào sau nút trỏ bởi Q.
f) Tách thành 2 DS mà DS sau trỏ bởi Q (cho như ở câu e).
g) Đảo ngược DS đã cho (tạo một danh sách L‟ mà các nút
móc nối theo thứ tự ngược lại so với L).
Bài tập áp dụng

6. Lập giải thuật thực hiện các phép sau đây đối
với danh sách móc nối vòng:
a) Ghép hai danh sách móc nối vòng có nút “đầu
danh sách” lần lượt trỏ bởi P và Q, thành một
danh sách mà nút đầu danh sách trỏ bởi P.
b) Lập “bản sao” của một danh sách móc nối vòng
có nút đầu danh sách trỏ bởi L.
Bài tập áp dụng

7. Cho L và R là 2 con trỏ trỏ tới nút cực trái và


cực phải của một danh sách móc nối kép.
Hãy lập giải thuật bổ sung và loại bỏ để danh
sách đó hoạt động như một hàng đợi.
8. Cho danh sách móc nối kép có nút đầu danh
sách. P là con trỏ, trỏ tới nút đầu danh sách
đó. Hãy lập giải thuật loại bỏ tất cả các nút
mà thành phần DATA của nó có giá trị bằng
K cho trước.

You might also like