www.itspiritclub.

net

Cấu trúc dữ liệu Queue

Translater: Group ITSPIRIT

Cấu trúc dữ liệu Queue (hàng đợi)
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.

Các thao tác
Add Remove thêm vào một nút mới hủy bỏ một nút

Các hàm bổ sung IsEmpty IsFull Initalise Destroy Kiểm tra hàng đợi có rỗng Kiểm tra hàng đợi có đầy Khởi tạo hàng đợi 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 . VD: Initalise(Q) tạo một hàng đợi rỗng đặt tên là Q

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

Bài tập: Các thao tác hàng đợi
Hãy cho biết nội dung của hàng đợi sau khi thực hiện các thao tác sau ? 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 dữ liệu của nó bằng cách sử dụng Queue.Head.DataItem 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

Thêm một nút (Add)
Nút mới được thêm vào đuôi của hàng đợi. Tham chiếu Queue.Tail sẽ trỏ đến nút mới và tham chiếu NextNode của nút trước đó của đuôi hàng đợi sẽ trỏ đến DataItem của nút mới. 4

www.itspiritclub.net

Cấu trúc dữ liệu Queue

Translater: Group ITSPIRIT

Hủy một nút (Remove)
Trả về giá trị của Queue.Head.DataItem. Một tham chiếu tạm thời Temp được khai báo và trỏ đến nút đầu hàng đợi ( Temp = Queue.Head). Queue.Head sau đó được trỏ đến nút thứ hai thay vì nút đầu tiên. Lúc này tham chiếu đến nút đầu giờ là Temp không sử dụng nữa có thể giải phóng nó khỏi bộ nhớ.

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.

Lớp hàng đợi

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 */ public class Node { Object dataItem; Node nextNode; }

Cấu trúc dữ liệu Queue

Translater: Group ITSPIRIT

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; } }

Sử dụng một hàng đợi
Để sử dụng lớp Queue, bạn cần phải biết làm thế nào để viết code gọi các thao tác trên hàng đợi. 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(); } } }

Bài tập: Sử dụng hàng đợi
Tạo một project mới đặt tên là BlueJ, tạo các lớp Node, Queue và QueueTester bằng cách sử dụng các đoạn code trên Tạo một thể hiện mới của lớp Queue 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 Gọi phương thức checkIfEmpty của lớp QueueTester 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: “queue”, “gets”, “longer” 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 Nội dung hàng đợi của bạn lúc này sẽ ra sao ?

Translater: Group ITSPIRIT

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 Kiểu đối tượng của đầu hàng đợi là gì ? DataItem của nó là gì ?

Click vào tham chiếu NextNode để chọn nó và click vào nút Inspect Bạn thấy những gì ? Đ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ạn có thấy dữ liệu theo thứ tự bạn mong đợi ? 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 Các nút mà bạn thêm vào sẽ ở đâu ? Các nút mà bạn hủy đi sẽ ở đâu ? 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.

Bài tập: Lưu trữ kiểu dữ liệu khác
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 Các lớp liên quan là Message, MessageSender và MessageReceiver: + 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 Message.java
import java.text.*; import java.util.Date;

Cấu trúc dữ liệu Queue

Translater: Group ITSPIRIT

/** * 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(); } public Message(String sender, String recipient, String content) { this.sender = sender; this.recipient = recipient; this.content = content; 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 MessageSender.java

Cấu trúc dữ liệu Queue

Translater: Group ITSPIRIT

/** * 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
*/ public class MessageReceiver {

Cấu trúc dữ liệu Queue

Translater: Group ITSPIRIT

/** * 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:" + To:" + m.recipient); node = node.nextNode; } } } }

m.sender +

",

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