Bài 27.1: Bài tập thực hành mảng 2 chiều cơ bản trong C++

Bài 27.1: Bài tập thực hành mảng 2 chiều cơ bản 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!
Sau khi chúng ta đã cùng nhau tìm hiểu về khái niệm mảng 2 chiều (hay còn gọi là ma trận) trong C++ ở các bài trước, đã đến lúc chúng ta xắn tay áo vào thực hành. Lý thuyết là một chuyện, nhưng để thực sự thành thạo và hiểu rõ cách hoạt động của mảng 2 chiều, việc làm bài tập là cực kỳ quan trọng.
Bài viết này sẽ tập trung vào các bài tập thực hành cơ bản, giúp bạn làm quen với việc khai báo, khởi tạo, truy cập, nhập xuất và thực hiện các thao tác đơn giản trên mảng 2 chiều. Chúng ta sẽ đi qua từng bài tập nhỏ, kèm theo code minh họa ngắn gọn và giải thích chi tiết để bạn dễ dàng theo dõi.
Bắt đầu nào!
1. Khai báo và Khởi tạo mảng 2 chiều
Trước khi làm bất cứ điều gì khác, chúng ta cần biết cách tạo ra một mảng 2 chiều. Mảng 2 chiều trong C++ có thể được khai báo tĩnh (kích thước cố định) hoặc động (sử dụng con trỏ hoặc vector
). Ở mức cơ bản, chúng ta sẽ tập trung vào khai báo tĩnh.
Một mảng 2 chiều có thể được hình dung như một "lưới" hoặc "bảng" gồm các hàng và cột.
- Bài tập 1.1: Khai báo một mảng 2 chiều chứa số nguyên có kích thước 3 hàng và 4 cột, sau đó khởi tạo các giá trị ban đầu cho nó.
#include <iostream>
using namespace std;
int main() {
int mt[3][4];
int mt_kt[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
cout << "Phan tu o hang 1, cot 2 (chi so 1, 2) la: " << mt_kt[1][2] << endl;
int mt_0[3][4] = {};
cout << "Phan tu o hang 0, cot 0 cua ma tran zero la: " << mt_0[0][0] << endl;
return 0;
}
Output:
Phan tu o hang 1, cot 2 (chi so 1, 2) la: 7
Phan tu o hang 0, cot 0 cua ma tran zero la: 0
- Giải thích:
int maTran[3][4];
: Dòng này khai báo một mảng 2 chiều tên làmaTran
, có 3 hàng và 4 cột, chứa các phần tử kiểuint
. Các chỉ số hàng sẽ chạy từ 0 đến 2, và chỉ số cột sẽ chạy từ 0 đến 3.int maTranKhoiTao[3][4] = {{...}, {...}, {...}};
: Đây là cách phổ biến để khởi tạo giá trị ngay khi khai báo. Mỗi cặp{}
bên trong dấu{}
lớn tương ứng với một hàng của mảng. Các giá trị bên trong{}
nhỏ chính là các phần tử của hàng đó.maTranKhoiTao[1][2]
: Cú pháp để truy cập vào một phần tử cụ thể. Chỉ số đầu tiên là chỉ số hàng (bắt đầu từ 0), chỉ số thứ hai là chỉ số cột (cũng bắt đầu từ 0).maTranKhoiTao[1][2]
truy cập vào phần tử ở hàng thứ hai (chỉ số 1) và cột thứ ba (chỉ số 2).int maTranZero[3][4] = {};
: Cách ngắn gọn để khởi tạo tất cả các phần tử của mảng về giá trị 0.
2. Truy cập và Gán giá trị cho từng phần tử
Như bạn đã thấy ở ví dụ trên, việc truy cập một phần tử cụ thể trong mảng 2 chiều được thực hiện bằng cú pháp tenMang[chiSoHang][chiSoCot]
. Bạn có thể sử dụng cú pháp này để đọc giá trị của phần tử hoặc gán giá trị mới cho nó.
- Bài tập 2.1: Truy cập một vài phần tử trong mảng đã khởi tạo và thay đổi giá trị của một phần tử.
#include <iostream>
using namespace std;
int main() {
int mt[2][3] = {
{10, 20, 30},
{40, 50, 60}
};
cout << "Phan tu o hang 0, cot 1: " << mt[0][1] << endl;
cout << "Phan tu o hang 1, cot 0: " << mt[1][0] << endl;
mt[0][1] = 25;
cout << "Gia tri moi cua phan tu o hang 0, cot 1: " << mt[0][1] << endl;
return 0;
}
Output:
Phan tu o hang 0, cot 1: 20
Phan tu o hang 1, cot 0: 40
Gia tri moi cua phan tu o hang 0, cot 1: 25
- Giải thích:
maTran[0][1]
: Truy cập phần tử ở hàng đầu tiên (chỉ số 0), cột thứ hai (chỉ số 1).maTran[1][0]
: Truy cập phần tử ở hàng thứ hai (chỉ số 1), cột đầu tiên (chỉ số 0).maTran[0][1] = 25;
: Gán giá trị 25 cho phần tử tại vị trí hàng 0, cột 1. Điều quan trọng cần nhớ là các chỉ số phải nằm trong phạm vi hợp lệ của mảng (0 đến số_hang - 1 cho hàng, 0 đến số_cot - 1 cho cột). Truy cập ngoài phạm vi sẽ dẫn đến lỗi không xác định (undefined behavior) và có thể gây crash chương trình.
3. Nhập dữ liệu cho mảng từ bàn phím
Để nhập dữ liệu cho toàn bộ mảng từ người dùng, chúng ta cần duyệt qua tất cả các phần tử của mảng. Với mảng 2 chiều, điều này đòi hỏi sử dụng hai vòng lặp lồng nhau: một vòng lặp để duyệt qua các hàng và một vòng lặp bên trong để duyệt qua các cột trong mỗi hàng.
- Bài tập 3.1: Viết chương trình nhập dữ liệu cho một mảng 2 chiều kích thước 2x3 từ bàn phím.
#include <iostream>
using namespace std;
int main() {
const int h = 2;
const int c = 3;
int mt[h][c];
cout << "Nhap cac phan tu cho ma tran " << h << "x" << c << ":" << endl;
for (int i = 0; i < h; ++i) {
for (int j = 0; j < c; ++j) {
cout << "Nhap phan tu [" << i << "][" << j << "]: ";
cin >> mt[i][j];
}
}
cout << "Da nhap xong du lieu cho ma tran." << endl;
return 0;
}
Output (ví dụ với input giả định 1 2 3 4 5 6):
Nhap cac phan tu cho ma tran 2x3:
Nhap phan tu [0][0]: 1
Nhap phan tu [0][1]: 2
Nhap phan tu [0][2]: 3
Nhap phan tu [1][0]: 4
Nhap phan tu [1][1]: 5
Nhap phan tu [1][2]: 6
Da nhap xong du lieu cho ma tran.
- Giải thích:
- Chúng ta sử dụng các hằng số
SO_HANG
vàSO_COT
để dễ dàng quản lý kích thước mảng. - Vòng lặp ngoài
for (int i = 0; i < SO_HANG; ++i)
sẽ chạy từi = 0
đếni = SO_HANG - 1
, tương ứng với việc xử lý từng hàng một. - Với mỗi giá trị của
i
(tức là mỗi hàng), vòng lặp bên trongfor (int j = 0; j < SO_COT; ++j)
sẽ chạy từj = 0
đếnj = SO_COT - 1
, tương ứng với việc xử lý từng cột trong hàng hiện tại. - Bên trong vòng lặp lồng nhau,
cin >> maTran[i][j];
sẽ đọc một giá trị từ bàn phím và lưu vào phần tử ở hàngi
, cộtj
. Quá trình này lặp lại cho đến khi tất cả các phần tử đều được nhập.
- Chúng ta sử dụng các hằng số
4. In nội dung mảng ra màn hình
Tương tự như nhập dữ liệu, để hiển thị toàn bộ nội dung của mảng 2 chiều, chúng ta cũng cần duyệt qua tất cả các phần tử bằng hai vòng lặp lồng nhau. Để định dạng output trông giống như một ma trận thực sự, chúng ta cần in các phần tử của một hàng trên cùng một dòng và sau khi in xong một hàng thì xuống dòng.
- Bài tập 4.1: Viết chương trình in nội dung của một mảng 2 chiều kích thước 3x4 ra màn hình theo định dạng ma trận.
#include <iostream>
using namespace std;
int main() {
const int h = 3;
const int c = 4;
int mt[h][c] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
cout << "Noi dung cua ma tran " << h << "x" << c << ":" << endl;
for (int i = 0; i < h; ++i) {
for (int j = 0; j < c; ++j) {
cout << mt[i][j] << "\t";
}
cout << endl;
}
return 0;
}
Output:
Noi dung cua ma tran 3x4:
1 2 3 4
5 6 7 8
9 10 11 12
- Giải thích:
- Cấu trúc vòng lặp lồng nhau vẫn giống như khi nhập, đảm bảo chúng ta duyệt qua mọi phần tử.
cout << matrix[i][j] << "\t";
: In giá trị của phần tử hiện tại, sau đó in ký tự tab (\t
) để tạo khoảng cách giữa các phần tử trên cùng một hàng. Điều này giúp output dễ đọc hơn.cout << endl;
: Quan trọng! Dòng này nằm sau vòng lặp cột (vòng lặp bên trong) nhưng bên trong vòng lặp hàng (vòng lặp bên ngoài). Nó có tác dụng xuống dòng sau khi tất cả các phần tử của một hàng đã được in ra, đảm bảo mỗi hàng của ma trận được in trên một dòng riêng biệt trên màn hình.
5. Tính tổng tất cả các phần tử
Một trong những thao tác cơ bản nhất là tính tổng giá trị của tất cả các phần tử trong mảng. Điều này cũng chỉ đơn giản là duyệt qua toàn bộ mảng và cộng dồn giá trị của từng phần tử vào một biến tổng.
- Bài tập 5.1: Viết chương trình tính tổng các phần tử của một mảng 2 chiều có kích thước 3x3.
#include <iostream>
using namespace std;
int main() {
const int n = 3;
int mt[n][n] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
long long tong = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
tong += mt[i][j];
}
}
cout << "Tong cac phan tu trong ma tran la: " << tong << endl;
return 0;
}
Output:
Tong cac phan tu trong ma tran la: 45
- Giải thích:
- Chúng ta khai báo một biến
sum
(kiểulong long
để an toàn hơn với các tổng lớn) và khởi tạo nó bằng 0. - Hai vòng lặp lồng nhau duyệt qua từng phần tử
matrix[i][j]
. - Bên trong vòng lặp,
sum += matrix[i][j];
sẽ cộng giá trị của phần tửmatrix[i][j]
vào biếnsum
. Sau khi duyệt hết mảng, biếnsum
sẽ chứa tổng của tất cả các phần tử.
- Chúng ta khai báo một biến
6. Tìm phần tử lớn nhất và nhỏ nhất
Tương tự như tính tổng, việc tìm giá trị lớn nhất hoặc nhỏ nhất trong mảng 2 chiều cũng yêu cầu duyệt qua tất cả các phần tử. Chúng ta sẽ cần duy trì hai biến: một để lưu giá trị lớn nhất hiện tại tìm được và một để lưu giá trị nhỏ nhất hiện tại tìm được.
- Bài tập 6.1: Viết chương trình tìm giá trị lớn nhất và nhỏ nhất trong một mảng 2 chiều kích thước 2x4.
#include <iostream>
#include <limits>
using namespace std;
int main() {
const int h = 2;
const int c = 4;
int mt[h][c] = {
{10, 5, 18, 2},
{25, 1, 15, 7}
};
int min_g = mt[0][0];
int max_g = mt[0][0];
for (int i = 0; i < h; ++i) {
for (int j = 0; j < c; ++j) {
if (mt[i][j] > max_g) {
max_g = mt[i][j];
}
if (mt[i][j] < min_g) {
min_g = mt[i][j];
}
}
}
cout << "Gia tri lon nhat trong ma tran la: " << max_g << endl;
cout << "Gia tri nho nhat trong ma tran la: " << min_g << endl;
return 0;
}
Output:
Gia tri lon nhat trong ma tran la: 25
Gia tri nho nhat trong ma tran la: 1
- Giải thích:
- Chúng ta khởi tạo
min_val
vàmax_val
bằng giá trị của phần tử đầu tiên của mảng (matrix[0][0]
). Đây là một cách khởi tạo an toàn vì chắc chắn giá trị min/max thực sự phải bằng hoặc nằm giữa các giá trị trong mảng, bao gồm cả phần tử đầu tiên. Một cách khác là khởi tạomin_val
bằng giá trị lớn nhất có thể của kiểuint
(numeric_limits<int>::max()
) vàmax_val
bằng giá trị nhỏ nhất có thể (numeric_limits<int>::min()
). - Vòng lặp lồng nhau duyệt qua từng phần tử
matrix[i][j]
. - Với mỗi phần tử, chúng ta so sánh nó với
max_val
hiện tại. Nếu phần tử đó lớn hơnmax_val
, chúng ta cập nhậtmax_val
. - Tương tự, chúng ta so sánh phần tử đó với
min_val
hiện tại. Nếu phần tử đó nhỏ hơnmin_val
, chúng ta cập nhậtmin_val
. - Sau khi duyệt hết mảng,
max_val
sẽ chứa giá trị lớn nhất vàmin_val
sẽ chứa giá trị nhỏ nhất.
- Chúng ta khởi tạo
7. Tính tổng các phần tử trên một hàng hoặc cột cụ thể
Đôi khi bạn chỉ muốn thao tác trên một phần của mảng, ví dụ như tính tổng các phần tử trên một hàng cụ thể hoặc một cột cụ thể. Điều này đòi hỏi bạn cố định chỉ số của hàng hoặc cột mà bạn quan tâm trong vòng lặp.
- Bài tập 7.1: Viết chương trình tính tổng các phần tử trên hàng thứ 1 (chỉ số 1) và cột thứ 2 (chỉ số 2) của một mảng 3x4.
#include <iostream>
using namespace std;
int main() {
const int h = 3;
const int c = 4;
int mt[h][c] = {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12}
};
const int hang_tinh = 1;
const int cot_tinh = 2;
if (hang_tinh >= 0 && hang_tinh < h) {
int tong_h = 0;
for (int j = 0; j < c; ++j) {
tong_h += mt[hang_tinh][j];
}
cout << "Tong cac phan tu tren hang " << hang_tinh << " la: " << tong_h << endl;
} else {
cout << "Chi so hang khong hop le!" << endl;
}
if (cot_tinh >= 0 && cot_tinh < c) {
int tong_c = 0;
for (int i = 0; i < h; ++i) {
tong_c += mt[i][cot_tinh];
}
cout << "Tong cac phan tu tren cot " << cot_tinh << " la: " << tong_c << endl;
} else {
cout << "Chi so cot khong hop le!" << endl;
}
return 0;
}
Output:
Tong cac phan tu tren hang 1 la: 26
Tong cac phan tu tren cot 2 la: 21
- Giải thích:
- Để tính tổng một hàng cụ thể (
HANG_CAN_TINH
), chúng ta sử dụng một vòng lặp duy nhất duyệt qua các cột (j
từ 0 đếnCOLS - 1
). Trong vòng lặp, chỉ số hàng (i
) được cố định làHANG_CAN_TINH
, còn chỉ số cột (j
) thay đổi.sum_hang += matrix[HANG_CAN_TINH][j];
. - Để tính tổng một cột cụ thể (
COT_CAN_TINH
), chúng ta sử dụng một vòng lặp duy nhất duyệt qua các hàng (i
từ 0 đếnROWS - 1
). Trong vòng lặp, chỉ số cột (j
) được cố định làCOT_CAN_TINH
, còn chỉ số hàng (i
) thay đổi.sum_cot += matrix[i][COT_CAN_TINH];
. - Việc kiểm tra
if (HANG_CAN_TINH >= 0 && HANG_CAN_TINH < ROWS)
vàif (COT_CAN_TINH >= 0 && COT_CAN_TINH < COLS)
là cần thiết để đảm bảo chỉ số hàng/cột mà bạn muốn truy cập nằm trong phạm vi hợp lệ của mảng, tránh lỗi chương trình.
- Để tính tổng một hàng cụ thể (
Comments