Bài 15.2: Bài tập thực hành Iterator và Pair trong C++

Bài 15.2: Bài tập thực hành Iterator và Pair trong C++
Chào mừng các bạn quay trở lại với loạt bài viết về C++ của FullhouseDev!
Sau khi đã làm quen với các khái niệm lý thuyết về Iterator và Pair trong những bài học trước, đây là lúc chúng ta xắn tay áo lên và đi sâu vào thực hành! Việc luyện tập thông qua các bài tập cụ thể là cách tốt nhất để củng cố kiến thức và hiểu rõ hơn về cách áp dụng chúng trong lập trình C++.
Bài viết này sẽ tập trung hoàn toàn vào các bài tập thực hành với Iterator, Pair, và cả khi kết hợp cả hai. Hãy cùng bắt đầu nào!
Ôn lại nhanh: Iterator là gì?
Trước khi nhảy vào bài tập, hãy cùng ôn lại một chút về Iterator.
- Iterator (Bộ lặp) là một khái niệm cực kỳ quan trọng trong C++, đặc biệt là khi làm việc với các Standard Template Library (STL) containers như
vector
,list
,map
,set
, v.v. - Nó hoạt động giống như một con trỏ thông minh, cho phép chúng ta điều hướng qua các phần tử trong một container mà không cần quan tâm đến cấu trúc lưu trữ bên trong của container đó.
- Mỗi container trong STL đều cung cấp các phương thức để lấy iterator, phổ biến nhất là
begin()
(trả về iterator trỏ tới phần tử đầu tiên) vàend()
(trả về iterator sau phần tử cuối cùng).
Ôn lại nhanh: Pair là gì?
Và không thể quên pair
!
- pair là một cấu trúc đơn giản cho phép gom nhóm hai giá trị (có thể khác kiểu dữ liệu) thành một đơn vị duy nhất.
- Nó rất hữu ích khi bạn cần trả về hoặc lưu trữ một cặp thông tin liên quan với nhau.
- Các thành phần của pair có thể được truy cập thông qua các thành viên công cộng
.first
và.second
.
Giờ thì, lý thuyết đã đủ rồi. Bắt tay vào code ngay thôi!
Bài tập thực hành với Iterator
Các bài tập sau đây sẽ giúp bạn làm quen với việc sử dụng iterator để duyệt và thao tác với các container.
Bài tập 1: Duyệt qua Vector bằng Iterator
Yêu cầu: Duyệt qua tất cả các phần tử của một vector<int>
và in chúng ra màn hình sử dụng iterator.
Code:
#include <iostream>
#include <vector>
int main() {
vector<int> numbers = {10, 20, 30, 40, 50};
cout << "Phan tu trong vector: ";
// Khai bao iterator, bat dau tu phan tu dau tien (numbers.begin())
for (auto it = numbers.begin(); // it bat dau tu begin()
it != numbers.end(); // vong lap dung lai khi it tro toi end()
++it) // di chuyen iterator sang phan tu ke tiep
{
// Truy cap gia tri ma iterator dang tro toi dung toan tu *
cout << *it << " ";
}
cout << endl;
return 0;
}
Giải thích:
- Chúng ta khai báo một iterator
it
và khởi tạo nó vớinumbers.begin()
, trỏ đến phần tử đầu tiên (10
). - Vòng lặp tiếp tục miễn là
it
chưa trỏ tới vị trínumbers.end()
(vị trí sau phần tử cuối cùng). ++it
di chuyển iterator đến phần tử tiếp theo.*it
giải tham chiếu iterator, cho phép chúng ta truy cập vào giá trị của phần tử mà iterator đang trỏ tới.
Bài tập 2: Tìm kiếm phần tử trong Vector bằng Iterator
Yêu cầu: Tìm một giá trị cụ thể (ví dụ: 30) trong vector<int>
sử dụng iterator và in ra thông báo nếu tìm thấy hoặc không tìm thấy.
Code:
#include <iostream>
#include <vector>
#include <algorithm> // Can find
int main() {
vector<int> numbers = {10, 20, 30, 40, 50};
int value_to_find = 30;
// find tra ve iterator toi phan tu dau tien tim thay,
// hoac numbers.end() neu khong tim thay
auto it = find(numbers.begin(), numbers.end(), value_to_find);
// Kiem tra xem iterator co phai la numbers.end() khong
if (it != numbers.end()) {
cout << "Tim thay " << value_to_find << " trong vector." << endl;
// Neu muon biet vi tri (index), co the dung distance
cout << "Tai vi tri (index): " << distance(numbers.begin(), it) << endl;
} else {
cout << value_to_find << " khong tim thay trong vector." << endl;
}
return 0;
}
Giải thích:
- Hàm
find
(trong<algorithm>
) nhận vào hai iterator defining phạm vi tìm kiếm (begin()
vàend()
) và giá trị cần tìm. - Nó trả về một iterator trỏ đến vị trí của phần tử đầu tiên tìm thấy.
- Nếu không tìm thấy, nó trả về iterator
numbers.end()
. - Chúng ta so sánh iterator trả về với
numbers.end()
để xác định xem phần tử có tồn tại hay không. distance
có thể tính khoảng cách giữa hai iterator, hữu ích để xác định chỉ mục (index) trong các container có chỉ mục như vector.
Bài tập 3: Duyệt ngược Vector bằng Reverse Iterator
Yêu cầu: Duyệt qua vector<int>
theo chiều ngược lại và in các phần tử sử dụng reverse iterator.
Code:
#include <iostream>
#include <vector>
int main() {
vector<int> numbers = {10, 20, 30, 40, 50};
cout << "Phan tu trong vector (duyet nguoc): ";
// Reverse iterator bat dau tu rbegin() va ket thuc tai rend()
for (auto it = numbers.rbegin(); // rbegin() tro toi phan tu cuoi cung
it != numbers.rend(); // vong lap dung lai khi it tro toi rend()
++it) // ++it di chuyen nguoc ve phia dau vector
{
// Truy cap gia tri
cout << *it << " ";
}
cout << endl;
return 0;
}
Giải thích:
- Các container STL cung cấp reverse iterator thông qua
rbegin()
vàrend()
. rbegin()
trả về một reverse iterator trỏ đến phần tử cuối cùng.rend()
trả về một reverse iterator trỏ đến vị trí trước phần tử đầu tiên (tương tự nhưend()
đối với forward iterator).- Điều đặc biệt là toán tử
++
khi dùng với reverse iterator lại khiến nó di chuyển ngược về phía đầu vector (từ phần tử cuối cùng về phần tử đầu tiên).
Bài tập thực hành với Pair
Các bài tập sau sẽ giúp bạn làm quen với việc tạo, truy cập và sử dụng pair
.
Bài tập 4: Tạo và truy cập Pair cơ bản
Yêu cầu: Tạo một pair
chứa một int
(số thứ tự) và một string
(tên), sau đó in ra các giá trị này.
Code:
#include <iostream>
#include <utility> // Can pair
#include <string>
int main() {
// Tao mot pair su dung constructor
pair<int, string> student1(101, "Alice");
// Tao mot pair su dung make_pair (thuong dung hon vi ngan gon)
auto student2 = make_pair(102, "Bob");
cout << "Thong tin hoc sinh 1:" << endl;
cout << " ID: " << student1.first << endl; // Truy cap phan tu thu nhat
cout << " Ten: " << student1.second << endl; // Truy cap phan tu thu hai
cout << "\nThong tin hoc sinh 2:" << endl;
cout << " ID: " << student2.first << endl;
cout << " Ten: " << student2.second << endl;
return 0;
}
Giải thích:
- Chúng ta có thể tạo
pair
bằng cách gọi constructor trực tiếp (như vớistudent1
) hoặc sử dụng hàmmake_pair
(thường tiện lợi hơn vì trình biên dịch có thể suy luận kiểu dữ liệu). student1.first
vàstudent1.second
là cách để truy cập vào hai giá trị được lưu trữ bên trong pair.
Bài tập 5: Sử dụng Pair trong Vector
Yêu cầu: Tạo một vector
chứa các pair<int, string>
, sau đó duyệt qua vector này và in ra từng cặp giá trị.
Code:
#include <iostream>
#include <vector>
#include <utility> // Can pair
#include <string>
int main() {
// Tao mot vector chua cac pair
vector<pair<int, string>> product_list = {
{10, "Laptop"},
{20, "Mouse"},
{30, "Keyboard"}
};
cout << "Danh sach san pham:" << endl;
// Duyet qua vector su dung range-based for loop (don gian hon iterator trong truong hop nay)
for (const auto& product_pair : product_list) {
// Moi phan tu trong vector bay gio la mot pair
cout << " ID: " << product_pair.first << ", Ten: " << product_pair.second << endl;
}
return 0;
}
Giải thích:
- Chúng ta khai báo một
vector
mà mỗi phần tử của nó là mộtpair<int, string>
. - Trong ví dụ này, chúng ta sử dụng range-based for loop (
for (const auto& ...)
), đây là một cách duyệt container hiện đại và thường gọn gàng hơn so với iterator thông thường khi bạn chỉ cần truy cập (không sửa đổi) các phần tử. - Bên trong vòng lặp,
product_pair
là một tham chiếu đến một pair trong vector, và chúng ta truy cập các giá trị bên trong pair bằng.first
và.second
.
Kết hợp Iterator và Pair
Đây là lúc mọi thứ trở nên thú vị hơn! Pair và Iterator thường làm việc rất chặt chẽ với nhau, đặc biệt là trong các container như map
.
Bài tập 6: Duyệt Map bằng Iterator (Map lưu trữ Pair)
Yêu cầu: Duyệt qua một map<string, int>
(lưu trữ tên và tuổi) sử dụng iterator và in ra từng cặp key-value. (Lưu ý: Các phần tử trong map
được lưu trữ dưới dạng pair<const Key, Value>
).
Code:
#include <iostream>
#include <map>
#include <string>
#include <utility> // Can pair (mac du map da bao gom)
int main() {
// map luu tru cac cap (key, value) duoi dang pair<const Key, Value>
map<string, int> ages = {
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35}
};
cout << "Danh sach ten va tuoi:" << endl;
// Duyet map bang iterator
for (auto it = ages.begin(); // it tro toi phan tu dau tien
it != ages.end(); // vong lap ket thuc
++it) // di chuyen den phan tu ke tiep
{
// Iterator cua map tro toi mot pair<const string, int>
// it->first la key (ten)
// it->second la value (tuoi)
cout << " Ten: " << it->first << ", Tuoi: " << it->second << endl;
// Hoac dung toan tu * roi moi den .first/.second: (*it).first, (*it).second
// cout << " Ten: " << (*it).first << ", Tuoi: " << (*it).second << endl;
}
return 0;
}
Giải thích:
map
là một container lưu trữ các cặp key-value đã được sắp xếp. Bên trong, mỗi phần tử được lưu trữ dưới dạng mộtpair
, với key là phần tử.first
(và nó làconst
), và value là phần tử.second
.- Khi sử dụng iterator với
map
, iterator sẽ trỏ đến từngpair
này. - Chúng ta dùng toán tử mũi tên
->
(hoặc giải tham chiếu*
rồi dùng dấu chấm.
) để truy cập.first
(key) và.second
(value) của pair mà iterator đang trỏ tới.
Bài tập 7: Tìm và Sửa giá trị trong Vector of Pairs bằng Iterator
Yêu cầu: Tìm một pair trong vector<pair<int, string>>
dựa trên giá trị .first
(ID) sử dụng iterator, và sau đó sửa đổi giá trị .second
(Tên) của pair đó.
Code:
#include <iostream>
#include <vector>
#include <utility> // Can pair
#include <string>
#include <algorithm> // Can find_if
int main() {
vector<pair<int, string>> product_list = {
{10, "Laptop"},
{20, "Mouse"},
{30, "Keyboard"}
};
int id_to_find = 20;
string new_name = "Wireless Mouse";
cout << "Vector ban dau:" << endl;
for (const auto& p : product_list) {
cout << " (" << p.first << ", " << p.second << ")" << endl;
}
// Tim pair co first element la id_to_find su dung find_if
// find_if nhan vao pham vi va mot ham dieu kien (lambda function o day)
auto it = find_if(product_list.begin(), product_list.end(),
[id_to_find](const pair<int, string>& p){
return p.first == id_to_find; // Ham dieu kien: pair co first == id_to_find?
});
// Kiem tra xem pair co duoc tim thay khong
if (it != product_list.end()) {
cout << "\nTim thay san pham co ID " << id_to_find << ". Dang cap nhat..." << endl;
// it dang tro toi pair can cap nhat. Sua doi phan tu .second
it->second = new_name;
cout << "Da cap nhat ten moi: " << it->second << endl;
} else {
cout << "\nKhong tim thay san pham co ID " << id_to_find << "." << endl;
}
cout << "\nVector sau khi cap nhat:" << endl;
for (const auto& p : product_list) {
cout << " (" << p.first << ", " << p.second << ")" << endl;
}
return 0;
}
Giải thích:
- Chúng ta sử dụng
find_if
để tìm kiếm. Hàm này linh hoạt hơnfind
vì nó cho phép chúng ta cung cấp một hàm điều kiện tùy chỉnh. - Hàm điều kiện ở đây là một lambda function
[id_to_find](const pair<int, string>& p){ return p.first == id_to_find; }
. Lambda này nhận một pair (tham chiếu hằng để hiệu quả) và trả vềtrue
nếu phần.first
của pair đó khớp vớiid_to_find
. find_if
sẽ trả về iterator trỏ đến pair đầu tiên thỏa mãn điều kiện, hoặcproduct_list.end()
nếu không tìm thấy.- Nếu tìm thấy (iterator khác
end()
), chúng ta sử dụngit->second = new_name;
để trực tiếp sửa đổi phần tử.second
của pair thông qua iterator.
Các bài tập này chỉ là điểm khởi đầu. Hãy thử tự mình nghĩ ra thêm các kịch bản khác:
- Sử dụng iterator để chèn hoặc xóa phần tử (cẩn thận với invalidation!).
- Sử dụng iterator với các container khác như
list
hoặcset
. - Sắp xếp một vector chứa các pair dựa trên
.first
hoặc.second
. - Sử dụng pair để trả về nhiều giá trị từ một hàm.
Comments