Skip to content

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 2

Duyệ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]=6

Cá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

  1. 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}, ...}
  2. 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
  3. Ứ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: studentGrades thay 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!

Khóa học C++ miễn phí