C13: Queue, Stack, Deque — Hàng đợi, Ngăn xếp
Bạn sẽ học được: queue, stack, deque, priority_queue — các cấu trúc dữ liệu cơ bản
Yêu cầu: Đã học C11 (Sort & Algorithm)
Thời gian: 45 phút
Stack — Ngăn xếp
Analogies: Stack = Chồng đĩa
flowchart TD
A["push(1)"] --> B["push(2)"]
B --> C["push(3)"]
C --> D["pop() → 3"]
D --> E["pop() → 2"]
E --> F["pop() → 1"]
| Khái niệm |
Analogies |
Ví dụ |
| Stack |
Chồng đĩa |
stack<int> st |
| push |
Đặt đĩa lên |
st.push(5) |
| pop |
Lấy đĩa trên cùng |
st.pop() |
| top |
Xem đĩa trên cùng |
st.top() |
Sử dụng Stack
| stack<int> st;
// Thêm phần tử
st.push(1);
st.push(2);
st.push(3);
// Xem phần tử trên cùng
cout << st.top() << endl; // 3
// Xóa phần tử trên cùng
st.pop();
// Kiểm tra rỗng
if (st.empty()) cout << "Rong";
// Kích thước
cout << st.size() << endl;
|
Ứng dụng: Kiểm tra ngoặc đúng
| bool isValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) return false;
char top = st.top();
st.pop();
if (c == ')' && top != '(') return false;
if (c == ']' && top != '[') return false;
if (c == '}' && top != '{') return false;
}
}
return st.empty();
}
|
Queue — Hàng đợi
Analogies: Queue = Hàng người xếp hàng
flowchart LR
A["push(1)"] --> B["push(2)"]
B --> C["push(3)"]
C --> D["pop() → 1"]
D --> E["pop() → 2"]
E --> F["pop() → 3"]
| Khái niệm |
Analogies |
Ví dụ |
| Queue |
Hàng người xếp hàng |
queue<int> q |
| push |
Người vào cuối hàng |
q.push(5) |
| pop |
Người đầu hàng ra |
q.pop() |
| front |
Xem người đầu hàng |
q.front() |
Sử dụng Queue
| queue<int> q;
// Thêm phần tử
q.push(1);
q.push(2);
q.push(3);
// Xem phần tử đầu
cout << q.front() << endl; // 1
// Xóa phần tử đầu
q.pop();
// Kiểm tra rỗng
if (q.empty()) cout << "Rong";
// Kích thước
cout << q.size() << endl;
|
Ứng dụng: BFS (Breadth-First Search)
| // BFS trên đồ thị
vector<int> adj[100];
bool visited[100];
void bfs(int start) {
queue<int> q;
q.push(start);
visited[start] = true;
while (!q.empty()) {
int u = q.front();
q.pop();
cout << u << " ";
for (int v : adj[u]) {
if (!visited[v]) {
visited[v] = true;
q.push(v);
}
}
}
}
|
Deque — Hàng đợi hai đầu
Analogies: Deque = Hàng người có thể ra/vào 2 đầu
flowchart LR
A["push_front(1)"] --> B["push_back(2)"]
B --> C["push_back(3)"]
C --> D["pop_front() → 1"]
D --> E["pop_back() → 3"]
Sử dụng Deque
| deque<int> dq;
// Thêm vào cuối
dq.push_back(1);
dq.push_back(2);
// Thêm vào đầu
dq.push_front(0);
// Xem đầu và cuối
cout << dq.front() << endl; // 0
cout << dq.back() << endl; // 2
// Xóa đầu
dq.pop_front();
// Xóa cuối
dq.pop_back();
// Truy cập theo chỉ số
cout << dq[0] << endl;
|
Ứng dụng: Sliding Window Maximum
| vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> dq;
vector<int> result;
for (int i = 0; i < nums.size(); i++) {
// Loại bỏ phần tử ngoài cửa sổ
while (!dq.empty() && dq.front() < i - k + 1) {
dq.pop_front();
}
// Loại bỏ phần tử nhỏ hơn nums[i]
while (!dq.empty() && nums[dq.back()] < nums[i]) {
dq.pop_back();
}
dq.push_back(i);
if (i >= k - 1) {
result.push_back(nums[dq.front()]);
}
}
return result;
}
|
Priority Queue — Hàng đợi ưu tiên
Analogies: Priority Queue = Bệnh viện (ai nặng nhất được ưu tiên)
flowchart TD
A["push(3)"] --> B["push(1)"]
B --> C["push(4)"]
C --> D["push(1)"]
D --> E["top() → 4"]
E --> F["pop()"]
F --> G["top() → 3"]
Sử dụng Priority Queue
| // Max-heap (mặc định)
priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(4);
pq.push(1);
cout << pq.top() << endl; // 4 (phần tử lớn nhất)
pq.pop();
cout << pq.top() << endl; // 3
|
Min-heap
| // Min-heap
priority_queue<int, vector<int>, greater<int>> pq;
pq.push(3);
pq.push(1);
pq.push(4);
pq.push(1);
cout << pq.top() << endl; // 1 (phần tử nhỏ nhất)
|
Ứng dụng: Tìm K phần tử lớn nhất
| vector<int> topK(vector<int>& nums, int k) {
priority_queue<int, vector<int>, greater<int>> pq;
for (int x : nums) {
pq.push(x);
if (pq.size() > k) {
pq.pop();
}
}
vector<int> result;
while (!pq.empty()) {
result.push_back(pq.top());
pq.pop();
}
return result;
}
|
So sánh các cấu trúc dữ liệu
| Cấu trúc |
Thêm |
Xóa |
Xem |
Ứng dụng |
| Stack |
O(1) |
O(1) |
O(1) |
Đệ quy, backtrack, DFS |
| Queue |
O(1) |
O(1) |
O(1) |
BFS, hàng đợi |
| Deque |
O(1) |
O(1) |
O(1) |
Sliding window |
| Priority Queue |
O(log n) |
O(log n) |
O(1) |
Top-K, Dijkstra |
Common Mistakes — Lỗi thường gặp
Lỗi 1: Quên kiểm tra rỗng
| stack<int> st;
// ❌ SAI: Không kiểm tra rỗng
cout << st.top() << endl; // Lỗi runtime!
// ✅ ĐÚNG
if (!st.empty()) cout << st.top() << endl;
|
Lỗi 2: Nhầm stack và queue
| // Stack: LIFO (vào sau ra trước)
stack<int> st;
st.push(1); st.push(2); st.push(3);
cout << st.top() << endl; // 3
// Queue: FIFO (vào trước ra trước)
queue<int> q;
q.push(1); q.push(2); q.push(3);
cout << q.front() << endl; // 1
|
Lỗi 3: Nhầm max-heap và min-heap
| // Max-heap (mặc định)
priority_queue<int> maxPQ;
maxPQ.push(1); maxPQ.push(3); maxPQ.push(2);
cout << maxPQ.top() << endl; // 3
// Min-heap
priority_queue<int, vector<int>, greater<int>> minPQ;
minPQ.push(1); minPQ.push(3); minPQ.push(2);
cout << minPQ.top() << endl; // 1
|
Bài tập thực hành
Bài 1: Kiểm tra palindrome bằng stack
Đọc chuỗi s. Kiểm tra s có phải palindrome không (dùng stack).
Lời giải
| #include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
stack<char> st;
for (char c : s) st.push(c);
string rev = "";
while (!st.empty()) {
rev += st.top();
st.pop();
}
if (s == rev) cout << "YES";
else cout << "NO";
return 0;
}
|
Bài 2: Tìm K phần tử lớn nhất
Đọc n số nguyên và số k. Tìm k phần tử lớn nhất.
Lời giải
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
priority_queue<int, vector<int>, greater<int>> pq;
for (int x : a) {
pq.push(x);
if (pq.size() > k) pq.pop();
}
vector<int> result;
while (!pq.empty()) {
result.push_back(pq.top());
pq.pop();
}
reverse(result.begin(), result.end());
for (int x : result) cout << x << " ";
return 0;
}
|
Tóm tắt bài học
| Nội dung |
Chi tiết |
| Stack |
LIFO — vào sau ra trước |
| Queue |
FIFO — vào trước ra trước |
| Deque |
Hai đầu — có thể thêm/xóa 2 đầu |
| Priority Queue |
Hàng đợi ưu tiên — phần tử lớn nhất ra trước |
Bài viết liên quan
Bài trước: C12: Set & Map
Bài tiếp theo: C14: Algorithm nâng cao →