🏔️ C++ STL • Heap • Priority Queue

PRIORITY QUEUE
HÀNG ĐỢI ƯU TIÊN - HEAP

Bài giảng hệ thống hóa kiến thức priority_queue trong C++: heap max, heap min, comparator, các hàm thường dùng và các bài tập ứng dụng.

top()Lấy phần tử ưu tiên nhất
O(log n)push / pop nhanh
max-heapMặc định trong C++
min-heapDùng greater<T>
1

Khái niệm Priority Queue

Priority queue là gì?

priority_queue là hàng đợi ưu tiên. Mỗi lần lấy phần tử, ta không lấy theo thứ tự vào trước ra trước như queue, mà lấy phần tử có độ ưu tiên cao nhất.

Trong C++, priority_queue được cài đặt bằng cấu trúc heap. Mặc định là max-heap, nghĩa là phần tử lớn nhất nằm ở đỉnh.

pushheap tự sắptop là phần tử ưu tiên nhất

Heap max và heap min

Heap max: phần tử ở đỉnh là lớn nhất. Đây là mặc định của priority_queue<int>.

Heap min: phần tử ở đỉnh là nhỏ nhất. Dùng dạng priority_queue<int, vector<int>, greater<int>>.

Ghi nhớ: cần lấy lớn nhất nhiều lần → heap max. Cần lấy nhỏ nhất nhiều lần → heap min.

So sánh với sort

Nếu chỉ cần sắp xếp toàn bộ dãy một lần, dùng sort() là đơn giản. Nếu dữ liệu được thêm dần và ta cần lấy phần tử lớn nhất/nhỏ nhất liên tục, priority_queue rất phù hợp.

Tình huốngNên dùngLý do
Sắp xếp toàn bộ dãysortDễ viết, O(n log n)
Liên tục thêm và lấy max/minpriority_queuepush/pop O(log n), top O(1)
Tìm k phần tử nhỏ nhất/lớn nhấtpriority_queueKhông cần sinh toàn bộ dữ liệu
Huffman, Dijkstra, KMINpriority_queueMỗi bước cần chọn phần tử ưu tiên nhất
2

Cú pháp khai báo

Heap max mặc định

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

priority_queue<int> pq;
priority_queue<long long> pq2;
priority_queue<double> pq3;

pq.top() luôn trả về phần tử lớn nhất hiện có.

Heap min

priority_queue<int, vector<int>, greater<int>> pq;

// Với pair:
priority_queue<
  pair<int, int>,
  vector<pair<int, int>>,
  greater<pair<int, int>>
> q;

pq.top() trả về phần tử nhỏ nhất.

Comparator tự viết

Khi cần ưu tiên theo quy tắc riêng, ta tạo struct comparator.

struct cmp {
    bool operator()(int a, int b) {
        return a < b;   // ưu tiên số lớn hơn trước
    }
};

priority_queue<int, vector<int>, cmp> pq;
Lưu ý: Comparator của priority_queue hơi ngược cảm giác ban đầu. Nếu cmp(a,b) trả về true thì a có độ ưu tiên thấp hơn b.
3

Các hàm thành viên

HàmÝ nghĩaĐộ phức tạpVí dụ
size()Trả về số lượng phần tử trong heap.O(1)pq.size()
empty()Kiểm tra heap có rỗng không.O(1)pq.empty()
push(x)Thêm phần tử x vào heap.O(log n)pq.push(5)
top()Lấy giá trị ở đỉnh heap.O(1)pq.top()
pop()Xóa phần tử ở đỉnh heap.O(log n)pq.pop()
Chú ý: pop() chỉ xóa phần tử ở đỉnh, không trả về giá trị. Muốn lấy giá trị thì gọi top() trước rồi mới pop().
4

Ví dụ code C++

Ví dụ 1. Heap max

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

int main() {
    priority_queue<int> pq;
    pq.push(3);
    pq.push(5);
    pq.push(1);

    cout << pq.top() << "\n"; // 5
    pq.pop();
    cout << pq.top() << "\n"; // 3
}

Ví dụ 2. Heap min

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

int main() {
    priority_queue<int, vector<int>, greater<int>> pq;
    pq.push(3);
    pq.push(5);
    pq.push(1);

    cout << pq.top() << "\n"; // 1
    pq.pop();
    cout << pq.top() << "\n"; // 3
}

Ví dụ 3. Priority queue với pair

priority_queue<pair<int, int>> pq;

pq.push({5, 1});
pq.push({7, 2});
pq.push({7, 3});

// pair được so sánh theo first trước, nếu bằng thì so sánh second
while (!pq.empty()) {
    auto x = pq.top(); pq.pop();
    cout << x.first << " " << x.second << "\n";
}
5

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

1. Heap sort

Đưa toàn bộ phần tử vào heap rồi lấy dần ra để tạo dãy có thứ tự.

2. Bài toán ghép tối ưu

Luôn ghép hai phần tử nhỏ nhất trước, ví dụ bài cắt gỗ/Huffman.

3. K phần tử nhỏ nhất

Dùng heap để lấy dần k ứng viên tốt nhất mà không cần sinh toàn bộ tổ hợp.

4. Dijkstra

Lấy đỉnh có khoảng cách tạm thời nhỏ nhất bằng min-heap.

5. Mô phỏng sự kiện

Sự kiện nào có thời điểm nhỏ nhất thì xử lý trước.

6. Truy vấn online

Dữ liệu thay đổi liên tục, cần lấy max/min nhanh.

6

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

BàiTên bàiKỹ năng chínhMức độ
1HEAP - Sắp xếp bằng heapmin-heap / max-heapDễ
2QBHEAP - Thao tác heapmax-heap, xóa toàn bộ giá trị lớn nhấtTrung bình
3WOOD - Cắt gỗ tối ưumin-heap, greedyTrung bình
4KMIN - K tổng nhỏ nhấtpriority_queue với pairKhá
7

Nội dung bài tập

Câu 1. HEAP.CPP

HEAP.*

Cho dãy a1, a2, ..., aN. Hãy sử dụng heap để sắp xếp dãy trên thành dãy không giảm rồi in ra dãy sau khi sắp xếp.

Dữ liệu vàoDòng 1: N. Dòng 2: N số nguyên.
Kết quảDãy sau khi sắp xếp không giảm.
Giới hạn1 ≤ N ≤ 105, |a[i]| ≤ 109

HEAP.INP

5
1 4 6 3 2

HEAP.OUT

1 2 3 4 6
Gợi ý: Dùng min-heap rồi lấy dần top(), hoặc dùng max-heap rồi lưu kết quả ngược lại.

Câu 2. QBHEAP.CPP

QBHEAP.*

Cho trước một danh sách rỗng. Có hai thao tác: +V thêm giá trị V vào danh sách; - nếu danh sách không rỗng thì loại bỏ tất cả các phần tử lớn nhất hiện có. Sau khi thực hiện tất cả thao tác, in số lượng phần tử còn lại và các giá trị theo thứ tự giảm dần.

Dữ liệu vàoNhiều dòng, mỗi dòng là một thao tác.
Kết quảDòng 1: số lượng còn lại. Dòng 2: các giá trị giảm dần.
Giới hạnSố thao tác ≤ 106, 1 ≤ V ≤ 103

QBHEAP.INP

+1
+3
+2
+3
-
+4
+4
-
+2
+9
+7
+8
-

QBHEAP.OUT

5
8 7 2 2 1
Gợi ý: Dùng max-heap. Khi gặp -, lấy giá trị lớn nhất mx = top(), sau đó pop tất cả phần tử bằng mx.

Câu 3. WOOD.CPP

WOOD.*

Một người nông dân muốn cắt tấm gỗ thành N miếng có độ dài lần lượt là a1, a2, ..., aN. Để cắt một miếng gỗ dài X thành hai phần thì mất X tiền. Hãy tính chi phí nhỏ nhất để cắt được các miếng gỗ mong muốn.

Dữ liệu vàoT bộ test. Mỗi test gồm N và N độ dài.
Kết quảMỗi test in chi phí tối thiểu.
Giới hạn1 ≤ T ≤ 100, 1 ≤ N ≤ 20000, 1 ≤ a[i] ≤ 50000

WOOD.INP

1
4
1 2 3 4

WOOD.OUT

19
Gợi ý: Đây là bài ghép tối ưu kiểu Huffman. Luôn lấy hai miếng nhỏ nhất ghép trước bằng min-heap.

Câu 4. KMIN.CPP

KMIN.*

Cho hai dãy số nguyên A và B. Với mỗi Ai và Bj, tạo tổng Ai + Bj. Tất cả các tổng này sau khi sắp xếp không giảm tạo thành dãy C. Hãy tìm K số đầu tiên trong dãy C.

Dữ liệu vàoM, N, K; dãy A; dãy B.
Kết quảK dòng, mỗi dòng là một tổng nhỏ nhất tiếp theo.
Giới hạn1 ≤ M, N, K ≤ 5·104; 1 ≤ A[i], B[j] ≤ 109

KMIN.INP

4 4 6
1 2 3 4
2 3 4 5

KMIN.OUT

3
4
4
5
5
5
Gợi ý: Sắp xếp A, B. Đưa các cặp ứng viên vào min-heap theo tổng. Mỗi lần lấy tổng nhỏ nhất rồi sinh ứng viên tiếp theo tương ứng.
style> body { user-select: none; }