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

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ọng và phổ 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ộtvector
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
đếnkích thước - 1
. - Vòng lặp
for
vớimyNumbers.size()
giúp duyệt qua tất cả các phần tử của vector một cách an toàn và tiệ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:
- 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
. 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ểudouble
.- 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
. - 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ếnsum
. - Đ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()
). - Để tìm điểm cao nhất/thấp nhất, chúng ta khởi tạo
maxScore
vàminScore
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ớimaxScore
vàminScore
hiện tại và cập nhật nếu cần. fixed
vàsetprecision(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.- 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ọn và hiệ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:
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).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.- Vòng lặp
while
đọc các số nguyên từ input. - 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ệnfrequency[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. - 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 vectorfrequency
từ chỉ số 0 đếnMAX_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:
- Chúng ta có một vector
data
chứa sẵn các số. - Người dùng nhập
target
là số mà họ muốn tìm. - Chúng ta khởi tạo biến
found
làfalse
vàfoundIndex
là-1
. - Vòng lặp
for
duyệt qua vector từ chỉ số 0 đến cuối. - Trong mỗi lần lặp, chúng ta so sánh
data[i]
vớitarget
. - Nếu tìm thấy sự khớp (
data[i] == target
), chúng ta đặtfound
thànhtrue
, lưu lại chỉ sối
vàofoundIndex
, và quan trọng là sử dụngbreak;
để 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. - 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àng và trự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:
- 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). - 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. - 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]
. - 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ễ đọc và dễ 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ạt và hiệ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