Bài 9: Mảng 2 chiều (2D Arrays)
📖 Giới thiệu
Mảng 2 chiều là một cấu trúc dữ liệu quan trọng trong C++, cho phép chúng ta lưu trữ và xử lý dữ liệu dưới dạng bảng (hàng và cột). Mảng 2 chiều rất hữu ích khi làm việc với:
- Ma trận toán học: Tính toán đại số tuyến tính
- Bảng dữ liệu: Điểm số học sinh, báo cáo bán hàng
- Hình ảnh: Xử lý pixel trong game hoặc đồ họa
- Bản đồ: Lưu trữ địa hình, mê cung trong game
Hiểu được mảng 2 chiều là nền tảng để làm việc với các cấu trúc dữ liệu phức tạp hơn và giải quyết nhiều bài toán thực tế.
🔧 Cú pháp
Khai báo và khởi tạo
cpp
// Cú pháp cơ bản
data_type array_name[rows][columns];
// Khai báo với kích thước cố định
int matrix[3][4]; // Ma trận 3 hàng, 4 cột
float grades[5][3]; // Bảng điểm 5 học sinh, 3 môn học
char board[8][8]; // Bàn cờ 8x8
// Khởi tạo cùng lúc khai báo
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// Khởi tạo rút gọn
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
// Khởi tạo một phần
int matrix[3][3] = {
{1, 2}, // Hàng 1: {1, 2, 0}
{3}, // Hàng 2: {3, 0, 0}
// Hàng 3: {0, 0, 0}
};Truy cập phần tử
cpp
// Đọc giá trị
int value = matrix[row][column];
// Gán giá trị
matrix[row][column] = value;
// Lưu ý: chỉ số bắt đầu từ 0
matrix[0][0] = 10; // Phần tử hàng đầu, cột đầu
matrix[2][1] = 25; // Phần tử hàng 3, cột 2Duyệt mảng 2 chiều
cpp
// Duyệt toàn bộ mảng
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
// Duyệt theo hàng
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
// Xử lý matrix[i][j]
}
}
// Duyệt theo cột
for (int j = 0; j < columns; j++) {
for (int i = 0; i < rows; i++) {
// Xử lý matrix[i][j]
}
}🔬 Phân tích & Giải thích chi tiết
Cách thức hoạt động
Mảng 2 chiều thực chất được lưu trữ tuyến tính trong bộ nhớ theo thứ tự hàng (row-major order):
cpp
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
// Trong bộ nhớ: [1][2][3][4][5][6]
// matrix[0][0]=1, matrix[0][1]=2, matrix[0][2]=3
// matrix[1][0]=4, matrix[1][1]=5, matrix[1][2]=6Các thao tác cơ bản
1. Nhập dữ liệu:
cpp
void inputMatrix(int matrix[][MAX_COL], int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << "Nhap matrix[" << i << "][" << j << "]: ";
cin >> matrix[i][j];
}
}
}2. Xuất dữ liệu:
cpp
void printMatrix(int matrix[][MAX_COL], int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << setw(4) << matrix[i][j];
}
cout << endl;
}
}3. Tìm kiếm:
cpp
bool search(int matrix[][MAX_COL], int rows, int cols, int target) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (matrix[i][j] == target) {
return true;
}
}
}
return false;
}Khi nào sử dụng mảng 2 chiều
- Dữ liệu dạng bảng: Điểm số, báo cáo, lịch trình
- Ma trận toán học: Phép nhân ma trận, giải hệ phương trình
- Game development: Bản đồ, bàn cờ, mê cung
- Xử lý hình ảnh: Pixel, filter, transformation
💻 Ví dụ minh họa
Ví dụ 1: Quản lý điểm số lớp học
cpp
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main() {
const int MAX_STUDENTS = 10;
const int MAX_SUBJECTS = 5;
// Mảng lưu điểm: hàng = học sinh, cột = môn học
float grades[MAX_STUDENTS][MAX_SUBJECTS];
string students[MAX_STUDENTS] = {
"Nguyen Van A", "Tran Thi B", "Le Van C",
"Pham Thi D", "Hoang Van E"
};
string subjects[MAX_SUBJECTS] = {
"Toan", "Ly", "Hoa", "Sinh", "Van"
};
int numStudents = 5;
int numSubjects = 5;
// Nhập điểm
cout << "=== NHAP DIEM HOC SINH ===" << endl;
for (int i = 0; i < numStudents; i++) {
cout << "\nHoc sinh: " << students[i] << endl;
for (int j = 0; j < numSubjects; j++) {
cout << " " << subjects[j] << ": ";
cin >> grades[i][j];
}
}
// In bảng điểm
cout << "\n=== BANG DIEM LOP HOC ===" << endl;
cout << setw(15) << "Ten";
for (int j = 0; j < numSubjects; j++) {
cout << setw(8) << subjects[j];
}
cout << setw(10) << "Diem TB" << endl;
cout << string(70, '-') << endl;
for (int i = 0; i < numStudents; i++) {
cout << setw(15) << students[i];
float sum = 0;
for (int j = 0; j < numSubjects; j++) {
cout << setw(8) << fixed << setprecision(1) << grades[i][j];
sum += grades[i][j];
}
float average = sum / numSubjects;
cout << setw(10) << fixed << setprecision(2) << average << endl;
}
// Tìm học sinh có điểm trung bình cao nhất
float maxAverage = 0;
int topStudent = 0;
for (int i = 0; i < numStudents; i++) {
float sum = 0;
for (int j = 0; j < numSubjects; j++) {
sum += grades[i][j];
}
float average = sum / numSubjects;
if (average > maxAverage) {
maxAverage = average;
topStudent = i;
}
}
cout << "\nHoc sinh xuat sac nhat: " << students[topStudent]
<< " (DTB: " << fixed << setprecision(2) << maxAverage << ")" << endl;
return 0;
}Kết quả mẫu:
=== NHAP DIEM HOC SINH ===
Hoc sinh: Nguyen Van A
Toan: 8.5
Ly: 7.0
Hoa: 9.0
Sinh: 8.0
Van: 7.5
=== BANG DIEM LOP HOC ===
Ten Toan Ly Hoa Sinh Van Diem TB
----------------------------------------------------------------------
Nguyen Van A 8.5 7.0 9.0 8.0 7.5 8.00
Tran Thi B 9.0 8.5 8.0 9.5 8.0 8.60
Le Van C 7.0 6.5 7.5 7.0 8.0 7.20
Hoc sinh xuat sac nhat: Tran Thi B (DTB: 8.60)Ví dụ 2: Phép nhân ma trận
cpp
#include <iostream>
#include <iomanip>
using namespace std;
const int MAX_SIZE = 10;
void inputMatrix(int matrix[][MAX_SIZE], int rows, int cols, string name) {
cout << "Nhap ma tran " << name << " (" << rows << "x" << cols << "):" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << name << "[" << i << "][" << j << "] = ";
cin >> matrix[i][j];
}
}
}
void printMatrix(int matrix[][MAX_SIZE], int rows, int cols, string name) {
cout << "\nMa tran " << name << ":" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << setw(6) << matrix[i][j];
}
cout << endl;
}
}
void multiplyMatrix(int A[][MAX_SIZE], int B[][MAX_SIZE],
int C[][MAX_SIZE], int rowsA, int colsA, int colsB) {
// Khởi tạo ma trận kết quả
for (int i = 0; i < rowsA; i++) {
for (int j = 0; j < colsB; j++) {
C[i][j] = 0;
}
}
// Thực hiện phép nhân
for (int i = 0; i < rowsA; i++) {
for (int j = 0; j < colsB; j++) {
for (int k = 0; k < colsA; k++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
}
int main() {
int A[MAX_SIZE][MAX_SIZE], B[MAX_SIZE][MAX_SIZE], C[MAX_SIZE][MAX_SIZE];
int rowsA, colsA, rowsB, colsB;
cout << "=== PHEP NHAN MA TRAN ===" << endl;
// Nhập kích thước ma trận A
cout << "Nhap kich thuoc ma tran A:" << endl;
cout << "So hang: "; cin >> rowsA;
cout << "So cot: "; cin >> colsA;
// Nhập kích thước ma trận B
cout << "Nhap kich thuoc ma tran B:" << endl;
cout << "So hang: "; cin >> rowsB;
cout << "So cot: "; cin >> colsB;
// Kiểm tra điều kiện nhân ma trận
if (colsA != rowsB) {
cout << "Khong the nhan hai ma tran nay!" << endl;
cout << "So cot cua A (" << colsA << ") phai bang so hang cua B ("
<< rowsB << ")" << endl;
return 1;
}
// Nhập dữ liệu
inputMatrix(A, rowsA, colsA, "A");
inputMatrix(B, rowsB, colsB, "B");
// Thực hiện phép nhân
multiplyMatrix(A, B, C, rowsA, colsA, colsB);
// In kết quả
printMatrix(A, rowsA, colsA, "A");
printMatrix(B, rowsB, colsB, "B");
printMatrix(C, rowsA, colsB, "C = A x B");
return 0;
}Ví dụ 3: Game Tic-Tac-Toe (X-O)
cpp
#include <iostream>
using namespace std;
const int BOARD_SIZE = 3;
void initBoard(char board[][BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
board[i][j] = ' ';
}
}
}
void printBoard(char board[][BOARD_SIZE]) {
cout << "\n 0 1 2" << endl;
cout << " -----------" << endl;
for (int i = 0; i < BOARD_SIZE; i++) {
cout << i << " |";
for (int j = 0; j < BOARD_SIZE; j++) {
cout << " " << board[i][j] << " |";
}
cout << endl;
cout << " -----------" << endl;
}
}
bool isValidMove(char board[][BOARD_SIZE], int row, int col) {
return (row >= 0 && row < BOARD_SIZE &&
col >= 0 && col < BOARD_SIZE &&
board[row][col] == ' ');
}
bool checkWin(char board[][BOARD_SIZE], char player) {
// Kiểm tra hàng
for (int i = 0; i < BOARD_SIZE; i++) {
if (board[i][0] == player && board[i][1] == player && board[i][2] == player) {
return true;
}
}
// Kiểm tra cột
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[0][j] == player && board[1][j] == player && board[2][j] == player) {
return true;
}
}
// Kiểm tra đường chéo
if (board[0][0] == player && board[1][1] == player && board[2][2] == player) {
return true;
}
if (board[0][2] == player && board[1][1] == player && board[2][0] == player) {
return true;
}
return false;
}
bool isBoardFull(char board[][BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == ' ') {
return false;
}
}
}
return true;
}
int main() {
char board[BOARD_SIZE][BOARD_SIZE];
char currentPlayer = 'X';
int row, col;
initBoard(board);
cout << "=== GAME TIC-TAC-TOE ===" << endl;
cout << "Nguoi choi X di truoc!" << endl;
while (true) {
printBoard(board);
cout << "\nLuot cua nguoi choi " << currentPlayer << endl;
cout << "Nhap hang (0-2): "; cin >> row;
cout << "Nhap cot (0-2): "; cin >> col;
if (!isValidMove(board, row, col)) {
cout << "Nuoc di khong hop le! Thu lai." << endl;
continue;
}
board[row][col] = currentPlayer;
if (checkWin(board, currentPlayer)) {
printBoard(board);
cout << "\nNguoi choi " << currentPlayer << " thang!" << endl;
break;
}
if (isBoardFull(board)) {
printBoard(board);
cout << "\nHoa! Ban co day!" << endl;
break;
}
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}
return 0;
}🏋️ Thực hành
Bài tập 1: Tìm phần tử lớn nhất trong ma trận
cpp
#include <iostream>
#include <climits>
using namespace std;
const int MAX_SIZE = 100;
void findMaxElement(int matrix[][MAX_SIZE], int rows, int cols) {
int maxVal = INT_MIN;
int maxRow = 0, maxCol = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (matrix[i][j] > maxVal) {
maxVal = matrix[i][j];
maxRow = i;
maxCol = j;
}
}
}
cout << "Phan tu lon nhat: " << maxVal << endl;
cout << "Tai vi tri: hang " << maxRow << ", cot " << maxCol << endl;
}
int main() {
int matrix[MAX_SIZE][MAX_SIZE];
int rows, cols;
cout << "Nhap so hang: "; cin >> rows;
cout << "Nhap so cot: "; cin >> cols;
cout << "Nhap cac phan tu cua ma tran:" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << "matrix[" << i << "][" << j << "] = ";
cin >> matrix[i][j];
}
}
findMaxElement(matrix, rows, cols);
return 0;
}Bài tập 2: Tính tổng các phần tử trên đường chéo
cpp
#include <iostream>
using namespace std;
const int MAX_SIZE = 100;
void calculateDiagonalSums(int matrix[][MAX_SIZE], int n) {
int mainDiagonalSum = 0; // Đường chéo chính
int antiDiagonalSum = 0; // Đường chéo phụ
for (int i = 0; i < n; i++) {
mainDiagonalSum += matrix[i][i]; // (0,0), (1,1), (2,2)...
antiDiagonalSum += matrix[i][n-1-i]; // (0,n-1), (1,n-2)...
}
cout << "Tong duong cheo chinh: " << mainDiagonalSum << endl;
cout << "Tong duong cheo phu: " << antiDiagonalSum << endl;
}
int main() {
int matrix[MAX_SIZE][MAX_SIZE];
int n;
cout << "Nhap kich thuoc ma tran vuong (n x n): ";
cin >> n;
cout << "Nhap cac phan tu cua ma tran:" << endl;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << "matrix[" << i << "][" << j << "] = ";
cin >> matrix[i][j];
}
}
calculateDiagonalSums(matrix, n);
return 0;
}Bài tập 3: Chuyển vị ma trận
cpp
#include <iostream>
#include <iomanip>
using namespace std;
const int MAX_SIZE = 100;
void transposeMatrix(int original[][MAX_SIZE], int transposed[][MAX_SIZE],
int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
transposed[j][i] = original[i][j];
}
}
}
void printMatrix(int matrix[][MAX_SIZE], int rows, int cols, string title) {
cout << "\n" << title << ":" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << setw(4) << matrix[i][j];
}
cout << endl;
}
}
int main() {
int original[MAX_SIZE][MAX_SIZE];
int transposed[MAX_SIZE][MAX_SIZE];
int rows, cols;
cout << "Nhap so hang: "; cin >> rows;
cout << "Nhap so cot: "; cin >> cols;
cout << "Nhap cac phan tu cua ma tran:" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << "matrix[" << i << "][" << j << "] = ";
cin >> original[i][j];
}
}
transposeMatrix(original, transposed, rows, cols);
printMatrix(original, rows, cols, "Ma tran goc");
printMatrix(transposed, cols, rows, "Ma tran chuyen vi");
return 0;
}Bài tập 4: Sắp xếp ma trận theo hàng
cpp
#include <iostream>
#include <algorithm>
#include <iomanip>
using namespace std;
const int MAX_SIZE = 100;
void sortMatrixRows(int matrix[][MAX_SIZE], int rows, int cols) {
for (int i = 0; i < rows; i++) {
// Sắp xếp từng hàng
sort(matrix[i], matrix[i] + cols);
}
}
void printMatrix(int matrix[][MAX_SIZE], int rows, int cols, string title) {
cout << "\n" << title << ":" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << setw(4) << matrix[i][j];
}
cout << endl;
}
}
int main() {
int matrix[MAX_SIZE][MAX_SIZE];
int rows, cols;
cout << "Nhap so hang: "; cin >> rows;
cout << "Nhap so cot: "; cin >> cols;
cout << "Nhap cac phan tu cua ma tran:" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << "matrix[" << i << "][" << j << "] = ";
cin >> matrix[i][j];
}
}
printMatrix(matrix, rows, cols, "Ma tran goc");
sortMatrixRows(matrix, rows, cols);
printMatrix(matrix, rows, cols, "Ma tran sau khi sap xep tung hang");
return 0;
}Bài tập 5: Tìm kiếm trong ma trận đã sắp xếp
cpp
#include <iostream>
using namespace std;
const int MAX_SIZE = 100;
bool searchInSortedMatrix(int matrix[][MAX_SIZE], int rows, int cols, int target) {
int row = 0;
int col = cols - 1; // Bắt đầu từ góc phải trên
while (row < rows && col >= 0) {
if (matrix[row][col] == target) {
cout << "Tim thay " << target << " tai vi tri (" << row << ", " << col << ")" << endl;
return true;
}
else if (matrix[row][col] > target) {
col--; // Di chuyển sang trái
}
else {
row++; // Di chuyển xuống dưới
}
}
return false;
}
int main() {
// Ma trận đã sắp xếp (tăng dần theo hàng và cột)
int matrix[MAX_SIZE][MAX_SIZE] = {
{1, 4, 7, 11},
{2, 5, 8, 12},
{3, 6, 9, 16},
{10, 13, 14, 17}
};
int rows = 4, cols = 4;
int target;
cout << "Ma tran da sap xep:" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << setw(4) << matrix[i][j];
}
cout << endl;
}
cout << "\nNhap gia tri can tim: ";
cin >> target;
if (!searchInSortedMatrix(matrix, rows, cols, target)) {
cout << "Khong tim thay " << target << " trong ma tran!" << endl;
}
return 0;
}📋 Tóm tắt
Các điểm quan trọng
Cú pháp cơ bản:
- Khai báo:
data_type array[rows][columns] - Truy cập:
array[i][j] - Khởi tạo:
{{row1}, {row2}, ...}
- Khai báo:
Thao tác cơ bản:
- Duyệt: Vòng lặp lồng nhau
- Tìm kiếm: Duyệt tuần tự hoặc thuật toán tối ưu
- Sắp xếp: Theo hàng, cột hoặc toàn bộ ma trận
Ứng dụng thực tế:
- Quản lý dữ liệu dạng bảng
- Tính toán ma trận toán học
- Game development (bàn cờ, mê cung)
- Xử lý hình ảnh cơ bản
Best Practices
- Sử dụng const cho kích thước:
const int MAX_SIZE = 100 - Kiểm tra bounds: Đảm bảo chỉ số không vượt quá giới hạn
- Tách riêng logic: Viết hàm cho từng thao tác cụ thể
- Đặt tên có ý nghĩa:
studentGradesthay vìarr - Xử lý input validation: Kiểm tra dữ liệu đầu vào
Chuẩn bị cho bài tiếp theo
Bài tiếp theo chúng ta sẽ học về Chuỗi ký tự (Strings) - một cấu trúc dữ liệu quan trọng khác trong C++. Chuỗi giúp chúng ta:
- Xử lý văn bản và dữ liệu text
- Thao tác với tên, địa chỉ, thông tin người dùng
- Xây dựng các ứng dụng xử lý ngôn ngữ tự nhiên
- Làm việc với file text và dữ liệu đầu vào phức tạp
Hãy luyện tập thật nhiều với mảng 2 chiều vì đây là nền tảng cho nhiều thuật toán và cấu trúc dữ liệu nâng cao hơn!