↔ C++ STL • deque

DEQUE
Hàng đợi hai đầu

Deque là cấu trúc dữ liệu cho phép thêm, xóa phần tử ở cả đầu và cuối. Đây là công cụ rất mạnh để xử lý hàng đợi linh hoạt, cửa sổ trượt, truy vấn min/max đoạn liên tiếp và một số bài toán tối ưu.

push_frontThêm vào đầu
push_backThêm vào cuối
O(1)Truy cập hai đầu
Sliding WindowCửa sổ trượt
1

Khái niệm Deque

Deque là gì?

Deque là viết tắt của double-ended queue, nghĩa là hàng đợi hai đầu. Các phần tử được sắp thành một dãy có đầucuối.

Khác với queue chỉ thêm ở cuối và lấy ở đầu, deque cho phép thêm/xóa ở cả hai phía.

frontcác phần tửback

Cú pháp khai báo

Deque nằm trong thư viện chuẩn C++ và thường dùng chung với #include <bits/stdc++.h>.

#include <bits/stdc++.h>
using namespace std;

int main(){
    deque<int> d;
    return 0;
}
Ghi nhớ: dùng deque khi cần thao tác linh hoạt ở cả đầu và cuối dãy.
2

Một số hàm thành viên

Hàm / thao tácÝ nghĩaĐộ phức tạp
size()Trả về số lượng phần tử hiện có trong deque.O(1)
empty()Trả về true nếu deque rỗng, ngược lại false.O(1)
d[i], at(i)Truy cập phần tử ở vị trí i, đánh số từ 0.O(1)
push_back(x)Thêm x vào cuối deque.O(1)
push_front(x)Thêm x vào đầu deque.O(1)
pop_back()Xóa phần tử cuối deque.O(1)
pop_front()Xóa phần tử đầu deque.O(1)
front()Lấy giá trị phần tử đầu.O(1)
back()Lấy giá trị phần tử cuối.O(1)
insert(iterator,x)Chèn x vào trước vị trí iterator.O(n)
erase(l,r)Xóa các phần tử từ iterator l đến trước iterator r.O(n)
clear()Xóa toàn bộ phần tử.O(n)
swap()Hoán đổi hai deque.O(n)
Lưu ý: trước khi gọi front(), back(), pop_front(), pop_back(), nên kiểm tra deque có rỗng không.
3

Ví dụ code minh họa

Đoạn chương trình sau minh họa cách thêm, xóa và truy cập phần tử trong deque.

#include <bits/stdc++.h>
using namespace std;

int main(){
    deque<int> d;

    for(int i = 1; i <= 5; i++)
        d.push_back(i);       // d = {1, 2, 3, 4, 5}

    d.push_front(7);          // d = {7, 1, 2, 3, 4, 5}

    cout << d.size() << '\n'; // 6
    cout << d.back() << '\n'; // 5
    cout << d[4] << '\n';    // 4

    d.insert(d.begin(), 1);   // d = {1, 7, 1, 2, 3, 4, 5}
    cout << d.front() << '\n';// 1

    d.erase(d.begin() + 1, d.begin() + 5);
    // xóa vị trí 1 đến 4, d = {1, 4, 5}

    d.clear();
    cout << d.empty();        // 1
    return 0;
}
4

Ứng dụng thường gặp

1. Tách chẵn lẻ

Dùng push_front để đảo thứ tự một nhóm và push_back để giữ thứ tự nhóm còn lại.

2. Cửa sổ trượt

Dùng deque lưu chỉ số ứng viên để tìm min/max trong mỗi đoạn dài k với độ phức tạp O(n).

3. Quy hoạch động tối ưu

Một số bài DP có điều kiện đoạn gần nhất có thể dùng deque để lấy giá trị tốt nhất trong cửa sổ.

5

Bảng tổng quan bài tập

CâuTên bàiKỹ năng chínhMức độ
1CHANLEpush_front, push_backDễ
2MINRANGEDeque đơn điệu tìm min cửa sổTrung bình
3RONGTHANDP, tối ưu không chọn K liên tiếpKhá
4HAIHOAHai đoạn rời nhau, cửa sổ hợp lệKhá
5DAYHOCDeque / tối ưu vòng trònKhó
6

Nội dung bài tập

Câu 1. Chẵn lẻ

CHANLE.CPP

Cho một dãy số. Hãy in ra tất cả các số lẻ trước, sau đó đến tất cả các số chẵn trên cùng một dòng. Các số lẻ phải ở thứ tự ngược lại so với dãy ban đầu, còn các số chẵn vẫn giữ nguyên thứ tự.

Dữ liệu vàoN và N số nguyên dương.
Kết quảMột dòng là dãy sau khi sắp xếp theo yêu cầu.
Giới hạn1 ≤ N ≤ 105

CHANLE.INP

6
1 4 6 7 4 6

CHANLE.OUT

7 1 4 6 4 6
Gợi ý: nếu số lẻ thì push_front, nếu số chẵn thì push_back.

Câu 2. Giá trị nhỏ nhất trong mỗi đoạn

MINRANGE.CPP

Cho dãy A gồm N phần tử và số nguyên dương k. Với mỗi vị trí i từ k đến N, hãy tìm giá trị nhỏ nhất trong đoạn từ i-k+1 đến i.

Dữ liệu vàoN, k và dãy A.
Kết quảN-k+1 dòng, mỗi dòng là min của một cửa sổ.
Giới hạnN ≤ 105, A[i] ≤ 109

MINRANGE.INP

8 4
1 3 5 7 4 5 9 5

MINRANGE.OUT

1
3
4
4
4
Gợi ý: dùng deque lưu chỉ số sao cho giá trị trong deque tăng dần. Phần tử đầu deque là min của cửa sổ hiện tại.

Câu 3. Rồng thần

RONGTHAN.CPP

Rồng thần có thể phun lửa tối đa N lần, sát thương lần i là A[i]. Tuy nhiên, rồng không thể phun lửa K lần chí mạng liên tiếp. Hãy tính tổng sát thương lớn nhất có thể gây ra.

Dữ liệu vàoN, K và N số A[i].
Kết quảTổng sát thương lớn nhất.
Giới hạnN ≤ 105, 2 ≤ K ≤ 105

RONGTHAN.INP

7 3
1 4 2 3 6 5 9

RONGTHAN.OUT

23
Gợi ý: Có thể quy về chọn bỏ một số vị trí sao cho không có K vị trí liên tiếp đều được chọn. Dùng DP và tối ưu bằng deque cho cửa sổ.

Câu 4. Hai khóm hoa

HAIHOA.CPP

Cho luống hoa gồm N bông theo thứ tự, mỗi bông có độ xinh đẹp. Chọn hai khóm hoa rời nhau, mỗi khóm gồm các bông liên tiếp, sao cho trong mỗi khóm chênh lệch độ xinh đẹp giữa hai bông bất kỳ không quá K. Hãy chọn được nhiều bông nhất.

Dữ liệu vàoN, K và N dòng giá trị độ xinh đẹp.
Kết quảSố bông hoa lớn nhất có thể chọn.
Giới hạnN ≤ 105, K ≤ 103

HAIHOA.INP

5 2
1
3
2
5
4

HAIHOA.OUT

5
Gợi ý: Với mỗi đầu/phải cửa sổ, dùng hai deque để duy trì min và max, đảm bảo max-min ≤ K. Sau đó kết hợp hai đoạn rời nhau bằng mảng prefix/suffix.

Câu 5. Dạy học quanh bàn tròn

DAYHOC.CPP

Lớp có N học sinh ngồi quanh bàn tròn. Thầy chọn một học sinh bắt đầu, sau đó đi theo chiều kim đồng hồ, mỗi bạn được hướng dẫn đúng Δ giây. Học sinh thứ i sau khi được hướng dẫn cần thêm A[i] giây để viết xong chương trình. Hãy chọn vị trí bắt đầu để thời gian tất cả hoàn thành là nhỏ nhất.

Dãy A không nhập trực tiếp mà được xác định bởi công thức: A[i] = (p * i) mod m + q.

Dữ liệu vàon, Δ; sau đó p, q, m.
Kết quảThời gian nhỏ nhất để tất cả học sinh hoàn thành.
Giới hạnn ≤ 5·106, Δ ≤ 109

DAYHOC.INP

5 3
2 1 9

DAYHOC.OUT

18
Gợi ý: Nhân đôi dãy để xử lý vòng tròn. Với mỗi vị trí bắt đầu, thời gian hoàn thành là max của dạng A[j] + thứ_tự_hướng_dẫn·Δ trong một cửa sổ dài n. Có thể dùng deque để lấy max cửa sổ hiệu quả.
style> body { user-select: none; }