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>
using namespace std;
int main() {
vector<int> so(5);
so[0] = 10;
so[1] = 20;
so[2] = 30;
so[3] = 40;
so[4] = 50;
cout << "Gia tri tai chi so 2: " << so[2] << endl;
cout << "Tat ca cac phan tu: ";
for (int i = 0; i < so.size(); ++i) {
cout << so[i] << " ";
}
cout << endl;
return 0;
}
Output:
Gia tri tai chi so 2: 30
Tat ca cac phan tu: 10 20 30 40 50
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 <iomanip>
using namespace std;
int main() {
int n;
cout << "Nhap so luong hoc sinh: ";
cin >> n;
if (n <= 0) {
cout << "So luong hoc sinh phai lon hon 0.\n";
return 1;
}
vector<double> d(n);
cout << "Nhap diem cua " << n << " hoc sinh:\n";
for (int i = 0; i < n; ++i) {
cout << "Diem hoc sinh " << i + 1 << ": ";
cin >> d[i];
}
double tong = 0;
for (int i = 0; i < d.size(); ++i) {
tong += d[i];
}
double tb = tong / d.size();
double maxD = d[0];
double minD = d[0];
for (int i = 1; i < d.size(); ++i) {
if (d[i] > maxD) {
maxD = d[i];
}
if (d[i] < minD) {
minD = d[i];
}
}
cout << fixed << setprecision(2);
cout << "\n--- Ket Qua Phan Tich Diem ---\n";
cout << "Diem trung binh lop: " << tb << endl;
cout << "Diem cao nhat: " << maxD << endl;
cout << "Diem thap nhat: " << minD << endl;
return 0;
}
Output (ví dụ):
Nhap so luong hoc sinh: 3
Nhap diem cua 3 hoc sinh:
Diem hoc sinh 1: 7.5
Diem hoc sinh 2: 8.0
Diem hoc sinh 3: 6.5
--- Ket Qua Phan Tich Diem ---
Diem trung binh lop: 7.33
Diem cao nhat: 8.00
Diem thap nhat: 6.50
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>
using namespace std;
int main() {
const int MAX = 10;
vector<int> tanSuat(MAX + 1, 0);
cout << "Nhap cac so nguyen (nhap so am de ket thuc):\n";
int giaTri;
while (cin >> giaTri && giaTri >= 0) {
if (giaTri <= MAX) {
tanSuat[giaTri]++;
} else {
cout << "Gia tri " << giaTri << " vuot qua pham vi (" << MAX << ") va bi bo qua.\n";
}
}
cout << "\n--- Thong Ke Tan Suat ---\n";
for (int i = 0; i <= MAX; ++i) {
if (tanSuat[i] > 0) {
cout << "Gia tri " << i << " xuat hien " << tanSuat[i] << " lan.\n";
}
}
return 0;
}
Output (ví dụ):
Nhap cac so nguyen (nhap so am de ket thuc):
1
5
2
1
5
0
3
11
-1
Gia tri 11 vuot qua pham vi (10) va bi bo qua.
--- Thong Ke Tan Suat ---
Gia tri 0 xuat hien 1 lan.
Gia tri 1 xuat hien 2 lan.
Gia tri 2 xuat hien 1 lan.
Gia tri 3 xuat hien 1 lan.
Gia tri 5 xuat hien 2 lan.
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>
using namespace std;
int main() {
vector<int> ds = {12, 34, 5, 8, 45, 20, 99, 1, 34};
int s;
cout << "Nhap so can tim trong danh sach: ";
cin >> s;
bool timThay = false;
int viTri = -1;
for (int i = 0; i < ds.size(); ++i) {
if (ds[i] == s) {
timThay = true;
viTri = i;
break;
}
}
if (timThay) {
cout << "Da tim thay so " << s << " tai chi muc: " << viTri << endl;
} else {
cout << "Khong tim thay so " << s << " trong danh sach.\n";
}
return 0;
}
Output (ví dụ):
Nhap so can tim trong danh sach: 8
Da tim thay so 8 tai chi muc: 3
Output (ví dụ khác):
Nhap so can tim trong danh sach: 100
Khong tim thay so 100 trong danh sach.
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>
using namespace std;
int main() {
vector<int> ngayThang = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
vector<string> tenNgay = {"", "Chu Nhat", "Thu Hai", "Thu Ba", "Thu Tu", "Thu Nam", "Thu Sau", "Thu Bay"};
int t;
cout << "Nhap so thang (1-12): ";
cin >> t;
if (t >= 1 && t <= 12) {
cout << "Thang " << t << " co " << ngayThang[t] << " ngay (khong tinh nam nhuan).\n";
} else {
cout << "So thang khong hop le.\n";
}
cout << "\n";
cout << "Cac ngay trong tuan:\n";
for (int i = 1; i <= 7; ++i) {
cout << tenNgay[i] << endl;
}
return 0;
}
Output (ví dụ):
Nhap so thang (1-12): 2
Thang 2 co 28 ngay (khong tinh nam nhuan).
Cac ngay trong tuan:
Chu Nhat
Thu Hai
Thu Ba
Thu Tu
Thu Nam
Thu Sau
Thu Bay
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