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 |
|---|---|---|
Enqueue(x) |
thêm x vào cuối hàng |
O(1) trung bình |
Dequeue() |
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ụ
var orders = new Queue<string>();
orders.Enqueue("DH1");
orders.Enqueue("DH2");
orders.Enqueue("DH3");
Console.WriteLine(orders.Dequeue()); // DH1
Console.WriteLine(orders.Peek()); // DH2
Console.WriteLine(orders.Count); // 2Enqueuexếp đơn vào cuối hàng,Dequeuelấy đơn ở đầu hàng.DH1vào trước nên ra trước.PeekthấyDH2đang đứng đầu.
Bên trong Queue
Tự viết queue bằng List<T> với Add ở cuối và RemoveAt(0) ở đầu thì
Dequeue thành O(n), như bài Array và List bên trong. Queue<T> 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: Queue với array vòng tròn
var orders = new Queue<string>(4);
orders.Enqueue("DH1");
orders.Enqueue("DH2");
orders.Enqueue("DH3");
Console.WriteLine(orders.Dequeue());
Console.WriteLine(orders.Dequeue());
orders.Enqueue("DH4");
orders.Enqueue("DH5");
orders.Enqueue("DH6");
orders.Enqueue("DH7");Xem từng bước
- Enqueue(DH1): đặt vào ô 0 (cuối hàng), cuối sang ô 1. Hàng: DH1.
- Enqueue(DH2): đặt vào ô 1 (cuối hàng), cuối sang ô 2. Hàng: DH1 → DH2.
- Enqueue(DH3): đặt vào ô 2 (cuối hàng), cuối sang ô 3. Hàng: DH1 → DH2 → DH3.
- Dequeue(): lấy DH1 ở ô 0, đầu sang ô 1. Không phần tử nào phải dời: O(1). Hàng: DH2 → DH3.
- Dequeue(): lấy DH2 ở ô 1, đầu sang ô 2. Không phần tử nào phải dời: O(1). Hàng: DH3.
- Enqueue(DH4): đặt vào ô 3 (cuối hàng), hết array nên cuối quay về ô 0. Hàng: DH3 → DH4.
- Enqueue(DH5): đặt vào ô 0 (cuối hàng), cuối sang ô 1. Hàng: DH3 → DH4 → DH5.
- Enqueue(DH6): đặt vào ô 1 (cuối hàng), cuối sang ô 2. Hàng: DH3 → DH4 → DH5 → DH6.
- Array đầy (4/4 ô). Tạo array mới 8 ô (gấp đôi), chép 4 phần tử theo thứ tự hàng về đầu array mới: đầu về ô 0.
- Enqueue(DH7): đặt vào ô 4 (cuối hàng), cuối sang ô 5. Hàng: DH3 → DH4 → DH5 → DH6 → DH7.
Thử ngay
Xử lý đơn trong khi vẫn có đơn mới đến:
var orders = new Queue<string>();
orders.Enqueue("DH1");
orders.Enqueue("DH2");
Console.WriteLine("Đóng gói " + orders.Dequeue());
orders.Enqueue("DH3");
orders.Enqueue("DH4");
Console.WriteLine("Đóng gói " + orders.Dequeue());
while (orders.Count > 0)
{
Console.WriteLine("Còn chờ " + orders.Dequeue());
}Đ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 List<T> làm hàng đợi. RemoveAt(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)
var orders = new List<string> { "DH1", "DH2" };
string next = orders[0];
orders.RemoveAt(0);// ĐÚNG — Dequeue là O(1)
var orders = new Queue<string>();
orders.Enqueue("DH1");
orders.Enqueue("DH2");
string next = orders.Dequeue();Giống Stack<T>, gọi Dequeue khi hàng rỗng sẽ ném
InvalidOperationException (Queue empty.), nên kiểm tra Count trước.
Tóm tắt
- Queue thêm ở cuối, lấy ở đầu: vào trước ra trước (FIFO).
Dequeue,Peeklà O(1) nhờ array vòng tròn,Enqueuelà 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
List<T>vớiRemoveAt(0)làm hàng đợi.
Bình luận
Đang tải bình luận…