Bài 11.5: Bài tập thực hành mảng 1 chiều trong C++

Bài 11.5: Bài tập thực hành mảng 1 chiều trong C++
Chào mừng các bạn đã quay trở lại với chuỗi bài viết về C++ của FullhouseDev!
Ở các bài trước, chúng ta đã cùng nhau tìm hiểu về khái niệm mảng một chiều (one-dimensional array) trong C++: cách khai báo, khởi tạo và truy cập các phần tử thông qua chỉ số (index). Mảng là một cấu trúc dữ liệu cực kỳ cơ bản và quan trọng, cho phép chúng ta lưu trữ một tập hợp các giá trị cùng kiểu dữ liệu dưới một tên biến duy nhất.
Việc hiểu lý thuyết là bước đầu tiên, nhưng để thực sự nắm vững và áp dụng được mảng vào giải quyết các vấn đề thực tế, việc thực hành là không thể thiếu. Bài viết hôm nay sẽ tập trung vào việc đưa ra và giải thích một số bài tập thực hành cơ bản về mảng 1 chiều, giúp bạn củng cố kiến thức và làm quen với các thao tác phổ biến nhất.
Hãy cùng bắt tay vào thực hành nào!
1. Khai báo, Khởi tạo và In các Phần tử Mảng
Đây là bài tập cơ bản nhất để làm quen với cú pháp. Chúng ta sẽ khai báo một mảng, khởi tạo nó với một vài giá trị và sau đó dùng vòng lặp để in tất cả các phần tử ra màn hình.
Ví dụ 1: Khởi tạo trực tiếp và In toàn bộ mảng
#include <iostream>
int main() {
using namespace std;
int a[] = {10, 25, 5, 40, 15, 30};
int n = sizeof(a) / sizeof(a[0]);
cout << "Cac phan tu cua mang la:" << endl;
for (int i = 0; i < n; ++i) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
Output:
Cac phan tu cua mang la:
10 25 5 40 15 30
Giải thích:
- Chúng ta sử dụng cú pháp
{}
để khởi tạo mảngnumbers
ngay khi khai báo. C++ sẽ tự động xác định kích thước của mảng dựa trên số lượng giá trị bạn cung cấp. - Để biết kích thước mảng khi không khai báo rõ ràng (ví dụ:
int numbers[6]
), chúng ta dùng mẹosizeof(mang) / sizeof(phan_tu_dau_tien)
. Điều này trả về số lượng phần tử. - Vòng lặp
for
là công cụ lý tưởng để làm việc với mảng. Chúng ta lặp từ chỉ số0
đếnsize - 1
. - Trong mỗi lần lặp, chúng ta truy cập phần tử bằng cú pháp
numbers[i]
và in nó ra.
2. Nhập Dữ liệu vào Mảng từ Người dùng
Thay vì khởi tạo trực tiếp, chúng ta thường cần nhập dữ liệu cho mảng từ người dùng hoặc từ một nguồn khác.
Ví dụ 2: Nhập N phần tử vào mảng
#include <iostream>
int main() {
using namespace std;
const int MAX = 100;
int a[MAX];
int n;
cout << "Nhap so luong phan tu (toi da " << MAX << "): ";
cin >> n;
if (n <= 0 || n > MAX) {
cout << "Kich thuoc khong hop le!" << endl;
return 1;
}
cout << "Hay nhap " << n << " so nguyen:\n";
for (int i = 0; i < n; ++i) {
cout << "Nhap phan tu thu " << (i + 1) << ": ";
cin >> a[i];
}
cout << "\nMang vua nhap la:\n";
for (int i = 0; i < n; ++i) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
Output (ví dụ với input mẫu):
Nhap so luong phan tu (toi da 100): 3
Hay nhap 3 so nguyen:
Nhap phan tu thu 1: 10
Nhap phan tu thu 2: 20
Nhap phan tu thu 3: 30
Mang vua nhap la:
10 20 30
Giải thích:
- Chúng ta khai báo một mảng có kích thước cố định tối đa (
MAX_SIZE
) vì kích thước mảng tĩnh phải được biết lúc biên dịch. - Chúng ta hỏi người dùng số lượng phần tử thực tế họ muốn nhập (
actual_size
). - Kiểm tra tính hợp lệ của
actual_size
để tránh truy cập ngoài phạm vi mảng (lỗi runtime nguy hiểm). - Vòng lặp
for
chạy từ0
đếnactual_size - 1
. - Trong vòng lặp, chúng ta sử dụng
cin >> data[i]
để đọc giá trị từ bàn phím và gán vào phần tử tại chỉ sối
.
3. Tính Tổng và Trung bình các Phần tử trong Mảng
Một bài tập phổ biến là tính toán các giá trị thống kê từ dữ liệu trong mảng. Tính tổng và trung bình là hai ví dụ điển hình.
Ví dụ 3: Tính tổng và trung bình
#include <iostream>
int main() {
using namespace std;
int a[] = {10, 20, 30, 40, 50};
int n = sizeof(a) / sizeof(a[0]);
long long tong = 0;
for (int i = 0; i < n; ++i) {
tong += a[i];
}
cout << "Tong cac phan tu trong mang: " << tong << endl;
double tbinh = static_cast<double>(tong) / n;
cout << "Gia tri trung binh cua mang: " << tbinh << endl;
return 0;
}
Output:
Tong cac phan tu trong mang: 150
Gia tri trung binh cua mang: 30
Giải thích:
- Chúng ta sử dụng một biến
sum
(kiểulong long
để đảm bảo đủ lớn) được khởi tạo bằng 0. - Duyệt qua mảng, cộng giá trị của mỗi phần tử vào
sum
. - Để tính trung bình, chúng ta chia
sum
chosize
. Việc ép kiểusum
sangdouble
(static_cast<double>(sum)
) là rất quan trọng để phép chia cho ra kết quả số thực, thay vì phép chia số nguyên (sẽ làm mất phần thập phân).
4. Tìm Phần tử Lớn nhất và Nhỏ nhất
Đây cũng là một bài tập kinh điển sử dụng mảng và vòng lặp.
Ví dụ 4: Tìm Max và Min
#include <iostream>
int main() {
using namespace std;
int a[] = {12, 45, 7, 99, 23, 5, 60};
int n = sizeof(a) / sizeof(a[0]);
if (n == 0) {
cout << "Mang rong, khong co phan tu lon nhat hoac nho nhat." << endl;
return 0;
}
int lon_nhat = a[0];
int nho_nhat = a[0];
for (int i = 1; i < n; ++i) {
if (a[i] > lon_nhat) {
lon_nhat = a[i];
}
if (a[i] < nho_nhat) {
nho_nhat = a[i];
}
}
cout << "Phan tu lon nhat trong mang: " << lon_nhat << endl;
cout << "Phan tu nho nhat trong mang: " << nho_nhat << endl;
return 0;
}
Output:
Phan tu lon nhat trong mang: 99
Phan tu nho nhat trong mang: 5
Giải thích:
- Chúng ta kiểm tra trường hợp mảng rỗng trước để tránh lỗi.
- Mẹo quan trọng: Để tìm max/min, chúng ta khởi tạo giá trị
max_val
vàmin_val
bằng phần tử đầu tiên của mảng (numbers[0]
). Điều này đảm bảo rằng giá trị khởi tạo là một phần tử thực sự trong mảng. - Vòng lặp duyệt qua các phần tử còn lại của mảng, bắt đầu từ chỉ số
1
. - Trong mỗi lần lặp, chúng ta so sánh phần tử hiện tại (
numbers[i]
) vớimax_val
vàmin_val
và cập nhật nếu cần.
Lưu ý: Một cách khác để khởi tạo max_val
và min_val
là sử dụng giới hạn kiểu dữ liệu: max_val = numeric_limits<int>::min();
và min_val = numeric_limits<int>::max();
. Cách này hoạt động tốt ngay cả với mảng chứa tất cả các số âm hoặc tất cả các số dương, nhưng cách khởi tạo bằng phần tử đầu tiên đơn giản hơn và thường đủ dùng.
5. Tìm kiếm một Phần tử trong Mảng (Tìm kiếm tuyến tính)
Bài tập này yêu cầu kiểm tra xem một giá trị cụ thể có tồn tại trong mảng hay không, và nếu có thì ở vị trí nào. Tìm kiếm tuyến tính (linear search) là thuật toán đơn giản nhất.
Ví dụ 5: Tìm kiếm một giá trị
#include <iostream>
int main() {
using namespace std;
int a[] = {5, 13, 7, 42, 19, 8, 30};
int n = sizeof(a) / sizeof(a[0]);
int x = 19;
bool tim_thay = false;
int vi_tri = -1;
for (int i = 0; i < n; ++i) {
if (a[i] == x) {
tim_thay = true;
vi_tri = i;
break;
}
}
if (tim_thay) {
cout << "Tim thay gia tri " << x << " tai chi so: " << vi_tri << endl;
} else {
cout << "Khong tim thay gia tri " << x << " trong mang." << endl;
}
return 0;
}
Output:
Tim thay gia tri 19 tai chi so: 4
Giải thích:
- Chúng ta sử dụng một biến
bool found
để ghi nhớ liệu giá trị có được tìm thấy hay không, ban đầu làfalse
. - Một biến
found_index
được dùng để lưu chỉ số của phần tử tìm thấy, ban đầu là-1
(một chỉ số không hợp lệ). - Vòng lặp duyệt qua mảng. Nếu phần tử hiện tại (
numbers[i]
) bằng vớitarget_value
, chúng ta đặtfound = true
, lưu lại chỉ sối
, và sử dụng lệnhbreak
để thoát khỏi vòng lặp ngay lập tức vì đã tìm thấy thứ cần tìm (tiết kiệm thời gian). - Sau vòng lặp, kiểm tra giá trị của
found
để in thông báo thích hợp.
6. Nhân đôi các Phần tử trong Mảng
Bài tập này minh họa việc biến đổi các phần tử trong mảng.
Ví dụ 6: Nhân đôi giá trị từng phần tử
#include <iostream>
int main() {
using namespace std;
int a[] = {1, 2, 3, 4, 5};
int n = sizeof(a) / sizeof(a[0]);
cout << "Mang truoc khi nhan doi: ";
for (int i = 0; i < n; ++i) {
cout << a[i] << " ";
}
cout << endl;
for (int i = 0; i < n; ++i) {
a[i] *= 2;
}
cout << "Mang sau khi nhan doi: ";
for (int i = 0; i < n; ++i) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
Output:
Mang truoc khi nhan doi: 1 2 3 4 5
Mang sau khi nhan doi: 2 4 6 8 10
Giải thích:
- Chúng ta duyệt qua mảng.
- Trong mỗi lần lặp, chúng ta truy cập phần tử
numbers[i]
, nhân giá trị của nó với 2, và gán lại kết quả vào chính vị trí đó trong mảng. Đây là cách biến đổi tại chỗ (in-place).
Tóm kết các thao tác chính với Mảng 1 chiều
Qua các ví dụ trên, bạn có thể thấy rằng các thao tác cơ bản với mảng 1 chiều thường xoay quanh việc sử dụng vòng lặp (for
) và truy cập các phần tử thông qua chỉ số ([]
).
Các kỹ thuật phổ biến bao gồm:
- Duyệt mảng: Sử dụng vòng lặp để đi qua từng phần tử.
- Truy cập/Thay đổi phần tử: Dùng
ten_mang[chi_so]
. - Nhập/Xuất dữ liệu: Kết hợp vòng lặp với
cin
vàcout
. - Tính toán/Thống kê: Dùng vòng lặp để tổng hợp thông tin từ các phần tử (tổng, trung bình, max, min, đếm...).
- Tìm kiếm: Dùng vòng lặp để so sánh từng phần tử với giá trị cần tìm.
- Biến đổi: Dùng vòng lặp để thay đổi giá trị của từng phần tử dựa trên một quy tắc nào đó.
Mảng 1 chiều là nền tảng vững chắc cho nhiều cấu trúc dữ liệu phức tạp hơn sau này. Hãy dành thời gian thực hành thêm các bài tập tương tự, thử nghiệm với các kiểu dữ liệu khác (ví dụ: float
, char
, string
) và các kích thước mảng khác nhau.
Bài tập ví dụ: C++ Bài 7.A6: Nền tảng
Có \(N\) nền tảng được sắp xếp thành một hàng. Chiều cao của nền tảng thứ \(i\) từ bên trái là \(H_i\).
Hiếu ban đầu đứng trên nền tảng bên trái nhất.
Vì anh ấy thích độ cao, anh ấy sẽ lặp lại động tác sau miễn là có thể.
Nếu nền tảng anh ấy đang đứng không phải là nền tảng bên phải nhất và nền tảng tiếp theo bên phải có chiều cao lớn hơn nền tảng hiện tại, anh ấy sẽ bước lên nền tảng tiếp theo. Tìm chiều cao của nền tảng cuối cùng mà anh ấy sẽ đứng lên.
Ràng buộc
\(2 \leq N \leq 10^5\)
\(1 \leq H_i \leq 10^9\)
Tất cả các giá trị đầu vào là số nguyên.
Đầu vào
Đầu vào được cung cấp từ Standard Input theo định dạng sau:
\(N\)
\(H_1\) ... \(H_N\)
Đầu ra
In câu trả lời.
Ví dụ:
Ví dụ 1
Đầu vào
5
1 5 10 4 2
Đầu ra
10
Hiếu ban đầu đứng trên nền tảng bên trái nhất, có chiều cao là \(1\). Nền tảng tiếp theo bên phải có chiều cao là \(5\) và cao hơn nền tảng hiện tại, vì vậy anh ấy bước lên nó.
Bây giờ anh ấy đang đứng trên nền tảng thứ \(2\) từ bên trái, có chiều cao là \(5\). Nền tảng tiếp theo bên phải có chiều cao là \(10\) và cao hơn nền tảng hiện tại, vì vậy anh ấy bước lên nó.
Bây giờ anh ấy đang đứng trên nền tảng thứ \(3\) từ bên trái, có chiều cao là \(10\). Nền tảng tiếp theo bên phải có chiều cao là \(4\) và thấp hơn nền tảng hiện tại, vì vậy anh ấy dừng lại.
Vì vậy, chiều cao của nền tảng cuối cùng mà Hiếu sẽ đứng lên là \(10\).
Ví dụ 2
Đầu vào
3
100 1000 100000
Đầu ra
100000
Ví dụ 3
Đầu vào
4
27 1828 1828 9242
Đầu ra
1828
Giải thích ví dụ mẫu:
- Trong ví dụ 1, Hiếu sẽ di chuyển từ nền tảng có chiều cao 1 lên nền tảng cao 5 và sau đó lên nền tảng cao 10, vì vậy nền tảng cuối cùng anh ấy đứng trên có chiều cao 10.
1. Đọc và lưu trữ dữ liệu:
- Bạn cần đọc số lượng nền tảng
N
trước. - Sau đó, đọc
N
chiều caoH_i
. - Để lưu trữ các chiều cao này, cách tốt nhất là sử dụng một mảng hoặc một vector. Trong C++,
vector
là lựa chọn linh hoạt và tiện lợi. Hãy sử dụngvector<int>
(hoặcvector<long long>
nếu chiều cao có thể vượt quá giới hạn củaint
, nhưng ràng buộc1 \leq H_i \leq 10^9
cho thấyint
là đủ trên hầu hết các hệ thống).
2. Mô phỏng chuyển động:
- Hiếu bắt đầu ở nền tảng bên trái nhất, tức là nền tảng có chỉ số (index) 0 trong mảng/vector.
- Bạn cần duy trì một biến để theo dõi vị trí hiện tại của Hiếu (chỉ số của nền tảng anh ấy đang đứng). Khởi tạo nó bằng 0.
- Sử dụng một vòng lặp để mô phỏng các bước di chuyển. Vòng lặp này sẽ tiếp tục chừng nào Hiếu còn có thể di chuyển theo luật.
- Luật di chuyển: Hiếu bước sang nền tảng tiếp theo nếu (1) anh ấy không ở nền tảng cuối cùng và (2) nền tảng tiếp theo cao hơn nền tảng hiện tại.
- Vòng lặp nên kiểm tra cả hai điều kiện này trước khi quyết định di chuyển.
- Điều kiện (1): Chỉ số hiện tại phải nhỏ hơn
N - 1
. - Điều kiện (2): Chiều cao tại chỉ số hiện tại + 1 phải lớn hơn chiều cao tại chỉ số hiện tại.
- Điều kiện (1): Chỉ số hiện tại phải nhỏ hơn
- Nếu cả hai điều kiện đúng, bạn tăng chỉ số hiện tại lên 1 và lặp lại quá trình kiểm tra.
- Vòng lặp dừng khi một trong hai điều kiện trên sai (hoặc anh ấy đến nền tảng cuối cùng, hoặc nền tảng tiếp theo không cao hơn).
3. Xác định kết quả:
- Khi vòng lặp kết thúc, biến chỉ số hiện tại sẽ lưu giữ chỉ số của nền tảng cuối cùng mà Hiếu đứng lên.
- Chiều cao của nền tảng cuối cùng chính là giá trị tại chỉ số này trong vector chiều cao.
- In giá trị này ra màn hình.
4. Cấu trúc code gợi ý (dùng std):
- Bao gồm các header cần thiết:
<iostream>
để nhập/xuất,<vector>
để sử dụng vector. - Sử dụng
cin
vàcout
. - Đọc N.
- Khai báo
vector<int> heights(N);
. - Đọc N chiều cao vào vector
heights
. - Khai báo biến
int current_index = 0;
. - Sử dụng một vòng lặp
while
với điều kiện kiểm tracurrent_index < N - 1
và so sánh chiều caoheights[current_index + 1] > heights[current_index]
. - Bên trong vòng lặp
while
, nếu điều kiện đúng, tăngcurrent_index
lên 1. - Sau vòng lặp, in ra
heights[current_index]
.
Lưu ý: Để tối ưu tốc độ nhập/xuất với N lớn, bạn có thể thêm dòng ios_base::sync_with_stdio(false); cin.tie(NULL);
ở đầu hàm main.
#include <iostream>
#include <vector>
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
vector<long long> h(n);
for (int i = 0; i < n; ++i) {
cin >> h[i];
}
int i = 0;
while (i < n - 1 && h[i + 1] > h[i]) {
i++;
}
cout << h[i] << endl;
return 0;
}
Output (với ví dụ 1):
10
Comments