You are on page 1of 53

KHOA CÔNG NGHỆ THÔNG TIN

MÔN HỌC: CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT

BÀI 3: NGĂN XẾP VÀ


HÀNG ĐỢI
STACK & QUEUE
Biên soạn: Ths. Trần Huê
NỘI DUNG

1. Ngăn xếp (Stack)

2. Hàng đợi (Queue).

3. Ngăn xếp và hàng đợi móc nối sơ lược


3. 1. NGĂN XẾP( STACK)

3.1.1. Định nghĩa

3.1.2. Biểu diễn

3.1.3. Các thao tác cơ bản với ngăn xếp

3.1.4. Ứng dụng ngăn xếp


3.1. NGĂN XẾP

3.1.1. Định nghĩa


• Ngăn xếp là 1 dạng đặc biệt của danh sách liên kết mà việc bổ sung hay loại bỏ 1 phần tử
đều thực hiện ở 1 đầu của danh sách gọi là đỉnh.
• Ngăn xếp có 2 thao tát cơ bản: thêm phần tử vào được gọi là push và loại bỏ phần tử
được gọi là pop.
• Việc loại bỏ phần tử sẽ tiến hành loại bỏ phần tử mới nhất được đưa vào danh sách, chính
vì tính chất này mà ngăn xếp còn được gọi là kiểu dữ liệu LIFO( last in first out – Vào sau ra
trước)
• Ví dụ:
• Ta hình dung ngăn xếp như một ngăn kéo đựng tài liệu (của một bàn làm việc) mà ta
chỉ có thể thêm vào hoặc bớt đi các phần tử (tài liệu) trong đó từ mặt trên (đỉnh) của
ngăn kéo.
• Đoạn đảo chiều toa xe lửa.
• Hộp băng đạn của súng AK.
• Một chồng các đồ vật cùng kiểu (sách, bát, áo, đồng xu, hộp đạn súng máy AK47)
• Vì vậy mà có tên gọi Stack (danh sách kiểu xếp chồng)
3.1. NGĂN XẾP

3.1.1. Định nghĩa


3.1. NGĂN XẾP

3.1.2. Biểu diễn


a. Cài đặt ngăn xếp bằng mảng
b. Cài đặt ngăn xếp bằng liên kết
3.1. NGĂN XẾP

3.1.2. Biểu diễn


• Với kiểu ngăn xếp, ta chỉ có thể thực hiện các thao tác sau:
• Khởi tạo một ngăn xếp.
• Đẩy (push) một phần tử mới vào ngăn xếp nếu ngăn xếp chưa đầy. Phần tử dữ liệu mới luôn được
thêm tại đỉnh.
• Lấy (pop) một phần tử ra khỏi ngăn xếp nếu ngăn xếp khác rỗng. Phần tử bị loại là phần tử đang nằm
tại đỉnh.
• Kiểm tra xem ngăn xếp có hay không có phần tử (rỗng hay không).
• Kiểm tra xem ngăn xếp đã đầy hay chưa
• Mọi tác động khác vào ngăn xếp đều phải thông qua các thao tác này.
• Như vậy:
• Các phần tử của ngăn xếp có cùng một kiểu nào đó
• Ngăn xếp là một trường hợp riêng của danh sách, được sử dụng trong các ứng dụng có liên quan đến
sự đảo ngược. Trong CTDL ngăn xếp, việc thêm hay lấy dữ liệu chỉ được thực hiện tại một đầu. Dữ liệu
thêm vào sau sẽ lấy ra trước, tính chất này còn được gọi là vào sau ra trước (Last In First Out -LIFO).
• Vì vậy, từ nay về sau khi nói đến ngăn xếp ta hiểu đó là danh sách kiểu LIFO
3.1 Ngăn xếp (Stack)
3.1.2 Biểu diễn Ngăn xếp

Slide 8
3.1.3 Biểu diễn Ngăn xếp

Slide 9
3.1.3 Các thao tác cơ bản với ngăn xếp
 Push: Thêm một phần tử vào đỉnh của ngăn xếp, số
phần tử của ngăn xếp tăng lên 1.
 Pop: Xóa bỏ phần tử đầu tiên ở đỉnh của ngăn xếp, số
phần tử của ngăn xếp giảm đi 1.
 Top: Lấy giá trị của phần tử đầu tiên ở đỉnh của ngăn
xếp, số phần tử của ngăn xếp không thay đổi.
 IsEmpty: Kiểm tra ngăn xếp trống hay không. Ngăn xếp
trống là ngăn xếp không có phần tử nào.
 IsFull: Kiểm tra ngăn xếp đã đầy hay chưa. Thao tác này
không phải lúc nào cũng có.
 Size: Lấy số lượng phần tử stack đang có.
Slide 10
3.1.3 CÀI ĐẶT NGĂN XẾP BẰNG MẢNG
Chúng ta sẽ sử dụng mảng 1 chiều kiểu int làm Stack stack,
một biến capacity để lưu kích thước(sức chứa) của stack và
một biến top để lưu chỉ số của phần tử ở top của
Stack stack.
1. Kiểm tra stack đầy(IsFull)

Hàm này sẽ kiểm tra xem stack hiện tại đã đầy hay
chưa. Nếu chỉ số top của stack đang bằng với capacity
- 1, tức stack đã đầy 1
2
bool IsFull(int capacity){
.
if(top >= capacity - 1){
3 return true;
4 }else{
5 return false;
6 }
7 }

Slide 11
2.2. Kiểm tra stack rỗng(IsEmpty)

Nếu như stack đang không có phần tử nào, ta sẽ gán chỉ số top = -1 để đánh dấu. Như vậy, để
kiểm tra stack có đang rỗng hay không rất đơn giản. Ta chỉ cần so sánh giá trị top có phải -1 hay
không mà thôi.

1 bool IsEmpty(){
2 if(top == -1){
3 return true;
4 }else{
5 return false;
6 }
7 }
• 2.3. Thêm phần tử vào đỉnh stack(Push)
• Chúng ta sẽ chỉ có thể push(thêm phần tử) vào đỉnh stack khi stack chưa đầy. Nếu
stack đầy, chúng ta sẽ đưa ra thông báo và không thực hiện push. Ngược lại, ta sẽ
tăng top lên một đơn vị và gán giá trị cho phần tử tại chỉ số top .
1 void Push(int stack[], int value, int capacity){
2 if(IsFull(capacity) == true){
3 printf("\nStack is full. Overflow condition!");
4 }else{
5 ++top;
6 stack[top] = value;
7 }
8 }
• 2.4. Xóa phần tử khỏi đỉnh stack(Pop)
Chúng ta sẽ chỉ có thể pop(xóa phần tử) khỏi đỉnh stack khi
stack không trống. Nếu stack trống, chúng ta sẽ đưa ra thông báo và
không thực hiện pop. Ngược lại, ta sẽ giảm giá trị top đi một đơn vị.
1 void Pop(){
2 if(IsEmpty() == true){
3 printf("\nStack is empty. Underflow
4 condition!");
5 }else{
6 --top;
7 }
}
• 2.5. Lấy giá trị phần tử ở đỉnh stack(Top)
• Để lấy giá trị phần tử ở đỉnh stack, ta có thao tác rất đơn giản:
1 int Top(int stack[]){
2 return stack[top];
3 }

• 2.6. Lấy số lượng phần tử stack đang có(Size)


Biến top lưu chỉ số lớn nhất của stack. Như vậy, việc lấy size của stack cực kỳ đơn giản:

1 int Size(){
2 return top + 1;
3 }
• Và cuối cùng, chúng ta sẽ có 1 chương trình cài đặt stack hoàn thiện như sau:
Stack cài đặt trên con trỏ
Stack trên con trỏ vẫn là stack bình thường nhưng linh
hoạt hơn vì nó dùng con trỏ để cấp phát và quản lý,
không bị thừa hoặc thiếu gì cả.
Xây dựng cấu trúc
typedef int item; //kieu du lieu
struct Node
{
item Data; //du lieu
Node *Next; //link
};
typedef struct Stack
{
Node *Top;
};
Slide 18
Stack cài đặt trên con trỏ
 Khởi tạo danh sách rỗng, kiểm tra danh sách rỗng,
độ dài danh sách

Slide 19
Stack cài đặt trên con trỏ
 Tạo 1 Node

Slide 20
Stack cài đặt trên con trỏ
 Chèn phần tử vào Stack (Push)
Để chèn phần tử vào Stack thì chỉ cần cho con trỏ Note
đó trỏ và Top, rồi Top trỏ lại nó là xong

Slide 21
Stack cài đặt trên con trỏ
 Lấy dữ liệu tại Top nhưng không xóa (Peak)

Slide 22
Stack cài đặt trên con trỏ
 Xóa và lấy dữ liệu tại Top (Pop)
Ta chỉ cần cho con trỏ Top trỏ đến vị trí thứ 2 thôi.

Slide 23
3.1.4 Ứng dụng của ngăn xếp

Slide 24
3.1.4 ỨNG DỤNG NGĂN XẾP
3.1.4 Ứng dụng của ngăn xếp
VÍ DỤ
VÍ DỤ
Đổi cơ số từ hệ thập phân sang hệ nhị phân

Slide 29
Đổi cơ số từ hệ thập phân sang hệ nhị phân

Slide 30
Bài tập ngăn xếp

Bài 1: Viết chương trình chuyển một số nguyên thập phân


sang nhị phân

Bài 2: Viết chương trình in ra màn hình các số là số nguyên tố trong


Stack.

Bài 3: Viết chương trình cài đặt ngăn xếp các số nguyên bằng
mảng

Slide 31
3.2. HÀNG ĐỢI

• Queue là một dạng danh sách đặt biệt, trong đó chúng ta chỉ được
phép thêm vào các phần tử ở cuối hàng đợi và xoá đi các phần tử ở
đầu hàng đợi. Vì phần tử thêm vào trước được lấy ra trước nên cấu
trúc hàng đợi còn được gọi là cấu trúc FIFO( First In First Out).
• Hàng đợi là cấu trúc được sử dụng rộng rãi trong thực tế: người ta
dùng hàng đợi để giải quyết các vấn đề có cấu trúc FIFO như xử lý
các dịch vụ của ngân hàng, để xử lý các tiến trình đang đợi phục vụ
trong các hệ điều hành nhiều người sử dụng, quản lý các yêu cầu in
trên máy print servers…
3.1. HÀNG ĐỢI

• Queue là một dạng danh sách đặt biệt, trong đó chúng ta chỉ được
phép thêm vào các phần tử ở cuối hàng đợi và xoá đi các phần tử ở
đầu hàng đợi. Vì phần tử thêm vào trước được lấy ra trước nên cấu
trúc hàng đợi còn được gọi là cấu trúc FIFO( First In First Out).
• Hàng đợi là cấu trúc được sử dụng rộng rãi trong thực tế: người ta
dùng hàng đợi để giải quyết các vấn đề có cấu trúc FIFO như xử lý
các dịch vụ của ngân hàng, để xử lý các tiến trình đang đợi phục vụ
trong các hệ điều hành nhiều người sử dụng, quản lý các yêu cầu in
trên máy print servers…
3.2. HÀNG ĐỢI

3.2.1 Khái niệm về hàng đợi


•Hàng đợi chứa các nút có cùng kiểu dữ liệu trải dài từ đầu hàng đợi
(front) đến cuối hàng đợi (rear).
•Có hai tác vụ chính trên hàng đợi là insert – thêm nút mới vào cuối
hàng đợi và tác vụ remove dùng để xoá một phần tử ra khỏi hàng
đợi.
•Hình vẽ sau đây dùng để mô tả hàng đợi qua các tác vụ như sau:
3.2. HÀNG ĐỢI

3.2.1 Khái niệm về hàng đợi


 Trạng thái bắt đầu: hàng đợi bị rỗng (hình a)
 insert (q,A)
 insert (q,B)
 insert (q,C) (hình b)
 remove(q) (hình c)
 insert(q,D) (hình d)
 remove(q) (hình e)
 insert(q,E) (hình f)
3.2.2 Mô tả hàng đợi
•Mô tả dữ liệu:
•Hàng đợi là một kiểu dữ liệu trừu tượng có nhiều nút cùng kiểu
dữ liệu trải dài từ đầu hàng đợi đến cuối hàng đợi.
•Mô tả các tác vụ:
 Tác vụ initialize
• Chức năng: khởi động hàng đợi
• Dữ liệu nhập: không
• Dữ liệu xuất: hai con trỏ front và rear được gán các giá trị phù
hợp.
3.2.2 Mô tả hàng đợi
 Tác vụ empty
• Chức năng: kiểm tra hàng đợi có bị rỗng hay không
• Dữ liệu nhập: không
• Dữ liệu xuất: TRUE|FALSE
 Tác vụ insert
• Chức năng: Thêm nút mới vào hàng đợi.
• Dữ liệu nhập: nút mới
• Điều kiện: hàng đợi không bị đầy.
• Dữ liệu xuất: không.
 Tác vụ remove
• Chức năng: xóa nút tại đầu hàng đợi
• Dữ liệu nhập: không
• Điều kiện: hàng đợi không bị rỗng
• Dữ liệu xuất: nút bị xóa.
 Tác vụ queuefront
• Chức năng: truy xuất nút tại đầu hàng đợi
3.2.2 Mô tả hàng đợi
• Dữ liệu nhập: không
• Điều kiện: hàng đợi không bị rỗng.
• Dữ liệu xuất: nút tại đầu hàng đợi.
 Tác vụ queuerear
• Chức năng: truy xuất nút cuối của hàng đợi
• Dữ liệu nhập: không
• Điều kiện: hàng đợi không bị rỗng
• Dữ liệu xuất: nút cuối cùng của hàng đợi
 Tác vụ queuesize
• Chức năng: xác định số nút hiện có trong hàng đợi
• Dữ liệu nhập: không
• Dữ liệu xuất: số nút có trong hàng đợi
 Tác vụ clearqueue
• Chức năng: xoá tất cả các nút có trong hàng đợi
• Dữ liệu nhập: Không
• Dữ liệu xuất: front và rear được trả về vị trí ban đầu.
3.2.3 Ứng dụng của hàng đợi
 Hàng đợi được dùng để giải quyết các vấn đề có cơ chế FIFO (First In
First Out).
 Trong thực tế người ta thường cài đặt hàng đợi trong các chương trình xử
lý các dịch vụ ngân hàng, xử lý các tác vụ đang đợi phục vụ trong các hệ
điều hành đa người sử dụng, quản lý các yêu cầu in trong máy server
printers…
3.2.3 Ứng dụng của hàng đợi
 Hàng đợi được dùng để giải quyết các vấn đề có cơ chế FIFO (First In
First Out).
 Trong thực tế người ta thường cài đặt hàng đợi trong các chương trình xử
lý các dịch vụ ngân hàng, xử lý các tác vụ đang đợi phục vụ trong các hệ
điều hành đa người sử dụng, quản lý các yêu cầu in trong máy server
printers…
3.2.4 HIỆN THỰC QUEUE

 Dùng mảng vòng hiện thực hàng đợi


 Đây là cách thường dùng nhất để cài đặt hàng đợi theo kiểu kế tiếp. Lúc này ta xem
mảng như là mảng vòng chứ không phải là mảng thẳng: nút nodes[0] xem như là nút
sau của nút nodes[MAXQUEUE -1].
a. Khai báo cấu trúc:
#define MAXQUEUE 100
struct queue{
int front, rear;
int nodes[MAXQUEUE];
}
3.2.4 HIỆN THỰC QUEUE

 Dùng mảng vòng hiện thực hàng đợi


b. Tác vụ khởi động:
void initialize(struct queue *pq){
pq->front=pq->rear=MAXQUEUE-1;
}

Tác vụ kiểm tra hàng đợi trống:


int empty(struct queue *pq){
if(pq->front==pq->rear)
return TRUE;
else{
return FALSE;
}
}
3.2.4 HIỆN THỰC QUEUE

 Dùng mảng vòng hiện thực hàng đợi


c. Tác vụ thêm vào hàng đợi:
void insert(struct queue *pq, int x){
if(pq->rear==MAXQUEUE -1)
pq->rear=0;
else
pq->rear++;
if(pq->rear==pq->front)
printf(“Hang doi bi day”);
else
pq->nodes[pq->rear]=x;
}
3.2.4 HIỆN THỰC QUEUE

 Dùng mảng vòng hiện thực hàng đợi


d. Tác vụ xoá một phần tử ra khỏi hàng đợi
int remove(struct queue *pq){
if(empty(pq))
printf(“hang doi bi day”);
else{
if(pq->front==MAXQUEUE-1)
pq->front=0;
else
pq->front++;
return pq->nodes[pq->front];
}
}
3.2.4 HIỆN THỰC QUEUE

 Dùng mảng vòng hiện thực hàng đợi


e. Tác vụ duyệt hàng đợi
void traverse(struct queue *pq){
int i;
if(empty(pq)){
printf(“Hang doi bi rong”);
return;
}
if(pq->front==MAXQUEUE-1)
i=0;
else
i=pq->front+1;
while(i!=pq->rear){
printf(“%d”,pq->nodes[i]);
if(i==MAXQUEUE-1)
i=0;
else
i++;
}
printf(“%d”,pq->nodes[i]);
}
3.2.4 HIỆN THỰC QUEUE

•Dùng danh sách liên kết hiện thực hàng đợi


•Ta có thể cài đặt hàng đợi như một danh sách liên kết: con trỏ đầu danh sách
liên kểt front chỉ nút tại đầu hàng đợi, con trỏ rear trỏ đến nút cuối cùng của
hàng đợi.
• Hình vẽ sau thể hiện cách cài đặt hàng đợi bằng danh sách liên kết.
3.2.4 HIỆN THỰC QUEUE

•Dùng danh sách liên kết hiện thực hàng đợi


Với cách cài đặt này ta có thể hiện thực các tác vụ của hàng
đợi như: insert, remove, empty…trên danh sách liên kết.
3.2.4 HIỆN THỰC QUEUE

•Dùng danh sách liên kết hiện thực hàng đợi


Với cách cài đặt này ta có thể hiện thực các tác vụ của hàng
đợi như: insert, remove, empty…trên danh sách liên kết.
3.2.4 HIỆN THỰC QUEUE

•Hàng đợi có ưu tiên


• Hàng đợi hoạt động trên nguyên tắc nút nào vào trước được lấy ra trước, nút nào
vào sau được lấy ra sau là hàng đợi không có ưu tiên.
• Trong thực tế có một dạng hàng đợi khác hoạt động theo cơ chế: nút nào có độ ưu
tiên cao hơn sẽ được lấy ra trước, nút nào có độ ưu tiên thấp hơn thì lấy ra sau.
Hàng đợi lúc này gọi là hàng đợi có ưu tiên (priority queue).
• Có 2 loại hàng đợi có ưu tiên:
• Hàng đợi có ưu tiên tăng: nút có độ ưu tiên cao nhất được lấy ra.
• Hàng đợi có ưu tiên giảm: nút có độ ưu tiên giảm sẽ được lấy ra trước.
• Phần hiện thực hàng đợi ưu tiên sẽ được xem như bài tập.
3.2.4 HIỆN THỰC QUEUE

•Hàng đợi có ưu tiên


• Hàng đợi hoạt động trên nguyên tắc nút nào vào trước được lấy ra trước, nút nào
vào sau được lấy ra sau là hàng đợi không có ưu tiên.
• Trong thực tế có một dạng hàng đợi khác hoạt động theo cơ chế: nút nào có độ ưu
tiên cao hơn sẽ được lấy ra trước, nút nào có độ ưu tiên thấp hơn thì lấy ra sau.
Hàng đợi lúc này gọi là hàng đợi có ưu tiên (priority queue).
• Có 2 loại hàng đợi có ưu tiên:
• Hàng đợi có ưu tiên tăng: nút có độ ưu tiên cao nhất được lấy ra.
• Hàng đợi có ưu tiên giảm: nút có độ ưu tiên giảm sẽ được lấy ra trước.
• Phần hiện thực hàng đợi ưu tiên sẽ được xem như bài tập.
3.2.4 HIỆN THỰC STACK+ QUEUE

CHƯƠNG TRÌNH MINH HOẠ

Bài 1: Viết chương trình chuyển một số nguyên thập phân sang nhị phân

Bài 2: Viết chương trình in ra màn hình các số là số nguyên tố trong Stack.

Bài 3: Viết chương trình cài đặt ngăn xếp các số nguyên bằng mảng

Bài 4: Viết chương trình nhập 1 chuỗi bất kỳ và in ra ngược đảo chuỗi đó

Bài 5: Giả sử chúng ta có một kho hàng có tính chất: mặt hàng nào nhập kho
trước sẽ xuất kho trước, mặt hàng nào nhập kho sau sẽ xuất kho sau. Chương
trình có các chức năng nhập kho, xuất kho, xem tồn kho…

You might also like