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>
int main() {
// Khai báo mảng 2 chiều 3 hàng, 4 cột kiểu int
int maTran[3][4];
// Khởi tạo giá trị cho mảng ngay lúc khai báo (cách 1)
int maTranKhoiTao[3][4] = {
{1, 2, 3, 4}, // Hàng 0
{5, 6, 7, 8}, // Hàng 1
{9, 10, 11, 12} // Hàng 2
};
// In ra một phần tử để kiểm tra
cout << "Phan tu o hang 1, cot 2 (chi so 1, 2) la: " << maTranKhoiTao[1][2] << endl;
// Kết quả sẽ là 7
// Khởi tạo tất cả phần tử về 0 (cách 2)
int maTranZero[3][4] = {}; // Hoặc = {0};
cout << "Phan tu o hang 0, cot 0 cua ma tran zero la: " << maTranZero[0][0] << endl;
// Kết quả sẽ là 0
return 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>
int main() {
int maTran[2][3] = {
{10, 20, 30},
{40, 50, 60}
};
// Truy cập và in giá trị
cout << "Phan tu o hang 0, cot 1: " << maTran[0][1] << endl; // Kết quả: 20
cout << "Phan tu o hang 1, cot 0: " << maTran[1][0] << endl; // Kết quả: 40
// Gán giá trị mới
maTran[0][1] = 25;
cout << "Gia tri moi cua phan tu o hang 0, cot 1: " << maTran[0][1] << endl; // Kết quả: 25
return 0;
}
- 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>
int main() {
const int SO_HANG = 2;
const int SO_COT = 3;
int maTran[SO_HANG][SO_COT];
cout << "Nhap cac phan tu cho ma tran " << SO_HANG << "x" << SO_COT << ":" << endl;
// Vòng lặp duyệt qua các hàng
for (int i = 0; i < SO_HANG; ++i) {
// Vòng lặp duyệt qua các cột trong mỗi hàng
for (int j = 0; j < SO_COT; ++j) {
cout << "Nhap phan tu [" << i << "][" << j << "]: ";
cin >> maTran[i][j]; // Nhập giá trị cho phần tử tại (i, j)
}
}
cout << "Da nhap xong du lieu cho ma tran." << endl;
return 0;
}
- 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>
int main() {
const int ROWS = 3;
const int COLS = 4;
int matrix[ROWS][COLS] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
cout << "Noi dung cua ma tran " << ROWS << "x" << COLS << ":" << endl;
// Vòng lặp duyệt qua các hàng
for (int i = 0; i < ROWS; ++i) {
// Vòng lặp duyệt qua các cột trong mỗi hàng
for (int j = 0; j < COLS; ++j) {
// In phần tử, thêm khoảng trắng để các cột thẳng hàng hơn (tùy chọn)
cout << matrix[i][j] << "\t"; // Sử dụng tab (\t) để căn chỉnh
}
cout << endl; // Sau khi in xong một hàng, xuống dòng
}
return 0;
}
- 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>
int main() {
const int N = 3; // Sử dụng N cho cả số hàng và số cột nếu là ma trận vuông
int matrix[N][N] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
long long sum = 0; // Sử dụng long long để tránh tràn số nếu tổng lớn
// Duyệt qua tất cả các phần tử và cộng vào tổng
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
sum += matrix[i][j]; // Cộng dồn giá trị của phần tử hiện tại
}
}
cout << "Tong cac phan tu trong ma tran la: " << sum << endl;
// Kết quả: 1 + 2 + ... + 9 = 45
return 0;
}
- 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> // Bao gồm thư viện này để sử dụng numeric_limits
int main() {
const int ROWS = 2;
const int COLS = 4;
int matrix[ROWS][COLS] = {
{10, 5, 18, 2},
{25, 1, 15, 7}
};
// Khởi tạo min_val và max_val
// Cách an toàn là khởi tạo bằng giá trị của phần tử đầu tiên,
// hoặc sử dụng giới hạn min/max của kiểu dữ liệu.
int min_val = matrix[0][0];
int max_val = matrix[0][0];
// Duyệt qua tất cả các phần tử (có thể bỏ qua matrix[0][0] vì đã dùng để khởi tạo)
for (int i = 0; i < ROWS; ++i) {
for (int j = 0; j < COLS; ++j) {
// So sánh với giá trị lớn nhất hiện tại
if (matrix[i][j] > max_val) {
max_val = matrix[i][j]; // Cập nhật giá trị lớn nhất
}
// So sánh với giá trị nhỏ nhất hiện tại
if (matrix[i][j] < min_val) {
min_val = matrix[i][j]; // Cập nhật giá trị nhỏ nhất
}
}
}
cout << "Gia tri lon nhat trong ma tran la: " << max_val << endl; // Kết quả: 25
cout << "Gia tri nho nhat trong ma tran la: " << min_val << endl; // Kết quả: 1
return 0;
}
- 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>
int main() {
const int ROWS = 3;
const int COLS = 4;
int matrix[ROWS][COLS] = {
{ 1, 2, 3, 4}, // Hang 0
{ 5, 6, 7, 8}, // Hang 1
{ 9, 10, 11, 12} // Hang 2
};
const int HANG_CAN_TINH = 1; // Chi so cua hang can tinh tong
const int COT_CAN_TINH = 2; // Chi so cua cot can tinh tong
// Kiem tra chi so hang/cot co hop le khong
if (HANG_CAN_TINH >= 0 && HANG_CAN_TINH < ROWS) {
int sum_hang = 0;
// Duyet qua cac cot cua hang HANG_CAN_TINH
for (int j = 0; j < COLS; ++j) {
sum_hang += matrix[HANG_CAN_TINH][j]; // Co dinh chi so hang, chi so cot chay
}
cout << "Tong cac phan tu tren hang " << HANG_CAN_TINH << " la: " << sum_hang << endl;
// Ket qua: 5 + 6 + 7 + 8 = 26
} else {
cout << "Chi so hang khong hop le!" << endl;
}
if (COT_CAN_TINH >= 0 && COT_CAN_TINH < COLS) {
int sum_cot = 0;
// Duyet qua cac hang cua cot COT_CAN_TINH
for (int i = 0; i < ROWS; ++i) {
sum_cot += matrix[i][COT_CAN_TINH]; // Co dinh chi so cot, chi so hang chay
}
cout << "Tong cac phan tu tren cot " << COT_CAN_TINH << " la: " << sum_cot << endl;
// Ket qua: 3 + 7 + 11 = 21
} else {
cout << "Chi so cot khong hop le!" << endl;
}
return 0;
}
- 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