Bài tập C++: hàm, mảng/vector, con trỏ
Luyện cách chia chương trình thành hàm, làm việc với danh sách phần tử, và dùng con trỏ/tham chiếu. Đây là phần bản lề trước khi sang OOP.
Bài 1: hàm kiểm tra số nguyên tố
Viết hàm bool nguyenTo(int n) trả về true nếu n là số nguyên tố.
Gợi ý
Số n < 2 không nguyên tố. Duyệt i từ 2 đến sqrt(n) (hoặc i * i <= n), nếu
n % i == 0 thì không nguyên tố. Xem lại bài Hàm.
Bài 2: hàm lớn hơn
Viết hàm int lonHon(int a, int b) trả về số lớn hơn, rồi dùng nó để tìm số lớn nhất
trong ba số.
Gợi ý
return a > b ? a : b;. Tìm max ba số bằng cách gọi lồng: lonHon(lonHon(a, b), c).
Xem lại bài Hàm.
Bài 3: phần tử lớn nhất trong vector
Viết hàm nhận một vector<int> và trả về phần tử lớn nhất.
Gợi ý
Khởi tạo maxV = v[0], duyệt phần còn lại, cập nhật khi gặp phần tử lớn hơn. Nhớ xét
trường hợp vector rỗng. Xem lại bài Mảng và vector.
Bài 4: đếm số chẵn
Viết hàm đếm xem trong một vector<int> có bao nhiêu phần tử chẵn.
Gợi ý
Duyệt dạng range for (int x : v), mỗi lần x % 2 == 0 thì tăng biến đếm. Xem lại
các bài Vòng lặp và Mảng và vector.
Bài 5: hoán vị hai biến
Viết hàm hoanVi đổi giá trị của hai biến nguyên truyền vào, sao cho sau khi gọi thì
giá trị hai biến gốc thực sự đổi chỗ.
Gợi ý
Phải truyền theo tham chiếu int& a, int& b (hoặc con trỏ int*), nếu truyền theo giá
trị thì chỉ đổi bản sao. Dùng một biến tạm để đổi chỗ. Xem lại bài
Con trỏ và tham chiếu.
Bài 6: đảo ngược vector
Viết hàm đảo ngược thứ tự các phần tử của một vector<int> (tại chỗ).
Gợi ý
Dùng hai chỉ số, một ở đầu một ở cuối, đổi chỗ hai phần tử rồi tiến vào giữa, dừng khi
gặp nhau. Truyền vector theo tham chiếu vector<int>& để sửa bản gốc. Xem lại các bài
Mảng và vector và Con trỏ và tham chiếu.
Bài 7: trung bình cộng của vector
Viết hàm nhận một vector<int> và trả về trung bình cộng dưới dạng double.
Gợi ý
Cộng dồn tất cả phần tử vào một biến tổng, rồi chia cho v.size(). Lưu ý size() trả
kiểu không dấu, nên ép tổng sang double trước khi chia để ra số thực, và xét riêng
trường hợp vector rỗng để tránh chia cho 0. Xem lại bài Mảng và vector.
Bài 8: tăng mọi phần tử tại chỗ
Viết hàm nhận một vector<int> và cộng thêm một lượng d vào mọi phần tử, sửa trực
tiếp trên vector gốc.
Gợi ý
Truyền vector theo tham chiếu vector<int>& thì mới sửa được bản gốc. Nếu duyệt bằng
for (int x : v) thì x chỉ là bản sao, muốn ghi ngược lại phải dùng for (int& x : v).
Xem lại các bài Con trỏ và tham chiếu và Mảng và vector.
Bài 9: nạp chồng hàm tính lũy thừa
Viết hai phiên bản cùng tên luyThua: một nhận (int co, int mu), một nhận
(double co, int mu), cùng trả về co mũ mu. Cho mu một giá trị mặc định là 2.
Gợi ý
Hai hàm cùng tên khác kiểu tham số là nạp chồng, trình biên dịch chọn phiên bản theo
kiểu đối số truyền vào. Tham số mặc định int mu = 2 chỉ ghi ở khai báo, khi gọi
luyThua(5) thì mu tự bằng 2. Thân hàm nhân dồn co đúng mu lần. Xem lại bài
Hàm.
Bài 10: sàng nguyên tố tới n
Nhập số nguyên dương n, in ra mọi số nguyên tố từ 2 đến n bằng sàng Eratosthenes
với một vector<bool>.
Gợi ý
Khởi tạo vector<bool> sang(n + 1, true), đánh dấu 0 và 1 là không nguyên tố, rồi với
mỗi i còn được đánh dấu, gạch bỏ mọi bội của nó bắt đầu từ i * i. Ép i * i sang
long long khi so sánh để tránh tràn với n lớn.
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<bool> sang(n + 1, true);
if (n >= 0) sang[0] = false;
if (n >= 1) sang[1] = false;
for (int i = 2; (long long)i * i <= n; i++) {
if (sang[i]) {
for (int j = i * i; j <= n; j += i) sang[j] = false;
}
}
for (int i = 2; i <= n; i++) {
if (sang[i]) cout << i << " ";
}
cout << "\n";
return 0;
}
Xem lại bài Mảng và vector.
Bài 11: sắp xếp vector với comparator
Cho một vector<int>, sắp xếp giảm dần bằng std::sort kèm một hàm so sánh, rồi in ra.
Gợi ý
std::sort mặc định sắp tăng dần; truyền thêm một comparator (ví dụ lambda
[](int a, int b){ return a > b; }) để đảo thành giảm dần. Comparator trả về true
nghĩa là phần tử thứ nhất đứng trước phần tử thứ hai.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v = {5, 2, 9, 1, 7};
// sort descending with a custom comparator
sort(v.begin(), v.end(), [](int a, int b) {
return a > b;
});
for (int x : v) cout << x << " ";
cout << "\n";
return 0;
}
Xem lại bài Mảng và vector.
Bài 12: đếm tần suất ký tự
Nhập một dòng văn bản, đếm số lần xuất hiện của từng chữ cái thường từ a đến z và in
ra từng ký tự kèm số lần xuất hiện của nó.
Gợi ý
Một mảng int dem[26] khởi tạo 0 là đủ: với mỗi ký tự trong khoảng a..z, tăng
dem[c - 'a']. Dùng getline để đọc cả dòng kể cả dấu cách. Nếu muốn đếm cả ký tự
khác chữ cái thì thay mảng bằng map<char, int>.
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
getline(cin, s);
int dem[26] = {0}; // counts for a..z
for (char c : s) {
if (c >= 'a' && c <= 'z') dem[c - 'a']++;
}
for (int i = 0; i < 26; i++) {
if (dem[i] > 0) cout << char('a' + i) << ": " << dem[i] << "\n";
}
return 0;
}
Xem lại bài Mảng và vector.
Bài 13: chuẩn hóa vector qua tham chiếu
Viết hàm nhận một vector<int> theo tham chiếu và thay mọi phần tử âm bằng 0, sửa trực
tiếp trên vector gốc mà không tạo vector mới.
Gợi ý
Nhận tham số vector<int>& v để sửa được bản gốc. Duyệt bằng for (int& x : v) để x
là tham chiếu tới phần tử thật, nhờ đó gán x = 0 mới ghi ngược lại vào vector.
#include <iostream>
#include <vector>
using namespace std;
// modify the caller's vector in place
void khongAm(vector<int>& v) {
for (int& x : v) { // reference, so writes reach the real element
if (x < 0) x = 0;
}
}
int main() {
vector<int> a = {3, -1, 4, -5, 9};
khongAm(a);
for (int x : a) cout << x << " ";
cout << "\n";
return 0;
}
Xem lại bài Con trỏ và tham chiếu.
Bài 14: nạp chồng hàm tính diện tích
Viết hai hàm cùng tên dienTich: một nhận hai cạnh của hình chữ nhật, một nhận bán kính
hình tròn. Gọi cả hai trong main.
Gợi ý
Hai hàm cùng tên khác danh sách tham số là nạp chồng; trình biên dịch chọn phiên bản
theo số lượng và kiểu đối số truyền vào. Phiên bản hai tham số tính dai * rong, phiên
bản một tham số tính 3.14159 * r * r.
#include <iostream>
using namespace std;
// rectangle area
double dienTich(double dai, double rong) {
return dai * rong;
}
// circle area (overload with one parameter)
double dienTich(double r) {
return 3.14159 * r * r;
}
int main() {
cout << dienTich(3.0, 4.0) << "\n"; // rectangle
cout << dienTich(2.0) << "\n"; // circle
return 0;
}
Xem lại bài Hàm.
Câu hỏi tự kiểm
- 1v.size() của một vector trả về kiểu gì?
- 2Muốn sửa được giá trị thật của các phần tử khi duyệt, cách viết nào đúng?
- 3Khác nhau chính giữa v.at(i) và v[i] khi i vượt ngoài biên là gì?
- 4Hai hàm cùng tên luyThua nhưng khác kiểu tham số minh họa khái niệm nào?