Phần 2: Hàng đợi (Queue)
1
Hàng đợi ( Queue)
Theo cơ chế FIFO (First In First Out)
Các đối tượng có thể được thêm vào hàng đợi bất
kỳ lúc nào.
Chỉ có đối tượng thêm vào đầu tiên mới được
phép lấy ra khỏi hàng đợi.
2
Giới thiệu
Vào, ra dữ liệu:
3
Giới thiệu
Ứng dụng của hàng đợi:
Khử đệ qui.
Lưu vết các quá trình tìm kiếm theo chiều rộng
và quay lui, vét cạn
Quản lý và phân phối tiến trình trong các hệ
điều hành, tổ chức bộ đệm bàn phím, …
4
Biếu diễn Queue
Mảng 1 chiều
Danh sách liên kết
Cấp phát
động!
Kích thước stack
khi quá thiếu, lúc
quá thừa
Push/Pop
khá dễ
dàng
Push / Pop hơi
phức tạp
5
Biểu diễn Queue dùng mảng
Biểu diễn một Queue bằng mảng:
Mảng một chiều Ary lưu các phần tử dữ liệu.
Hằng số N cho biết kích thước (số phần tử tối đa)
của hàng đợi.
Hai biến nguyên front, rear cho biết chỉ số của
đầu và cuối của hàng đợi.
6
Biểu diễn Queue bằng mảng
typedef struct item
{
int data;
};
typedef struct queue
{
int front, rear;
item A[N];
};
7
Cấu trúc một
phần tử dữ liệu
Cấu trúc hàng
đợi
Biểu diễn Queue bằng mảng
Các thao tác cơ bản trên Queue
Khởi tạo Queue: Init (q)
Kiểm tra Queue rỗng: IsEmpty (q)
Kiểm tra Queue đầy: IsFull (q)
Thêm phần tử vào Queue: Add(q, x)
Lấy phần tử ra khỏi Queue: Remove (q)
8 179/44
Biểu diễn Queue dùng mảng
Khởi tạo Queue:
Lệnh khởi tạo front = 0 và rear = 0 sẽ tạo ra một
queue rỗng.
Đảm bảo các thao tác trên queue thực hiện đúng đắn.
Thuật toán:
+Vào: Queue cần khởi tạo.
+ Ra: Queue sau khi khởi tao.
B1) Gán front = 0;
rear = -1
BKT) Kết thúc;
Cài đặt:
void Init(queue &q)
{
q.front = 0;
rear = -1;
}
9
Biểu diễn Queue dùng mảng
Kiểm tra Queue rỗng:
Được gọi đến trước khi thực hiện thao tác lấy một
phần tử ra khỏi queue.
Queue rống nếu front = 0 hoặc front > rear.
Thuật toán:
+Vào: Queue cần kiểm tra.
+ Ra: giá trị 0 hoặc 1
B1) if front = 0 or front > rear
then return 1;
else return 0;
BKT) Kết thúc;
Cài đặt:
int IsEmpty(Queue q)
{
if (q.front > q.rear)
return 1;
return 0;
}
10
Biểu diễn Stack dùng mảng
Kiểm tra Queue đầy
Được gọi đến trước khi thực hiện thao tác đưa một
phần tử vào queue.
Queue đầy nếu rear= N-1.
Thuật toán:
+Vào: Queue cần kiểm tra.
+ Ra: giá trị 0 hoặc 1
B1) if rear= N-1 then return 1;
else return 0;
BKT) Kết thúc;
11
Cài đặt:
int IsFull (queue q)
{
if (q.rear==N-1)
return 1;
return 0;
}
Biểu diễn Stack dùng mảng
Thêm một phần tử vào queue
Việc thêm phần tử luôn được thực hiện ở cuối hàng đợi.
Thuật toán
Vào: Queue q, phần tử dữ liệu x
Ra: q với phần tử dữ liệu x ở cuối hàng đợi.
B1) Kiểm tra nếu queue đầy thì thực hiện BKT;
Ngược lại, thực hiện B2;
B2) lưu phần tử dữ liệu x vào queue:
2.1) Tăng rear lên 1;
2.2) Giá trị phần tử với chỉ số rear = x;
BKT) Kết thúc;
12
Biểu diễn Queue dùng mảng
Thêm một phần tử vào hàng đợi
Void Add (queue &q, item x){
if (!Full(q)) //Queue chưa đầy
{
q.rear++;
q.A[q.rear] = x;
}
}
13
Biểu diễn Queue dùng mảng
Lấy phần tử ra khỏi queue
Thuật toán
Vào: Queue q;
Ra: Giá trị phần tử cuối hàng đợi x.
B1) Kiểm tra nếu queue rỗng thì thực hiện BKT;
Ngược lại, thực hiện B2;
B2) Lấy phần tử ra khỏi hàng đợi
2.1) x giá trị phần tử ở đầu hàng đợi (front);
2.2) Tăng chỉ số fron lên 1;
BKT) Kết thúc;
14
Biểu diễn Queue dùng mảng
Lấy một phần tử ra khỏi hàng đợi
Cài đặt:
item Remove (queue &q)
{
item x;
if (!Empty(q))
{
item x = q.A[q.front];
q.front++;
return x;
}
}
15
Biểu diễn Queue dùng mảng
Nhận xét:
Ưu điểm:
• Các thao tác trên queue làm việc với chi phí O(1).
• Việc cài đặt queue thông qua mảng một chiều đơn
giản và khá hiệu quả.
Hạn chế:
• Giới hạn về kích thước của queue N.
• Giá trị của N có thể quá nhỏ so với nhu cầu thực tế
hoặc quá lớn sẽ làm lãng phí bộ nhớ.
Khắc phục: Sử dụng DSLK đơn để biểu diễn queue
16
Biểu diễn Queue bằng DSLK
Có thể tạo một hàng đợi sử dụng một DSLK đơn.
Phần tử đầu DSKL (pHead) sẽ là phần tử đầu
hàng đợi, phần tử cuối DSKL (pTail) sẽ là phần
tử cuối hàng đợi.
Ñaàu haøng
Cuoái
haøng
a0
a1
a2
17
aN-2
an-1
Biểu diễn Queue bằng DSLK
typedef struct node
{
int data;
node *Next;
};
typedef struct queue
{
node *front, *rear;
};
18
Biểu diễn Queue bằng DSLK
Các thao tác cơ bản trên Queue
Khởi tạo queue: Init (q)
Kiểm tra queue rỗng: IsEmpty (q)
Tạo mới một nút chứa dữ liệu x: CreateNode(x)
Thêm phần tử vào queue: Add(q, x)
Lấy phần tử ra khỏi queue: Remove (q)
19 179/44
Biểu diễn Queue dùng mảng
Khởi tạo Queue:
Lệnh khởi tạo front = rear = NULL sẽ tạo ra một
queue rỗng.
Thuật toán:
+Vào: Queue cần khởi tạo.
+ Ra: Queue sau khi khởi tao.
B1) Gán front = rear = NULL
BKT) Kết thúc;
20
Cài đặt:
void Init(queue &q)
{
q.front = q.rear = NULL;
}
Biểu diễn Queue dùng mảng
Kiểm tra Queue rỗng:
Queue rỗng nếu front = NULL.
Thuật toán:
+Vào: Queue cần kiểm tra.
+ Ra: giá trị 0 hoặc 1
B1) if front = NULL then return 1;
else return 0;
BKT) Kết thúc;
21
Cài đặt:
int IsEmpty(Queue q)
{
if (q.front == NULL)
return 1;
return 0;
}
Biểu diễn Stack dùng DSLK
Tạo mới một nút:
Node* CreateNode (int x) {
Node *p;
p = (Node*) malloc (sizeof(Node));
if (p==NULL) {
printf (“Khong du bo nho!”);
return NULL;
}
p->data=x;
p->next=NULL;
return p;
}
22
Biểu diễn Stack dùng mảng
Thêm một phần tử vào queue
Việc thêm phần tử luôn được thực hiện ở cuối hàng đợi.
Thuật toán
Vào: Queue q, phần tử dữ liệu x
Ra: q với phần tử dữ liệu x ở cuối hàng đợi.
B1) NewNode Tạo mới nút chứa dữ liệu x;
B2) Nếu tạo nút thành công thì
- Kiểm tra nếu front = NULL thì
front = rear = NewNode
23
Biểu diễn Stack dùng mảng
B3) Ngược lại, chèn vào cuối danh sách:
+ q.rear->Next = NewNode;
+ q.rear = NewNode;
BKT) Kết thúc;
24
Biểu diễn Queue bằng DSLK
Thêm một phần tử vào queue
Cài đặt:
void Add (queue &q, node *NewNode){
if(q.front == NULL){
q.front = NewNode;
q.rear = NewNode;
}
else{
q.rear->Next = NewNode;
q.rear = NewNode;
}
25
}