Professional Documents
Culture Documents
Mở đầu
Đặc điểm của cấu trúc dữ liệu hàng đợi là khi bổ sung được thực hiện vào cuối hay đuôi của hàng đợi
trong khi thao tác lấy ra được thực hiện từ phía trước hay đầu hàng đợi. Ngược lại với đặc điểm này của hàng
đợi đó là stack , một cấu trúc kiểu FIFO ( First–In First-Out - vào trước ra sau )
Sử dụng hàng đợi trong trường hợp tốc độ khách hàng yêu cầu các dịch vụ trong cùng thời điểm vượt
quá tốc độ mà khả năng các dịch vụ này cung ứng được. Ví dụ, trong một mạng chia sẻ có nhiều máy tính nhưng
chỉ có một vài máy in, công việc in ấn có thể gom lại vào một hàng đợi in. Trong một hệ điều hành với giao diện
đồ họa, các ứng dụng và cửa sổ giao tiếp bằng các thông điệp (message) được đặt trong một hàng đợi thông điệp
cho đến khi hệ điều hành có thể xử lí chúng.
Destroy Hủy nội dung của hàng đợi ( có thể thực hiện điều này bằng cách tái khởi tạo hàng đợi )
1/ Initalise
Khởi tạo cấu trúc – đảm bảo rằng cấu trúc tồn tại mà không cần biết có chứa các thuộc tính hay không .
2/ Add
VD: Add(X,Q) thêm nút có giá trị X vào đuôi hàng đợi Q
Sau đó, Add(Y,Q) thêm nút có giá trị Y vào đuôi hàng đợi Q
1
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
3/ Remove
VD: Remove(Q) Hủy nút đầu của hàng đợi Q và trả về giá trị của nút đó
Bảng ví dụ mẫu
Initialise(Q)
Add(A,Q)
Add(F,Q)
Add(X,Q)
Remove(Q)
Add(B,Q)
Remove(Q)
Remove(Q).
2
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
Lưu trữ một hàng đợi vào cấu trúc dữ liệu tĩnh
Thực hiện điều này bằng cách lưu hàng đợi vào một mảng. Các chỉ số mảng lưu trữ đuôi và đầu của hàng đợi
phải luôn được duy trì. Đầu của hàng đợi không nhất thiết lưu tại chỉ số 0. Mảng có thể là “mảng vòng ” –
tương ứng với hàng đợi “bọc quanh” nếu chỉ số cuối của mảng đạt tới - tức là đủ tạo được thành vòng.
Ví dụ: Lưu trữ một hàng đợi vào một mảng có 5 phần tử
3
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
Bài tập: Hàng đợi được lưu vào cấu trúc dữ liệu tĩnh
Tiếp tục ví dụ trên. Hãy vẽ lại trạng thái của hàng đợi sau khi thực hiện các thao tác sau:
Add(E,Q)
Remove(Q)
Add(W,Q)
Add(J,Q)
Add(K,Q)
Lưu trữ một hàng đợi vào cấu trúc dữ liệu động
Trong trường hợp là stack, mỗi nút trong cấu trúc dữ liệu động chứa dữ liệu VÀ tham chiếu đến nút kế tiếp
Hàng đợi cũng cần một tham chiếu đến nút đầu VÀ một tham chiếu đến nút cuối
Hình vẽ dưới đây mô tả việc lưu trữ của một hàng đợi gọi là Queue. Mỗi nút bao gồm dữ liệu (DataItem) và một
tham chiếu (NextNode)
Truy cập nút đầu tiên bằng cách sử dụng tên Queue.Head
Truy cập đến nút thứ hai bằng cách sử dụng Queue.Head.NextNode
Truy cập đến nút cuối cùng bằng cách sử dụng Queue.Tail
4
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
5
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
Thực hiện hàng đợi
Các bộ sưu tập Java Framwork trong các phiên bản mới nhất của Java hiện nay bao gồm các lớp hàng đợi. Như
những gì bạn đã làm với stack, bạn sẽ tạo ra một lớp hàng đợi của riêng bạn để tìm hiểu cách thức một hàng đợi
làm việc. Lớp hàng đợi của bạn sẽ đơn giản hơn một chút so với các bộ sưu tập Framwork nhưng về cơ bản là
như nhau.
Các nút của hàng đợi được biểu diễn bằng các thể hiện của lớp Node. Mỗi thể hiện của lớp Node lưu giữ một
mục dữ liệu (data item), loại đối tượng, và tham chiếu đến nút kế tiếp của nó. Mục dữ liệu có thể là một trong
bất kỳ loại đối tượng nào của Java
Lớp hàng đợi (Queue) có tham chiếu đến 2 nút, nút đầu và nút cuối. Hàm khởi tạo thiết lập các tham chiếu đó
đến NULL để khởi tạo một hàng đợi ban đầu rỗng.
Hàng đợi không có kích cỡ cố định, vì vậy nó sẽ không bao giờ đầy (trừ khi máy tính không còn đủ bộ nhớ ).
Phương thức isFull ở đây sẽ trả về False.
Node.java
/**
* class Node
* @author Jim
* @version 1.0
6
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
*/
Object dataItem;
Node nextNode;
Queue.java
/**
* class Queue
*
* @author Jim
* @version 1.0
*/
public class Queue
{
public Node head;
public Node tail;
/**
* Constructor for objects of class Queue
*/
public Queue()
{
// initialise head and tail references
head = null;
tail = null;
}
/**
* sets all queue entries to null
*
**/
public void destroy()
{
Node temp = new Node();
Node setNull = new Node();
temp = head;
while (temp!=null)
{
setNull = temp;
temp = temp.nextNode;
setNull = null;
}
head = null;
tail = null;
}
7
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
/**
* checks whether queue is empty
*/
public boolean isEmpty()
{
return head == null;
}
/**
* checks whether queue is full - not properly
* implemented here
*/
public boolean isFull()
{
return false;
}
/**
* add an item to the queue
*/
public void add(Object o)
{
Node newNode = new Node();
newNode.dataItem = o;
if (tail == null)
{
head = newNode;
tail = newNode;
}
else
{
tail.nextNode = newNode;
tail = newNode;
}
}
/**
* remove an item by obeying FIFO rule
*/
public Object remove()
{
if (head == null)
return null;
else
{
Node temp = new Node();
temp = head;
head = head.nextNode;
if (head == null) tail = null;
return temp.dataItem;
}
}
8
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
/**
* returns the number of items in the queue
*/
public int size()
{
int count = 0;
for (Node current=head;current!=null;current=current.nextNode)
count++;
return count;
}
}
Hãy nhớ rằng hàng đợi có thể chứa bất kì loại dữ liệu nào. Lớp QueueTester sau đây cho thấy cách sử dụng một
hàng đợi để lưu giữ các đối tượng String. Phần gọi các thao tác hàng đợi được tô đậm
/**
* class QueueTester
*
* @author Jim
* @version 1.0
*/
public class QueueTester {
private Queue queue;
public QueueTester(){
queue = new Queue();
}
public QueueTester(Queue queue){
this.queue = queue;
}
/**
* add item to queue
*/
public void addString(String str) {
queue.add(str);
System.out.println("Added new string");
}
/**
* remove item from queue
*/
public void removeString() {
String result = (String) queue.remove();
if (result!=null)
System.out.println("String is :" + result);
else
System.out.println("Remove was unsuccessful");
}
9
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
/**
* check if queue is empty
*/
public void checkIfEmpty() {
if (queue.isEmpty())
System.out.println("Queue empty");
else
System.out.println("Queue is not empty");
}
/**
* list the strings in queue
*/
public void listStringsInQueue() {
if (queue.isEmpty()) {
System.out.println("Queue empty");
}
else {
System.out.println("Strings in queue are: ");
System.out.println();
Node node = queue.head;
while (node != null){
String item = (String)node.dataItem;
System.out.println(item);
node = node.nextNode;
}
System.out.println();
}
}
}
Tạo một thể hiện mới của lớp QueueTester và chọn thể hiện của lớp Queue bạn vừa tạo làm tham số trong hàm
khởi tạo . Điều này nghĩa là bạn sẽ kiểm tra được hàng đợi bạn đã tạo trong bước trước
Kết quả là gì ?
Gọi phương thức AddString của lớp QueueTester để thêm chuỗi “The” vào hàng đợi. Tiếp tục dùng phương
thức này để thêm các chuỗi sau:
Gọi phương thức removeString của lớp QueueTester và kiểm tra kết quả có chính xác ?
Gọi phương thức AddString của lớp QueueTester để thêm 2 chuỗi “every” và “day” vào hàng đợi
10
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
Nội dung hàng đợi của bạn lúc này sẽ ra sao ?
Duyệt lại đối tượng hàng đợi của bạn. Bạn sẽ thấy tham chiếu đến đầu và đuôi của hàng đợi.
Kiểm tra tham chiếu đến đầu hàng đợi. Một đối đượng Inspector mới xuất hiện
DataItem của nó là gì ?
Click vào tham chiếu NextNode để chọn nó và click vào nút Inspect
Đi qua từng nút của hàng đợi bằng cách sử dụng tham chiếu nextNode mỗi lần đi từ nút này đến nút kế tiếp
Bằng cách nào bạn biết mình đã đi đến nút cuối cùng ?
Có cách nào khác để nhận biết được nút cuối cùng hay không ?
11
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
Hãy cố gắng hủy và thêm một vài nút nữa, kiểm tra lại nội dung hàng đợi sau khi bạn thực hiện mỗi thao tác
Bạn có thể theo dõi thông qua từng nút như trên hoặc bạn có thể gọi phương thức listStringsInQueue của lớp
QueueTester
Hàng đợi thay đổi như thế nào khi bạn hủy một nút ?
Chú ý: Chúng tôi chọn việc thực hiện hàng đợi này bằng cấu trúc động. Chúng ta có thể tạo một cấu trúc hàng
đợi tĩnh bằng cách sử dụng mảng. Phương thức thêm và hủy cũng sẽ có tác dụng tương tự. Tuy nhiên , cách thức
mà dữ liệu thực sự được lưu trữ sẽ tương tự như cấu trúc dữ liệu tĩnh bạn đã tìm hiểu trong chương ngăn xếp.
Sửa lớp QueueTester lưu các đối tượng Double trong hàng đợi thay vì các đối tượng String, kiểm tra lại kết quả
theo các bước tương tự như trên
Bài tập: Một ứng dụng thực tế của lớp hàng đợi
Hàng đợi là một cấu trúc dữ liệu hữu ích để lưu trữ dữ liệu cần xử lí theo trình tự nó được tạo ra, nhưng có thể
không phải lúc nào cũng được xử lí ngay. Một ứng dụng điển hình của hàng đợi đó là hệ thống nhắn tin. Trong
ví dụ sau đây, thông điệp nhận được theo thứ tự chúng được gửi đi
+ Một đối tượng Message có một người gửi, một người nhận, một chuỗi nội dung và ngày tháng
+ Một đối tượng Message được đặt trong một hàng đợi bởi một đối tượng MessageSender
+ Một Message được lấy ra khỏi hàng đợi bởi một đối tượng MessageReceiver có thể hiển thị nội dung của hàng
đợi
Lớp hàng đợi bạn tạo ra chong chương này có thể chứa bất kì loại đối tượng nào , kể cả đối tượng Message, vì
vậy bạn có thể sử dụng nó trong bài tập này
Thêm các lớp sau đây vào project hàng đợi của bạn
12
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
Message.java
import java.text.*;
import java.util.Date;
/**
* class Message
*
* @author Jim
* @version 1.0
*/
public class Message
{
public String sender;
public String recipient;
public String content;
public Date date;
/**
* Constructors for objects of class Message
*/
public Message()
{
this.sender = "unknown sender";
this.recipient = "unknown recipient";
this.content = "none";
this.date = new Date();
}
/**
* returns date/time at which message was created
*
* @return String - formatted representation of date
**/
public String getDate()
{
returnDateFormat.getDateTimeInstance();
format(this.date);
}
}
13
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
MessageSender.java
/**
* class MessageSender
* @author Jim
* @version 1.0
*/
public class MessageSender
{
/**
* places a message on a specified queue
*
*/
public void sendMessage(String sender, String recipient,String content,
Queue q)
{
Message m = new Message(sender, recipient, content);
if(!q.isFull())
{
q.add(m);
System.out.println("Message placed on queue");
}
else
System.out.println("Cannot send - queue is full");
}
}
MessageReceiver.java
14
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
*/
public class MessageReceiver
{
/**
* receives and outputs a message from a specified queue
*
*/
public void receiveMessage(Queue q)
{
Message m = (Message) q.remove();
if (m != null)
{
System.out.println("Date: " + m.getDate());
System.out.println("From: " + m.sender);
System.out.println("To: " + m.recipient);
System.out.println("Content: " + m.content);
}
else
System.out.println("No messages to receive");
}
/**
* outputs contents of a queue
*
*/
public void showQueue(Queue q)
{
Message m;
System.out.println("Queue contains " + q.size() + " messages");
/**
* class MessageReceiver
* @author Jim
* @version 1.0
15
www.itspiritclub.net Cấu trúc dữ liệu Queue Translater: Group ITSPIRIT
if (q.isEmpty()) {
System.out.println("Queue empty");
}
else {
Node node = q.head;
while (node != null){
m = (Message)node.dataItem;
System.out.println(m.getDate() + ", From:" + m.sender + ",
To:" + m.recipient);
node = node.nextNode;
}
}
}
}
Bây giờ bạn cho chạy kiểm tra theo trình tự sau đây:
1. Tạo các thể hiện của lớp MessageSender, MessageReceiver và lớp hàng đợi của bạn.
2. Sử dụng thể hiện của lớp MessageSender để thêm các tin nhắn sau vào hàng đợi:
Sender Recipient Content
Bob Alice Hello
Jane Joe Good Morning
Jack Jill See you later
3. Sử dụng các thể hiện của lớp MessageReceiver để :
+ Hiển thị các nội dung hàng đợi
+ Hủy tin nhắn đầu tiên trong hàng đợi
+ Hiển thị các nội dung hàng đợi một lần nữa
4. Sử dụng các phương thức thích hợp để thêm các tin nhắn sau vào hàng đợi, hủy tin nhắn đầu
tiên và hiển thị các nội dung hàng đợi lần nữa
Sender Recipient Content
George Mildred Good Evening
Diane Sam Bye for now
5. Sử dụng các phương thức thích hợp để hủy tin nhắn đầu tiên và thêm các tin nhắn sau vào hàng
đợi, hiển thị các nội dung hàng đợi lần nữa
16