Bài 24.3: Ứng dụng mảng 1 chiều trong bài toán thực tế trong C++

Chào mừng các bạn quay trở lại với series blog về C++! Trong những bài học trước, chúng ta đã làm quen với cú pháp cơ bản và cấu trúc điều khiển. Hôm nay, chúng ta sẽ đi sâu vào một cấu trúc dữ liệu cực kỳ quan trọngphổ biến: mảng một chiều (one-dimensional array).

Mảng một chiều cho phép chúng ta lưu trữ một tập hợp các phần tử có cùng kiểu dữ liệu dưới một tên biến duy nhất. Điều này vô cùng tiện lợi khi bạn cần xử lý một lượng lớn dữ liệu liên quan đến nhau, thay vì phải khai báo hàng trăm biến riêng lẻ.

Trong C++, bạn có thể sử dụng mảng theo kiểu C truyền thống (ví dụ: int arr[10];) hoặc sử dụng vector từ thư viện chuẩn. vector linh hoạt hơn nhiều vì nó có thể tự động thay đổi kích thước, là lựa chọn ưu việt hơn trong hầu hết các trường hợp thực tế. Trong bài viết này, chúng ta sẽ chủ yếu sử dụng vector để minh họa các ứng dụng.

Hãy cùng khám phá sức mạnh tiềm ẩn của mảng một chiều qua các bài toán thực tế nhé!

Mảng 1 Chiều Hoạt Động Như Thế Nào?

Trước khi đi vào ứng dụng, hãy nhắc lại một chút về cách mảng hoạt động.

Một mảng (hoặc vector) có kích thước cố định hoặc động, chứa các phần tử được sắp xếp liên tục trong bộ nhớ. Mỗi phần tử được gán một chỉ số (index) duy nhất để truy cập. Điều đặc biệt trong C++ (và nhiều ngôn ngữ khác) là chỉ số mảng luôn bắt đầu từ 0.

Ví dụ, một mảng có 5 phần tử sẽ có các chỉ số từ 0 đến 4.

#include <iostream>
#include <vector> // Bao gom thu vien vector

int main() {
    // Khai bao mot vector (mang dong) voi 5 phan tu kieu int
    vector<int> myNumbers(5);

    // Gan gia tri cho cac phan tu su dung chi so
    myNumbers[0] = 10; // Phan tu dau tien (chi so 0)
    myNumbers[1] = 20; // Phan tu thu hai (chi so 1)
    myNumbers[2] = 30;
    myNumbers[3] = 40;
    myNumbers[4] = 50; // Phan tu cuoi cung (chi so 4)

    // Truy cap va in gia tri cua mot phan tu
    cout << "Gia tri tai chi so 2: " << myNumbers[2] << endl; // Output: Gia tri tai chi so 2: 30

    // In tat ca cac phan tu su dung vong lap
    cout << "Tat ca cac phan tu: ";
    for (int i = 0; i < myNumbers.size(); ++i) {
        cout << myNumbers[i] << " ";
    }
    cout << endl; // Output: Tat ca cac phan tu: 10 20 30 40 50

    return 0;
}

Giải thích code:

  • vector<int> myNumbers(5); khai báo một vector tên là myNumbers có thể chứa các số nguyên (int) và có kích thước ban đầu là 5.
  • Chúng ta sử dụng myNumbers[i] để truy cập đến phần tử ở chỉ số i. Lưu ý rằng chỉ số i chạy từ 0 đến kích thước - 1.
  • Vòng lặp for với myNumbers.size() giúp duyệt qua tất cả các phần tử của vector một cách an toàntiện lợi.

Nắm vững cách truy cập bằng chỉ số là chìa khóa để làm việc với mảng. Bây giờ, hãy xem nó được áp dụng như thế nào trong các tình huống thực tế nhé!

Ứng Dụng 1: Quản lý Danh sách Điểm Học Sinh

Đây là một bài toán rất phổ biến. Bạn cần lưu trữ điểm của N học sinh và thực hiện các thao tác như tính điểm trung bình, tìm điểm cao nhất, thấp nhất. Mảng 1 chiều (hoặc vector) là lựa chọn hoàn hảo cho việc này.

Chúng ta sẽ tạo một vector<double> để lưu điểm của từng học sinh.

#include <iostream>
#include <vector>
#include <numeric> // Bao gom thu vien de dung accumulate
#include <algorithm> // Bao gom thu vien de dung max_element, min_element
#include <iomanip> // Bao gom thu vien de dinh dang output

int main() {
    int numStudents;
    cout << "Nhap so luong hoc sinh: ";
    cin >> numStudents;

    if (numStudents <= 0) {
        cout << "So luong hoc sinh phai lon hon 0.\n";
        return 1; // Bao loi
    }

    // Khai bao vector de luu diem cua cac hoc sinh
    vector<double> scores(numStudents);

    // Nhap diem cho tung hoc sinh
    cout << "Nhap diem cua " << numStudents << " hoc sinh:\n";
    for (int i = 0; i < numStudents; ++i) {
        cout << "Diem hoc sinh " << i + 1 << ": ";
        cin >> scores[i];
    }

    // --- Xu ly du lieu su dung mang ---

    // Tinh tong diem de tinh trung binh
    double sum = 0;
    for (int i = 0; i < scores.size(); ++i) {
        sum += scores[i];
    }
    // Hoac su dung accumulate tu thu vien numeric:
    // double sum = accumulate(scores.begin(), scores.end(), 0.0);


    double average = sum / scores.size();

    // Tim diem cao nhat va thap nhat
    double maxScore = scores[0]; // Gia su diem dau tien la cao nhat ban dau
    double minScore = scores[0]; // Gia su diem dau tien la thap nhat ban dau

    for (int i = 1; i < scores.size(); ++i) {
        if (scores[i] > maxScore) {
            maxScore = scores[i];
        }
        if (scores[i] < minScore) {
            minScore = scores[i];
        }
    }
    // Hoac su dung max_element va min_element tu thu vien algorithm:
    // double maxScore = *max_element(scores.begin(), scores.end());
    // double minScore = *min_element(scores.begin(), scores.end());


    // Hien thi ket qua
    cout << fixed << setprecision(2); // Dinh dang so thap phan
    cout << "\n--- Ket Qua Phan Tich Diem ---\n";
    cout << "Diem trung binh lop: " << average << endl;
    cout << "Diem cao nhat: " << maxScore << endl;
    cout << "Diem thap nhat: " << minScore << endl;

    return 0;
}

Giải thích code:

  1. Chúng ta yêu cầu người dùng nhập số lượng học sinh để xác định kích thước của vector.
  2. vector<double> scores(numStudents); tạo một vector có kích thước chính xác bằng số học sinh, sẵn sàng chứa các điểm kiểu double.
  3. Vòng lặp for đầu tiên được sử dụng để nhập điểm vào từng phần tử của vector thông qua chỉ số i.
  4. Vòng lặp for thứ hai tính tổng các điểm. Chúng ta duyệt qua vector và cộng giá trị của từng phần tử vào biến sum.
  5. Điểm trung bình được tính bằng cách chia tổng cho kích thước của vector (scores.size()).
  6. Để tìm điểm cao nhất/thấp nhất, chúng ta khởi tạo maxScoreminScore bằng điểm đầu tiên, sau đó duyệt qua phần còn lại của vector (từ chỉ số 1 trở đi), so sánh từng điểm với maxScoreminScore hiện tại và cập nhật nếu cần.
  7. fixedsetprecision(2) giúp định dạng kết quả điểm trung bình hiển thị với 2 chữ số thập phân.
  8. Tôi cũng để lại các dòng code sử dụng accumulate, max_element, min_element trong comment. Đây là các hàm có sẵn trong thư viện chuẩn C++ giúp thực hiện các tác vụ này một cách ngắn gọnhiệu quả hơn, cho thấy sức mạnh của việc kết hợp mảng với các công cụ có sẵn.

Ứng dụng này cho thấy mảng là một cách tự nhiên để biểu diễn và xử lý một tập hợp dữ liệu cùng loại.

Ứng Dụng 2: Thống kê Tần suất Xuất Hiện

Giả sử bạn có một danh sách các số nguyên (ví dụ: kết quả khảo sát từ 1 đến 5, hoặc số lần truy cập trang web trong một giờ) và muốn biết mỗi số xuất hiện bao nhiêu lần. Mảng có thể được sử dụng như một bộ đếm tần suất.

Chúng ta sẽ dùng chỉ số mảng để đại diện cho giá trị cần đếm, và giá trị tại chỉ số đó sẽ lưu số lần xuất hiện của nó.

#include <iostream>
#include <vector>

int main() {
    // Gia su cac gia tri can thong ke nam trong pham vi tu 0 den 10
    const int MAX_VALUE = 10;

    // Khai bao vector de luu tan suat
    // Kich thuoc la MAX_VALUE + 1 vi chung ta muon dem ca gia tri 0
    // Moi phan tu ban dau duoc khoi tao bang 0
    vector<int> frequency(MAX_VALUE + 1, 0);

    cout << "Nhap cac so nguyen (nhap so am de ket thuc):\n";

    int value;
    while (cin >> value && value >= 0) {
        if (value <= MAX_VALUE) {
            // Tang bo dem tai chi muc tuong ung voi gia tri nhap vao
            frequency[value]++;
        } else {
            cout << "Gia tri " << value << " vuot qua pham vi (" << MAX_VALUE << ") va bi bo qua.\n";
        }
    }

    // Hien thi ket qua thong ke tan suat
    cout << "\n--- Thong Ke Tan Suat ---\n";
    for (int i = 0; i <= MAX_VALUE; ++i) {
        if (frequency[i] > 0) {
            cout << "Gia tri " << i << " xuat hien " << frequency[i] << " lan.\n";
        }
    }

    return 0;
}

Giải thích code:

  1. const int MAX_VALUE = 10; định nghĩa phạm vi giá trị chúng ta muốn thống kê (từ 0 đến 10).
  2. vector<int> frequency(MAX_VALUE + 1, 0); tạo một vector có kích thước 11 (để đếm cho các giá trị 0 đến 10). Tất cả các phần tử được khởi tạo bằng 0. Mỗi chỉ số i của vector này sẽ đại diện cho giá trị i mà chúng ta muốn đếm tần suất.
  3. Vòng lặp while đọc các số nguyên từ input.
  4. Bên trong vòng lặp, nếu giá trị value đọc được nằm trong phạm vi hợp lệ (0 đến 10), chúng ta thực hiện frequency[value]++;. Thao tác này tăng giá trị tại chỉ số value lên 1, hiệu quả là đếm số lần giá trị value xuất hiện.
  5. Sau khi người dùng nhập số âm để kết thúc, vòng lặp for cuối cùng sẽ duyệt qua vector frequency từ chỉ số 0 đến MAX_VALUE. Nếu giá trị frequency[i] lớn hơn 0, điều đó có nghĩa là giá trị i đã xuất hiện ít nhất một lần, và chúng ta in ra tần suất của nó.

Cách sử dụng mảng làm bộ đếm tần suất này rất hiệu quả cho các bài toán mà phạm vi giá trị cần đếm là nhỏ và cố định.

Ứng Dụng 3: Tìm kiếm Phần tử trong Danh Sách

Một trong những thao tác cơ bản nhất với dữ liệu là tìm kiếm. Làm thế nào để biết một phần tử cụ thể có tồn tại trong mảng hay không, và nếu có thì nó ở vị trí nào? Mảng 1 chiều cho phép chúng ta thực hiện việc này một cách dễ dàng bằng cách duyệt qua từng phần tử.

Đây là minh họa cho thuật toán tìm kiếm tuyến tính (linear search):

#include <iostream>
#include <vector>
#include <algorithm> // De su dung find (khong dung trong vi du chinh, chi de tham khao)

int main() {
    // Mot vector chua du lieu mau
    vector<int> data = {12, 34, 5, 8, 45, 20, 99, 1, 34};

    int target;
    cout << "Nhap so can tim trong danh sach: ";
    cin >> target;

    // --- Thuc hien tim kiem tuyen tinh ---
    bool found = false;
    int foundIndex = -1; // Bien luu chi so tim thay (mac dinh la -1 neu khong tim thay)

    // Duyet qua tung phan tu cua mang
    for (int i = 0; i < data.size(); ++i) {
        if (data[i] == target) {
            // Neu tim thay phan tu trung khop
            found = true;
            foundIndex = i;
            break; // Tim thay roi thi thoat vong lap ngay
        }
    }

    // Hien thi ket qua
    if (found) {
        cout << "Da tim thay so " << target << " tai chi muc: " << foundIndex << endl;
    } else {
        cout << "Khong tim thay so " << target << " trong danh sach.\n";
    }

    /*
    // Cach 2: Su dung find (cach hien dai hon, tra ve iterator)
    auto it = find(data.begin(), data.end(), target);

    if (it != data.end()) {
        // Tinh chi muc tu iterator
        int index_std = distance(data.begin(), it);
        cout << "Da tim thay so " << target << " tai chi muc (su dung find): " << index_std << endl;
    } else {
        cout << "Khong tim thay so " << target << " trong danh sach (su dung find).\n";
    }
    */

    return 0;
}

Giải thích code:

  1. Chúng ta có một vector data chứa sẵn các số.
  2. Người dùng nhập target là số mà họ muốn tìm.
  3. Chúng ta khởi tạo biến foundfalsefoundIndex-1.
  4. Vòng lặp for duyệt qua vector từ chỉ số 0 đến cuối.
  5. Trong mỗi lần lặp, chúng ta so sánh data[i] với target.
  6. Nếu tìm thấy sự khớp (data[i] == target), chúng ta đặt found thành true, lưu lại chỉ số i vào foundIndex, và quan trọng là sử dụng break; để thoát khỏi vòng lặp ngay lập tức vì không cần tìm kiếm thêm nữa.
  7. Sau vòng lặp, chúng ta kiểm tra giá trị của found để thông báo kết quả cho người dùng.

Tìm kiếm tuyến tính là phương pháp đơn giản nhất, phù hợp với các mảng nhỏ hoặc khi dữ liệu không được sắp xếp. Đối với mảng lớn đã được sắp xếp, các thuật toán tìm kiếm hiệu quả hơn như tìm kiếm nhị phân (binary search) sẽ được sử dụng, nhưng chúng vẫn dựa trên cấu trúc mảng.

Tôi cũng đưa vào phần comment cách sử dụng find từ thư viện <algorithm>. Đây là cách C++ hiện đại để thực hiện tìm kiếm, trả về một iterator trỏ đến phần tử đầu tiên tìm thấy (hoặc data.end() nếu không tìm thấy).

Ứng Dụng 4: Lưu trữ Dữ liệu Hằng số hoặc Cố định

Đôi khi, bạn cần lưu trữ một tập hợp các giá trị liên quan nhưng không thay đổi trong quá trình chạy chương trình. Ví dụ điển hình là số ngày trong mỗi tháng, hoặc tên của các ngày trong tuần. Mảng là một cách gọn gàngtrực quan để làm điều này.

#include <iostream>
#include <vector>
#include <string> // De su dung string

int main() {
    // Luu tru so ngay trong cac thang (khong tinh nam nhuan)
    // Chi so 0 khong su dung de thang 1 ung voi chi so 1, thang 12 ung voi chi so 12
    vector<int> daysInMonth = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    // Luu tru ten cac ngay trong tuan
    vector<string> dayNames = {"", "Chu Nhat", "Thu Hai", "Thu Ba", "Thu Tu", "Thu Nam", "Thu Sau", "Thu Bay"};
    // Luu y: cach dung chi so co the khac nhau tuy bai toan (bat dau tu 0 hoac 1)

    // Vi du 1: Tra cuu so ngay cua mot thang
    int month;
    cout << "Nhap so thang (1-12): ";
    cin >> month;

    if (month >= 1 && month <= 12) {
        cout << "Thang " << month << " co " << daysInMonth[month] << " ngay (khong tinh nam nhuan).\n";
    } else {
        cout << "So thang khong hop le.\n";
    }

    cout << "\n"; // Xuong dong

    // Vi du 2: In ra ten cac ngay trong tuan
    cout << "Cac ngay trong tuan:\n";
    for (int i = 1; i <= 7; ++i) { // Bat dau tu chi so 1 den 7
         cout << dayNames[i] << endl;
    }


    return 0;
}

Giải thích code:

  1. Chúng ta khai báo vector<int> daysInMonth và trực tiếp khởi tạo nó với các giá trị số ngày tương ứng. Để tiện lợi, chúng ta thêm một phần tử "rỗng" (giá trị 0) ở chỉ số 0 để chỉ số của tháng (1-12) khớp với chỉ số mảng (1-12).
  2. Tương tự, vector<string> dayNames lưu tên các ngày. Ở đây tôi cũng dùng chi số 1-7 cho Chủ Nhật đến Thứ Bảy.
  3. Khi cần biết số ngày của một tháng, chúng ta chỉ cần truy cập daysInMonth[month].
  4. Khi cần in tên các ngày, chúng ta duyệt từ chỉ số 1 đến 7 trên vector dayNames.

Việc sử dụng mảng cho dữ liệu cố định giúp code trở nên dễ đọcdễ bảo trì hơn rất nhiều so với việc sử dụng các biến riêng lẻ hoặc cấu trúc if-else lồng nhau phức tạp.

Tóm Lại Sức Mạnh của Mảng 1 Chiều

Qua các ví dụ trên, bạn có thể thấy mảng 1 chiều, đặc biệt là vector trong C++, là một công cụ vô cùng linh hoạthiệu quả để:

  • Lưu trữ một tập hợp dữ liệu cùng kiểu.
  • Truy cập dữ liệu nhanh chóng thông qua chỉ số.
  • Thực hiện các thao tác hàng loạt (duyệt, tính tổng, tìm kiếm) trên tập dữ liệu.
  • Biểu diễn các danh sách, chuỗi, hoặc các tập dữ liệu liên quan trong bài toán thực tế.

Comments

There are no comments at the moment.