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.
▶ Mô phỏng: ArrayDeque với array vòng tròn
var orders = new ArrayDeque<String>(4);
orders.offer("DH1");
orders.offer("DH2");
orders.offer("DH3");
System.out.println(orders.poll());
System.out.println(orders.poll());
orders.offer("DH4");
orders.offer("DH5");
orders.offer("DH6");
orders.offer("DH7");Xem từng bước
- offer(DH1): đặt vào ô 0 (cuối hàng), cuối sang ô 1. Hàng: DH1.
- offer(DH2): đặt vào ô 1 (cuối hàng), cuối sang ô 2. Hàng: DH1 → DH2.
- offer(DH3): đặt vào ô 2 (cuối hàng), cuối sang ô 3. Hàng: DH1 → DH2 → DH3.
- poll(): lấy DH1 ở ô 0, đầu sang ô 1. Không phần tử nào phải dời: O(1). Hàng: DH2 → DH3.
- poll(): lấy DH2 ở ô 1, đầu sang ô 2. Không phần tử nào phải dời: O(1). Hàng: DH3.
- offer(DH4): đặt vào ô 3 (cuối hàng), cuối sang ô 4. Hàng: DH3 → DH4.
- offer(DH5): đặt vào ô 4 (cuối hàng), hết array nên cuối quay về ô 0. Hàng: DH3 → DH4 → DH5.
- offer(DH6): đặt vào ô 0 (cuối hàng), cuối sang ô 1. Hàng: DH3 → DH4 → DH5 → DH6.
- offer(DH7): đặt vào ô 1 (cuối hàng), cuối sang ô 2. Hàng: DH3 → DH4 → DH5 → DH6 → DH7.
- Đặt xong thì đầu trùng cuối: array đầy 5/5 ô. Nới thành 12 ô (thêm 5 + 2), chép sang rồi dời 3 phần tử từ đầu hàng ra cuối array mới, đầu sang ô 9.
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.
Bình luận
Đang tải bình luận…