Queue
Nội dung bài · 6 mục
Đơn hàng đến trước phải được đóng gói trước. Stack lấy phần tử mới nhất, nên dùng cho đơn hàng thì khách đặt sớm nhất lại chờ lâu nhất. Queue làm ngược lại: ai đến trước được phục vụ trước.
Khái niệm
🎟️ Queue (hàng đợi): danh sách thêm vào ở cuối và lấy ra ở đầu, phần tử vào trước được lấy ra trước (FIFO: first in, first out).
| Thao tác | Việc làm | Big-O |
|---|---|---|
offer(x) |
thêm x vào cuối hàng |
O(1) trung bình |
poll() |
lấy và bỏ phần tử ở đầu hàng | O(1) |
peek() |
xem phần tử ở đầu hàng, không bỏ | O(1) |
Ví dụ
// Main.java
void main() {
var orders = new ArrayDeque<String>();
orders.offer("DH1");
orders.offer("DH2");
orders.offer("DH3");
System.out.println(orders.poll()); // DH1
System.out.println(orders.peek()); // DH2
System.out.println(orders.size()); // 2
}offerxếp đơn vào cuối hàng,polllấy đơn ở đầu hàng.DH1vào trước nên ra trước.peekthấyDH2đang đứng đầu.- Cùng một
ArrayDequedùng được cho cả stack lẫn queue, tuỳ gọi method nào.
Bên trong Queue
Tự viết queue bằng ArrayList với add ở cuối và remove(0) ở đầu thì
poll thành O(n), như bài Array và List bên trong. ArrayDeque không dời
chỗ mà dùng array vòng tròn: giữ hai chỉ số đầu và cuối, lấy ra chỉ là tăng
chỉ số đầu. Hết ô ở cuối array thì quay lại dùng các ô trống ở đầu.
Thử ngay
Xử lý đơn trong khi vẫn có đơn mới đến:
// Main.java
void main() {
var orders = new ArrayDeque<String>();
orders.offer("DH1");
orders.offer("DH2");
System.out.println("Đóng gói " + orders.poll());
orders.offer("DH3");
orders.offer("DH4");
System.out.println("Đóng gói " + orders.poll());
while (!orders.isEmpty()) {
System.out.println("Còn chờ " + orders.poll());
}
}Đoán trước khi chạy: thứ tự các đơn được in ra là gì?
Xem kết quả
Đóng gói DH1
Đóng gói DH2
Còn chờ DH3
Còn chờ DH4Đơn mới đến lúc nào cũng xếp cuối hàng, nên không đơn nào chen lên trước đơn đến sớm hơn.
Lỗi hay gặp
Dùng ArrayList làm hàng đợi. remove(0) dời cả list mỗi lần lấy đơn,
hàng dài thì chậm hẳn.
// SAI — mỗi lần lấy đơn là O(n)
List<String> orders = new ArrayList<>();
orders.add("DH1");
orders.add("DH2");
String next = orders.remove(0);// ĐÚNG — poll là O(1)
var orders = new ArrayDeque<String>();
orders.offer("DH1");
orders.offer("DH2");
String next = orders.poll();Khác pop của stack, poll khi hàng rỗng không ném exception mà trả về
null, nên kiểm tra isEmpty() trước khi lấy.
Tóm tắt
- Queue thêm ở cuối, lấy ở đầu: vào trước ra trước (FIFO).
poll,peeklà O(1) nhờ array vòng tròn,offerlà O(1) trung bình.- Dùng cho đơn hàng chờ xử lý, và cho tìm kiếm theo chiều rộng (BFS) ở chương 5.
- Đừng dùng
ArrayListvớiremove(0)làm hàng đợi.
Tự kiểm tra
0/3 câuoffer lần lượt A, B, C rồi poll một lần. peek lúc này trả về gì?
Tổng đài xử lý cuộc gọi theo thứ tự gọi đến. Nên dùng cấu trúc nào?
Vì sao poll của ArrayDeque là O(1) còn remove(0) của ArrayList là O(n)?