L5. Mảng (Array) trong C++

Phần 1: Mảng Một Chiều

1. Giới Thiệu Về Mảng

Đặt vấn đề:

// Cần lưu trữ 3 số thực
float a, b, c;  // OK

// Cần lưu trữ 10 số thực
float a1, a2, a3, ..., a10;  // Khó quản lý

// Cần lưu trữ 100 hoặc 1000 số thực
// → Không thể khai báo 100 hoặc 1000 biến!

Giải pháp: Sử dụng MẢNG

┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│1.3 │9.4 │2.7 │6.2 │4.9 │7.7 │3.5 │8.6 │0.1 │5.4 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
  0    1    2    3    4    5    6    7    8    9

2. Khái Niệm Mảng

Định nghĩa:

Mảng là một dãy các phần tử có cùng kiểu dữ liệu, mỗi phần tử biểu diễn một giá trị.

Đặc điểm:

  • Kích thước mảng được xác định ngay khi khai báo và không thay đổi
  • Là kiểu dữ liệu có cấu trúc do người lập trình định nghĩa
  • C++ luôn chỉ định một khối nhớ liên tục cho mảng

Ví dụ minh họa:

Mảng 1 chiều:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 5 │ T │ 8 │ B │ 2 │ R │ 7 │ K │ 1 │
└───┴───┴───┴───┴───┴───┴───┴───┴───┘

Mảng 2 chiều (Ma trận):
┌───┬───┐      ┌───┬───┬───┐      ┌───┬───┐
│ 3 │ 7 │      │ 3 │ 7 │ 8 │      │ 6 │ 7 │
├───┼───┤      ├───┼───┼───┤      ├───┼───┤
│ 6 │ 1 │      │ 6 │ 1 │ 4 │      │ 1 │ 1 │
└───┴───┘      └───┴───┴───┘      ├───┼───┤
 2×2 (vuông)    2×3 (dòng<cột)    │ 6 │ 3 │
                                   └───┴───┘
                                   3×2 (dòng>cột)

3. Các Yếu Tố Xác Định Mảng

Một mảng được xác định bởi 4 yếu tố:

graph TD A[Mảng] --> B[1. Tên mảng] A --> C[2. Kiểu dữ liệu] A --> D[3. Số chiều] A --> E[4. Kích thước]

Ví dụ 1:

char MangKyTu[4];
  • Tên mảng: MangKyTu
  • Kiểu mảng: char
  • Số chiều: 1 chiều
  • Kích thước: 4 phần tử

Ví dụ 2:

int MangSoNguyen[3][2];
  • Tên mảng: MangSoNguyen
  • Kiểu mảng: int
  • Số chiều: 2 chiều
  • Kích thước: 3 dòng × 2 cột

4. Mảng Một Chiều

4.1. Khai Báo Mảng 1 Chiều

Cú pháp:

<Kiểu_dữ_liệu> <Tên_biến_mảng>[<Số_phần_tử>];

Ví dụ:

char A[10];
// Kiểu dữ liệu: char
// Tên biến mảng: A
// Số phần tử: 10

int Mang1Chieu[30];
// Kiểu dữ liệu: int
// Tên biến mảng: Mang1Chieu
// Số phần tử: 30

4.2. Khởi Tạo Mảng 1 Chiều

Cách 1: Khởi tạo đầy đủ

int A[4] = {29, 137, 50, 4};
┌────┬─────┬────┬───┐
│ 29 │ 137 │ 50 │ 4 │
└────┴─────┴────┴───┘
  0     1     2    3

Cách 2: Khởi tạo một phần

int B[4] = {91, 106};
┌────┬─────┬───┬───┐
│ 91 │ 106 │ 0 │ 0 │  ← Phần còn lại = 0
└────┴─────┴───┴───┘
  0     1     2   3

Cách 3: Khởi tạo tất cả = 0

int a[4] = {0};
┌───┬───┬───┬───┐
│ 0 │ 0 │ 0 │ 0 │
└───┴───┴───┴───┘
  0   1   2   3

Cách 4: Tự động xác định kích thước

int a[] = {22, 16, 56, 19};
// Tự động tạo mảng 4 phần tử
┌────┬────┬────┬────┐
│ 22 │ 16 │ 56 │ 19 │
└────┴────┴────┴────┘
  0    1    2    3

4.3. Chỉ Số Mảng

Đặc điểm:

  • Chỉ số mảng là giá trị số nguyên int
  • Chỉ số bắt đầu từ 0
  • Chỉ số tối đa = Số phần tử - 1
int A[5];

Các chỉ số hợp lệ: 0, 1, 2, 3, 4

┌────┬────┬────┬────┬────┐
│ 99 │ 17 │ 50 │ 43 │ 72 │
└────┴────┴────┴────┴────┘
  0    1    2    3    4
  ↑                    ↑
Chỉ số đầu       Chỉ số cuối

Số lượng chỉ số = Số phần tử = 5

4.4. Truy Xuất Phần Tử Mảng

Cú pháp:

<Tên_biến_mảng>[<chỉ_số>]

Ví dụ:

int A[4] = {29, 137, 50, 4};

// Truy xuất hợp lệ
A[0]  // = 29
A[1]  // = 137
A[2]  // = 50
A[3]  // = 4

// Truy xuất KHÔNG hợp lệ
A[-1] // SAI: chỉ số âm
A[4]  // SAI: vượt quá giới hạn
A[5]  // SAI: vượt quá giới hạn
┌────┬─────┬────┬───┐
│ 29 │ 137 │ 50 │ 4 │
└────┴─────┴────┴───┘
  0     1     2    3
A[0] = 29

4.5. Địa Chỉ Các Phần Tử Mảng

Cú pháp:

&<Tên_biến_mảng>[<chỉ_số>];

Ví dụ:

int A[4] = {29, 137, 50, 4};

// Địa chỉ các phần tử
&A[0]  // Địa chỉ phần tử thứ 0
&A[1]  // Địa chỉ phần tử thứ 1
&A[2]  // Địa chỉ phần tử thứ 2
&A[3]  // Địa chỉ phần tử thứ 3
         ┌────┬─────┬────┬───┐
Giá trị: │ 29 │ 137 │ 50 │ 4 │
         └────┴─────┴────┴───┘
Địa chỉ:  0x10  0x14  0x18 0x1C
Chỉ số:    0     1     2    3

4.6. Truyền Mảng Cho Hàm

Khai báo tham số:

void SapXep(int A[100], int n);
// Tên hàm: SapXep
// Tham số: mảng số nguyên A và số lượng phần tử n
// Giá trị trả về: void

int TinhTong(int A[100], int n);
// Tên hàm: TinhTong
// Tham số: mảng số nguyên A và số lượng phần tử n
// Giá trị trả về: int

Ví dụ đầy đủ:

#include <stdio.h>
#include <conio.h>

void Nhap(int A[], int &N);   // Nhập mảng
void Xuat(int A[], int N);    // Xuất mảng
int TinhTong(int A[], int N); // Tính tổng

void main() { 
    int a[100], n, S; 
    Nhap(a, n); 
    Xuat(a, n);  
    S = TinhTong(a, n); 
    cout << "Tong cac phan tu trong mang: " << S;
}

5. Các Tác Vụ Trên Mảng 1 Chiều

5.1. Nhập Mảng

void nhapmang(int A[], int N) {
    for (int i = 0; i < N; i++) {
        cout << "Nhap phan tu thu " << i << ": ";
        cin >> A[i];
    }
}

5.2. Xuất Mảng

void xuatmang(int A[], int N) {
    for (int i = 0; i < N; i++) {
        cout << A[i] << " ";
    }
}

5.3. Tìm Kiếm Phần Tử

int TimKiem(int a[], int n, int x) {
    for (int vt = 0; vt < n; vt++) {
        if (a[vt] == x)
            return vt;  // Trả về vị trí tìm thấy
    }
    return -1;  // Không tìm thấy
}

5.4. Tìm Giá Trị Lớn Nhất

int Max(int a[], int n) { 
    int Max = a[0];  // Giả sử phần tử đầu là lớn nhất
    for (int i = 1; i < n; i++) {
        if (Max < a[i])  // Áp dụng kỹ thuật lính canh
            Max = a[i]; 
    }
    return Max; 
}

5.5. Kiểm Tra Tính Chất Mảng

Yêu cầu: Kiểm tra mảng có toàn số nguyên tố không?

Ý tưởng 1: Đếm số lượng số nguyên tố, nếu = n thì toàn nguyên tố

// Hàm kiểm tra số nguyên tố
int LaSNT(int n) { 
    if (n < 2) return 0;
    for (int i = 2; i < n; i++) {
        if (n % i == 0) 
            return 0;  // Không phải SNT
    }
    return 1;  // Là SNT
}

// Ý tưởng 1
int KiemTra_YT1(int a[], int n) { 
    int dem = 0; 
    for (int i = 0; i < n; i++) {
        if (LaSNT(a[i]) == 1)
            dem++; 
    }
    if (dem == n) 
        return 1;  // Toàn SNT
    return 0; 
}

Ý tưởng 2: Đếm số không phải nguyên tố, nếu = 0 thì toàn nguyên tố

int KiemTra_YT2(int a[], int n) { 
    int dem = 0; 
    for (int i = 0; i < n; i++) {
        if (LaSNT(a[i]) == 0)  
            dem++; 
    }
    if (dem == 0) 
        return 1;  // Toàn SNT
    return 0; 
}

Ý tưởng 3: Tìm phần tử không phải nguyên tố (Tối ưu nhất)

int KiemTra_YT3(int a[], int n) { 
    for (int i = 0; i < n; i++) {
        if (LaSNT(a[i]) == 0) 
            return 0;  // Tìm thấy số không phải SNT
    }
    return 1;  // Không tìm thấy số nào không phải SNT
}

5.6. Đếm Số Lượng Phần Tử Chẵn

int DemChan(int A[], int N) {  
    int DC = 0;
    for (int i = 0; i < n; i++) {
        if (A[i] % 2 == 0)
            DC++;
    }
    return DC;
}

Sử dụng:

int main() { 
    int a[100], n, DC; 
    nhap(a, n); 
    xuat(a, n); 
    DC = DemChan(a, n);  
    printf("So ptu chan la %d", DC); 
}

5.7. Tính Tổng Các Phần Tử Chẵn

int TongChan(int A[], int N) { 
    int TC = 0;
    for (int i = 0; i < n; i++) {
        if (A[i] % 2 == 0)
            TC = TC + A[i];
    }
    return TC;
}

Sử dụng:

int main() { 
    int a[100], n, TC; 
    nhap(a, n); 
    xuat(a, n); 
    TC = TongChan(a, n);  
    printf("Tong ptu chan la %d", TC); 
}

6. Bài Tập Bắt Buộc - Mảng 1 Chiều