Giáo trình Cấu trúc dữ liệu và giải thuật - Chương 3: Hàng đợi
Định nghĩa: Một hàng các phần tử kiểu T là một chuỗi nối tiếp các phần tử của T,
kèm các tác vụ sau:
1. Tạo mới một đối tượng hàng rỗng.
2. Thêm một phần tử mới vào hàng, giả sử hàng chưa đầy (phần tử dữ liệu mới
luôn được thêm vào cuối hàng).
3. Loại một phần tử ra khỏi hàng, giả sử hàng chưa rỗng (phần tử bị loại là phần
tử tại đầu hàng, thường là phần tử vừa được xử lý xong).
4. Xem phần tử tại đầu hàng (phần tử sắp được xử lý).
ến tính có front luôn chỉ vị trí đầu tiên trong
hàng và mọi phần tử của hàng phải di chuyển tới một bước khi phần tử tại
front được lấy đi. Đây là phương pháp rất dở trong máy tính nói chung.
• Một dãy tuyến tính có hai chỉ số front và rear luôn luôn tăng. Đây là
phương pháp tốt nếu như hàng có thể được làm rỗng.
• Một dãy vòng có hai chỉ số front,rear và một vị trí để trống.
• Một dãy vòng có hai chỉ số front,rear và một cờ cho biết hàng đầy (hoặc
rỗng).
• Một dãy vòng có hai chỉ số front,rear và một biến đếm số phần tử hiện có
trong hàng.
• Một dãy vòng có hai chỉ số front,rear mà hai chỉ số này sẽ mang trị đặc
biệt trong trường hợp hàng rỗng.
3.3.2. Phương án hiện thực hàng liên kết
Bằng cách sử dụng bộ nhớ liên tục, việc hiệc thực hàng khó hơn việc hiện thực
ngăn xếp rất nhiều do chúng ta dùng vùng nhớ tuyến tính để giả lặp tổ chức vòng
và gặp khó khăn trong việc phân biệt một hàng đầy với một hàng rỗng. Tuy
nhiên, hiện thực hàng liên kết lại thực sự dễ dàng như hiện thực ngăn xếp liên
kết. Chúng ta chỉ cần nắm giữ hai con trỏ, front và rear để tham chiếu đến
phần tử đầu và phần tử cuối của hàng. Các tác vụ thêm hay loại phần tử trên
hàng được minh họa trong hình 3.5.
Chương 3 – Hàng đợi
Giáo trình Câu trúc dữ liệu và Giải thuật 46
3.4. Hiện thực hàng
3.4.1. Hiện thực hàng liên tục
Hiện thực vòng cho hàng liên tục trong C++
Phần này trình bày các phương thức của cách hiện thực hàng bằng dãy vòng
có biến đếm các phần tử. Chúng ta có định nghĩa lớp Queue như sau:
const int maxQueue = 10; // Giá trị nhỏ chỉ để kiểm tra CTDL Queue.
template
class Queue {
public:
Queue();
bool empty() const;
ErrorCode serve();
ErrorCode append(const Entry &item);
ErrorCode retrieve(Entry &item) const;
protected:
int count;
int front, rear;
Entry entry[maxQueue];
};
Các dữ liệu thành phần trong lớp Queue được khai báo protected. Đối với người sử dụng sẽ
không có gì thay đổi, nghĩa là chúng vẫn không được người sử dụng nhìn thấy và vẫn đảm bảo sự
che dấu thông tin. Mục đích ở đây là khi chúng ta xây dựng lớp Extended_Queue dẫn xuất từ lớp
Queue thì lớp dẫn xuất sẽ sử dụng được các dữ liệu thành phần này. Khi các dữ liệu thành phần
của lớp cơ sở được khai báo là private thì lớp dẫn xuất cũng sẽ không nhìn thấy chúng.
template
Queue::Queue()
/*
post: đối tượng hàng đã tồn tại và được khởi tạo là hàng rỗng.
*/
Hình 3.5 Các tác vụ thêm và loại phần tử trên hàng liên kết
Chương 3 – Hàng đợi
Giáo trình Câu trúc dữ liệu và Giải thuật 47
{
count = 0;
rear = maxQueue - 1;
front = 0;
}
template
bool Queue::empty() const
/*
post: hàm trả về true nếu hàng rỗng; ngược lại, hàm trả về false.
*/
{ return count == 0;
}
template
ErrorCode Queue::append(const Entry &item)
/*
post: nếu hàng còn chỗ, item được thêm vào tại rear, ErrorCode trả về là success; ngược lại,
ErrorCode trả về là overflow, hàng không đổi.
*/
{
if (count >= maxQueue) return overflow;
count++;
rear = ((rear + 1) == maxQueue) ? 0 : (rear + 1);
entry[rear] = item;
return success;
}
template
ErrorCode Queue::serve()
/*
post: nếu hàng không rỗng, phần tử tại front được lấy đi, ErrorCode trả về là success; ngược
lại, ErrorCode trả về là underflow, hàng không đổi.
*/
{
if (count <= 0) return underflow;
count--;
front = ((front + 1) == maxQueue) ? 0 : (front + 1);
return success;
}
template
ErrorCode Queue::retrieve(Entry &item) const
/*
post: nếu hàng không rỗng, phần tử tại front được chép vào item, ErrorCode trả về là
success; ngược lại, ErrorCode trả về là underflow; cả hai trường hợp hàng đều không
đổi.
*/
{
if (count <= 0) return underflow;
item = entry[front];
return success;
}
Chúng ta dành phương thức empty lại như là bài tập. Phương thức size dưới
đây rất đơn giản do lớp Extended_Queue sử dụng được thuộc tính count của lớp
Chương 3 – Hàng đợi
Giáo trình Câu trúc dữ liệu và Giải thuật 48
Queue, nếu như count được khai báo là private thì phương thức size của lớp
Extended_Queue phải sử dụng hàng loạt câu lệnh gọi các phương thức public
của Queue như serve, retrieve, append mới thực hiện được. Các phương thức
còn lại của Extended_Queue cũng tương tự, và chúng ta dành lại phần bài tập.
template
int Extended_Queue::size() const
/*
post: trả về số phần tử hiện có của hàng. Hàng không đổi.
*/
{
return count;
}
3.4.2. Hiện thực hàng liên kết
3.4.2.1. Các khai báo cơ bản
Với mọi hàng, chúng ta dùng kiểu Entry cho kiểu của dữ liệu lưu trong hàng.
Đối với hiện thực liên kết, chúng ta khai báo các node tương tự như đã làm cho
ngăn xếp liên kết trong chương 2. Chúng ta có đặc tả dưới đây:
template
class Queue {
public:
// Các phương thức chuẩn của hàng
Queue();
bool empty() const;
ErrorCode append(const Entry &item);
ErrorCode serve();
ErrorCode retrieve(Entry &item) const;
// Các phương thức bảo đảm tính an toàn cho hàng liên kết
~Queue();
Queue(const Queue &original);
void operator =(const Queue &original);
protected:
Node *front, *rear;
};
Constructor thứ nhất khởi tạo một hàng rỗng:
template
Queue::Queue()
/*
post: đối tượng hàng đã tồn tại và được khởi tạo là hàng rỗng.
*/
{
front = rear = NULL;
}
Chương 3 – Hàng đợi
Giáo trình Câu trúc dữ liệu và Giải thuật 49
Phương thức append thêm một phần tử vào đầu rear của hàng:
template
ErrorCode Queue::append(const Entry &item)
/*
post: nếu hàng còn chỗ, item được thêm vào tại rear, ErrorCode trả về là success; ngược
lại, ErrorCode trả về là overflow, hàng không đổi.
*/
{
Node *new_rear = new Node (item);
if (new_rear == NULL) return overflow;
if (rear == NULL) front = rear = new_rear; // trường hợp đặc biệt: thêm vào hàng
đang rỗng.
else {
rear->next = new_rear;
rear = new_rear;
}
return success;
}
Trường hợp hàng rỗng cần phân biệt với các trường hợp bình thường khác, do
khi thêm một node vào một hàng rỗng cần phải gán cả front và rear tham
chiếu đến node này, trong khi việc thêm một node vào một hàng không rỗng chỉ
có rear là cần được gán lại.
Phương thức loại một phần tử ra khỏi hàng được viết như sau:
template
ErrorCode Queue::serve()
/*
post: nếu hàng không rỗng, phần tử tại front được lấy đi, ErrorCode trả về là success; ngược
lại, ErrorCode trả về là underflow, hàng không đổi.
*/
{
if (front == NULL) return underflow;
Node *old_front = front;
front = old_front->next;
if (front == NULL) rear= NULL;// trường hợp đặc biệt: loại phần tử cuối cùng của hàng
delete old_front;
return success;
}
Một lần nữa trường hợp hàng rỗng cần được xem xét riêng. Khi phần tử được
loại khỏi hàng không phải là phần tử cuối trong hàng, chỉ có front cần được gán
lại, ngược lại cả front và rear cần phải gán về NULL.
Các phương thức khác của hàng liên kết được dành lại cho phần bài tập.
Chương 3 – Hàng đợi
Giáo trình Câu trúc dữ liệu và Giải thuật 50
Nếu so sánh với hàng liên tục, chúng ta sẽ thấy rằng hàng liên kết dễ hiểu
hơn cả về mặt khái niệm cả về cách hiện thực chương trình.
3.4.3. Hàng liên kết mở rộng
Hiện thực liên kết của lớp Queue cung cấp một lớp cơ sở cho các lớp khác.
Định nghĩa dưới đây dành cho lớp dẫn xuất Extended_Queue hoàn toàn tương tự
như hàng liên tục.
template
class Extended_queue: public Queue {
public:
bool full() const;
int size() const;
void clear();
ErrorCode serve_and_retrieve(Entry &item);
};
Mặc dù lớp Extended_Queue này có hiện thực liên kết, nhưng nó không cần các phương thức
như copy contructor, overloaded assignment, hoặc destructor. Đối với một trong các phương thức
này, trình biên dịch sẽ gọi các phương thức mặc định của lớp Extended_Queue. Phương thức mặc
định của một lớp dẫn xuất sẽ gọi phương thức tương ứng của lớp cơ sở. Chẳng hạn, khi một đối
tượng của lớp Extended_Queue chuẩn bị ra khỏi tầm vực, destructor mặc định của lớp
Extended_Queue sẽ gọi destructor của lớp Queue: mọi node cấp phát động của Extended_Queue
đều được giải phóng. Do lớp Extended_Queue của chúng ta không chứa thêm thuộc tính con trỏ
nào ngoài các thuộc tính con trỏ thừa kế từ lớp Queue, destructor mà trình biên dịch gọi đã làm
được tất cả những điều mà chúng ta mong muốn.
Các phương thức được khai báo cho lớp Extended_Queue cần được viết lại cho
phù hợp với cấu trúc liên kết của hàng. Chẳng hạn, phương thức size cần sử dụng
một con trỏ tạm window để duyệt hàng (nói cách khác, con trỏ window sẽ di
chuyển dọc theo hàng và lần lượt chỉ đến từng node trong hàng).
template
int Extended_queue::size() const
/*
post: trả về số phần tử hiện có của hàng. Hàng không đổi.
*/
{
Node *window = front;
int count = 0;
while (window != NULL) {
window = window->next;
count++;
}
return count;
}
Các phương thức khác của Extended_Queue liên kết xem như bài tập.
File đính kèm:
giao_trinh_cau_truc_du_lieu_va_giai_thuat_chuong_3_hang_doi.pdf
