Người đăng: admin   Ngày: 21/04/2025   Lượt xem: 128

Khi bắt đầu học lập trình, bạn sẽ thường xuyên làm việc với các cấu trúc dữ liệu. Một trong những cấu trúc cơ bản và quan trọng nhất mà bạn cần nắm vững chính là Mảng, hay còn gọi là Array trong tiếng Anh. Bài viết này sẽ giúp bạn hiểu rõ Array là gì, cấu trúc của nó ra sao và cách sử dụng cơ bản nhất.

Array là gì?

Array (hay Mảng trong tiếng Việt) là một cấu trúc dữ liệu cơ bản trong lập trình, được dùng để lưu trữ một tập hợp các phần tử có cùng kiểu dữ liệu tại các vị trí bộ nhớ liền kề nhau. Đây là một cách hiệu quả để quản lý nhiều giá trị liên quan dưới một tên duy nhất.

Trong thế giới lập trình, việc xử lý các bộ dữ liệu lớn là rất phổ biến. Thay vì phải tạo ra hàng trăm, hàng ngàn biến riêng lẻ để lưu trữ từng giá trị, array cung cấp một giải pháp gọn gàng và có tổ chức hơn nhiều.

Mỗi mục dữ liệu đơn lẻ được chứa bên trong một array được gọi là một phần tử (element). Các phần tử này là những "viên gạch" xây dựng nên toàn bộ array, mang giá trị cụ thể mà bạn muốn lưu trữ.

Các phần tử trong array không đứng độc lập mà được sắp xếp theo một thứ tự tuyến tính, liên tiếp nhau. Thứ tự này rất quan trọng vì nó cho phép chúng ta dễ dàng xác định và truy cập vào từng phần tử cụ thể.

Để phân biệt và truy cập đến từng phần tử riêng lẻ trong array, người ta sử dụng một con số gọi là chỉ số (index). Chỉ số này giống như "số nhà" của mỗi phần tử bên trong "dãy phố" array.

Hầu hết các ngôn ngữ lập trình phổ biến hiện nay như C, C++, Java, Python, JavaScript đều sử dụng hệ thống chỉ số bắt đầu từ số 0. Điều này có nghĩa là phần tử đầu tiên của array sẽ có chỉ số là 0, phần tử thứ hai có chỉ số là 1, và cứ thế tiếp tục.

Hiểu rõ khái niệm về phần tử và chỉ số là bước đầu tiên cực kỳ quan trọng để làm việc hiệu quả với array trong bất kỳ ngôn ngữ lập trình nào mà bạn chọn học. Nắm vững cách các chỉ số hoạt động là chìa khóa để truy xuất đúng dữ liệu bạn cần.

Khi bạn muốn thao tác với một giá trị cụ thể trong mảng, bạn chỉ cần "gọi tên" mảng và cung cấp chỉ số của phần tử đó. Ví dụ: myArray[0] sẽ đưa bạn đến phần tử đầu tiên.

Chỉ số mảng luôn là số nguyên không âm. Nếu một mảng có $N$ phần tử và bắt đầu từ chỉ số 0, các chỉ số hợp lệ sẽ là từ $0$ đến $N-1$. Truy cập chỉ số ngoài phạm vi này sẽ gây ra lỗi.

Việc sử dụng chỉ số giúp lập trình viên có thể làm việc với từng mục dữ liệu trong mảng một cách độc lập hoặc thực hiện các vòng lặp để xử lý toàn bộ các phần tử một cách tuần tự.

Cấu trúc và cách hoạt động của Array

Array có một cấu trúc rất đặc trưng, quyết định cách dữ liệu được lưu trữ và truy cập. Việc hiểu rõ cấu trúc này sẽ giúp bạn sử dụng array một cách tối ưu và tránh được các lỗi thường gặp. Cấu trúc này liên quan mật thiết đến cách bộ nhớ được quản lý.

Phần tử (Element) là gì?

Như đã đề cập, phần tử (element) là đơn vị dữ liệu cơ bản được lưu trữ trong array. Mỗi phần tử là một giá trị cụ thể, có thể là một số, một ký tự, một chuỗi văn bản, hoặc thậm chí là một đối tượng (tùy thuộc vào kiểu dữ liệu của array).

Chẳng hạn, nếu bạn có một array lưu trữ điểm số của sinh viên, mỗi điểm số (ví dụ: 8.5, 9.0, 7.2) sẽ là một phần tử riêng biệt nằm trong array đó. Tất cả các phần tử này đều mang thông tin liên quan đến điểm số.

Các phần tử này chiếm giữ không gian trong bộ nhớ máy tính. Kích thước của không gian này phụ thuộc vào kiểu dữ liệu của phần tử và ngôn ngữ lập trình bạn đang sử dụng. Ví dụ, một số nguyên (int) thường chiếm 4 byte, trong khi một ký tự (char) có thể chiếm 1 byte.

Kích thước mỗi phần tử là như nhau trong cùng một array nhờ tính đồng nhất kiểu dữ liệu. Điều này là yếu tố quan trọng cho phép tính toán địa chỉ bộ nhớ nhanh chóng.

Việc truy cập, đọc, ghi hoặc chỉnh sửa dữ liệu trong array thực chất là thao tác với các phần tử này tại những vị trí xác định. Bạn đang làm việc trực tiếp với các giá trị được lưu trữ.

Chỉ số (Index) và cách truy cập phần tử

Chỉ số (index) là "địa chỉ" hoặc "số thứ tự" của một phần tử trong array. Nó là một số nguyên không âm dùng để xác định vị trí duy nhất của mỗi phần tử trong dãy.

Ví dụ, trong một array có 5 phần tử, các chỉ số hợp lệ sẽ là 0, 1, 2, 3, và 4 (nếu chỉ số bắt đầu từ 0). Đây là phạm vi chỉ số an toàn để làm việc.

Để truy cập một phần tử cụ thể, bạn sẽ sử dụng tên của array theo sau là chỉ số của phần tử đó đặt trong cặp ngoặc vuông []. Cú pháp này là chuẩn chung trong hầu hết các ngôn ngữ lập trình khi thao tác với mảng.

Ví dụ: ten_array[chi_so] sẽ trả về giá trị của phần tử tại vị trí được chỉ định bởi chi_so. Nếu bạn muốn thay đổi giá trị đó, bạn có thể gán một giá trị mới vào vị trí này, ví dụ: ten_array[2] = gia_tri_moi;.

Việc truy cập phần tử bằng chỉ số là cực kỳ hiệu quả. Máy tính có thể tính toán ngay lập tức địa chỉ bộ nhớ của bất kỳ phần tử nào chỉ dựa vào địa chỉ bắt đầu của array và chỉ số của phần tử, bất kể array lớn đến đâu. Công thức thường là: Địa chỉ phần tử = Địa chỉ bắt đầu + Chỉ số * Kích thước mỗi phần tử.

Đây là lý do chính khiến array được coi là cấu trúc dữ liệu có hiệu suất cao cho các thao tác truy cập ngẫu nhiên (random access), tức là có thể nhảy thẳng tới bất kỳ phần tử nào mà không cần duyệt qua các phần tử trước đó. Hiệu suất này được gọi là O(1) - thời gian hằng số.

Trong Python, bạn còn có thể sử dụng chỉ số âm để truy cập phần tử từ cuối mảng lên. Chỉ số -1 là phần tử cuối cùng, -2 là phần tử kế cuối, v.v. Tính năng này tiện lợi khi bạn không biết chính xác kích thước mảng.

Array lưu trữ dữ liệu trong bộ nhớ như thế nào?

Một đặc điểm cấu trúc quan trọng của array là các phần tử của nó thường được lưu trữ tại các vị trí bộ nhớ liền kề (contiguous memory locations) trong RAM (Bộ nhớ truy cập ngẫu nhiên).

Điều này có nghĩa là nếu phần tử đầu tiên được lưu tại một địa chỉ bộ nhớ nhất định, thì phần tử thứ hai sẽ được lưu ngay sau đó trong khối bộ nhớ liền kề, rồi đến phần tử thứ ba, và cứ thế tiếp tục một cách liên tục, không có khoảng trống hay dữ liệu khác xen vào giữa các phần tử của cùng một mảng.

Việc lưu trữ liền kề này mang lại nhiều lợi ích về hiệu suất. Khi array được nạp vào bộ nhớ hoặc được CPU xử lý, bộ nhớ đệm (cache) của CPU có thể nạp các phần tử tiếp theo một cách hiệu quả nhờ vị trí gần nhau, giúp tăng tốc độ truy cập.

Nó cũng đơn giản hóa việc tính toán địa chỉ của một phần tử bất kỳ như đã nói ở trên. Hệ thống chỉ cần địa chỉ của phần tử đầu tiên và kích thước cố định của mỗi phần tử để nhanh chóng định vị bất kỳ phần tử nào theo chỉ số.

Tuy nhiên, việc lưu trữ liền kề cũng có nhược điểm. Nếu bạn muốn thêm một phần tử mới vào giữa một array đã đầy, hoặc xóa một phần tử, hệ thống có thể cần phải "dịch chuyển" rất nhiều phần tử phía sau để duy trì tính liền kề. Thao tác này có thể tốn kém về mặt thời gian và tài nguyên, đặc biệt với các array lớn.

Trong các ngôn ngữ lập trình cấp cao hơn, chi tiết về quản lý bộ nhớ này có thể được trừu tượng hóa đi (ví dụ: List trong Python hoặc ArrayList trong Java tự động quản lý việc cấp phát lại bộ nhớ khi thay đổi kích thước), nhưng nguyên lý cơ bản về sự liền kề (hoặc mô phỏng sự liền kề để truy cập hiệu quả) vẫn là cốt lõi đằng sau array tĩnh.

Các đặc điểm chính của Array

Array có những đặc điểm cố định giúp phân biệt nó với các cấu trúc dữ liệu khác như danh sách liên kết, cây, hay bảng băm. Nắm rõ các đặc điểm này sẽ giúp bạn lựa chọn array phù hợp với bài toán đang giải quyết.

Tính đồng nhất về kiểu dữ liệu

Một trong những quy tắc nghiêm ngặt nhất của array truyền thống là tất cả các phần tử bên trong nó phải có cùng một kiểu dữ liệu (data type).

Nếu bạn khai báo một array để lưu trữ các số nguyên (integer), bạn chỉ có thể lưu trữ các số nguyên vào đó. Bạn không thể xen lẫn các số thập phân (float/double), chuỗi ký tự (string) hay bất kỳ kiểu dữ liệu nào khác vào array đó.

Ví dụ, một array lưu điểm thi (integer hoặc float) không thể chứa tên của học sinh (string) hay trạng thái đỗ/trượt (boolean).

Điều này khác với một số cấu trúc dữ liệu khác như List trong Python hoặc ArrayList trong Java (khi không sử dụng Generic chặt chẽ), nơi bạn có thể lưu trữ các phần tử có kiểu dữ liệu khác nhau trong cùng một tập hợp.

Tính đồng nhất về kiểu dữ liệu giúp việc quản lý bộ nhớ trở nên đơn giản và hiệu quả hơn vì hệ thống biết chính xác kích thước của mỗi "ô" trong mảng. Nó cũng đảm bảo tính nhất quán của dữ liệu được lưu trữ trong mảng.

Kích thước cố định (trong nhiều ngôn ngữ lập trình)

Trong nhiều ngôn ngữ lập trình cấp thấp và truyền thống như C, C++ và Java (đối với array nguyên thủy), kích thước (size) của một array phải được xác định ngay tại thời điểm bạn khai báo nó.

Kích thước này chính là số lượng phần tử tối đa mà array đó có thể chứa. Sau khi được tạo, bạn không thể thay đổi kích thước này một cách trực tiếp.

Ví dụ, nếu bạn tạo một array kích thước 10, nó chỉ có thể chứa tối đa 10 phần tử. Bạn không thể "thêm" phần tử thứ 11 vào array đó.

Nếu bạn cần lưu trữ thêm dữ liệu vượt quá kích thước ban đầu, bạn sẽ cần phải tạo một array mới có kích thước lớn hơn (hoặc gấp đôi, v.v.) và sao chép toàn bộ dữ liệu từ array cũ sang array mới. Thao tác sao chép này có thể tốn kém về mặt thời gian và tài nguyên tính toán.

Đây là một hạn chế đáng kể của array cố định kích thước khi làm việc với các tập dữ liệu có số lượng phần tử không xác định trước hoặc thường xuyên thay đổi.

Tuy nhiên, đặc điểm kích thước cố định cũng là lý do array có thể được lưu trữ liền kề trong bộ nhớ và cho phép truy cập ngẫu nhiên rất nhanh (O(1)).

Các ngôn ngữ lập trình hiện đại hơn hoặc các thư viện cung cấp các phiên bản "động" của array (như ArrayList trong Java hoặc List trong Python), cho phép kích thước thay đổi linh hoạt hơn, nhưng thường có chi phí hiệu năng cao hơn một chút so với array tĩnh do phải xử lý việc cấp phát lại bộ nhớ khi cần mở rộng.

Array trong các ngôn ngữ lập trình phổ biến

Cú pháp để làm việc với array có thể hơi khác nhau giữa các ngôn ngữ lập trình, nhưng khái niệm cốt lõi về việc lưu trữ tập hợp các phần tử cùng loại và truy cập bằng chỉ số vẫn giữ nguyên. Chúng ta sẽ xem xét cách sử dụng array trong một số ngôn ngữ phổ biến nhất.

Array trong C/C++

Trong C và C++, array là một trong những cấu trúc dữ liệu cơ bản nhất và hoạt động rất gần với cách quản lý bộ nhớ vật lý. Chúng là array tĩnh (fixed-size) theo mặc định.

Để khai báo một array trong C/C++, bạn cần chỉ rõ kiểu dữ liệu của các phần tử và kích thước của array. Ví dụ: int diem_thi[5]; khai báo một array tên diem_thi có thể chứa 5 số nguyên. Các phần tử sẽ có chỉ số từ 0 đến 4.

Kích thước này (ví dụ: 5) phải là một hằng số hoặc biểu thức có thể tính toán được tại thời điểm biên dịch. Bạn không thể dùng một biến để xác định kích thước lúc khai báo một array tĩnh thông thường (trong C++11 trở về trước). C++ hiện đại có hỗ trợ VLA (Variable-Length Array) nhưng cần cẩn trọng khi sử dụng.

Truy cập phần tử được thực hiện bằng chỉ số: diem_thi[0] là phần tử đầu tiên, diem_thi[4] là phần tử cuối cùng. Chỉ số hợp lệ chạy từ 0 đến kích thước - 1.

Ví dụ về khai báo, khởi tạo và truy cập trong C++:

```cpp

#include // Thu vien nhap xuat co ban

int main() { // Khai bao va khoi tao array so nguyen kich thuoc 3 int so_nguyen[3] = {10, 20, 30};

// Gan gia tri moi cho phan tu thu hai (chi so 1)
so_nguyen[1] = 25;

// Truy cap va in gia tri cua cac phan tu
std::cout << "Phan tu thu nhat (chi so 0): " << so_nguyen[0] << std::endl; // In ra 10
std::cout << "Phan tu thu hai (chi so 1): " << so_nguyen[1] << std::endl; // In ra 25
std::cout << "Phan tu thu ba (chi so 2): " << so_nguyen[2] << std::endl; // In ra 30

// Lap qua tat ca cac phan tu su dung vong lap
std::cout << "Cac phan tu cua array: ";
for (int i = 0; i < 3; ++i) {
    std::cout << so_nguyen[i] << (i < 2 ? ", " : ""); // In kem dau phay
}
std::cout << std::endl;

return 0;
}

Code trên minh họa cách khai báo một array so_nguyen kích thước 3 với giá trị ban đầu, gán lại giá trị cho một phần tử cụ thể và sau đó truy cập chúng thông qua chỉ số. Vòng lặp for là cách phổ biến để duyệt (iterate) qua tất cả phần tử của mảng.

Khi làm việc với array trong C/C++, lập trình viên cần tự quản lý việc truy cập để không vượt quá chỉ số hợp lệ (index out of bounds). Nếu cố gắng truy cập chỉ số âm hoặc lớn hơn kích thước - 1, chương trình có thể gặp lỗi nghiêm trọng như crash hoặc gây ra lỗ hổng bảo mật do truy cập vào vùng nhớ không được cấp phát cho mảng đó.

Array trong Java

Java cũng hỗ trợ array với cú pháp tương tự C/C++, nhưng array trong Java là đối tượng (objects). Array trong Java cũng có kích thước cố định sau khi được tạo, nhưng được quản lý an toàn hơn về mặt bộ nhớ.

Để khai báo một biến tham chiếu đến array trong Java: int[] diemThi; hoặc int diemThi[];. Đây mới chỉ là khai báo biến, chưa cấp phát bộ nhớ cho các phần tử.

Để cấp phát bộ nhớ và tạo array: diemThi = new int[5];. Lệnh new int[5] sẽ tạo một array mới có 5 phần tử kiểu int trên bộ nhớ heap và gán địa chỉ tham chiếu cho biến diemThi.

Bạn cũng có thể khai báo, cấp phát và khởi tạo giá trị ban đầu ngay lập tức: String[] tenHocSinh = {"Alice", "Bob", "Charlie"};. Kích thước array sẽ được tự động xác định là số phần tử bạn cung cấp ban đầu (ở đây là 3).

Truy cập phần tử cũng dùng chỉ số trong ngoặc vuông [], bắt đầu từ 0: diemThi[0], tenHocSinh[1]. Chỉ số hợp lệ từ 0 đến tenHocSinh.length - 1. Java cung cấp thuộc tính .length để lấy kích thước của array, rất tiện lợi.

Ví dụ về array trong Java:

Java

public class ArrayJavaExample {
    public static void main(String[] args) {
        // Khai bao, cap phat va khoi tao array diem so
        double[] diemMonHoc = {7.5, 8.0, 6.5, 9.0}; // Kich thuoc tu dong la 4

        // In kich thuoc array
        System.out.println("Kich thuoc array diemMonHoc: " + diemMonHoc.length); // In ra 4

        // Truy cap va in phan tu dau tien
        System.out.println("Diem mon hoc dau tien (chi so 0): " + diemMonHoc[0]); // In ra 7.5

        // Gan gia tri moi cho phan tu cuoi cung
        diemMonHoc[diemMonHoc.length - 1] = 9.5;

        // Lap qua tat ca cac phan tu
        System.out.println("Cac diem mon hoc:");
        for (int i = 0; i < diemMonHoc.length; i++) {
            System.out.print(diemMonHoc[i] + (i < diemMonHoc.length - 1 ? ", " : ""));
        }
        System.out.println();

        // Thu truy cap chi so khong hop le (se gay loi luc chay)
        // System.out.println(diemMonHoc[4]); // Day se gay ra ArrayIndexOutOfBoundsException
    }
}

Java kiểm tra chỉ số truy cập có hợp lệ hay không trong lúc chạy chương trình. Nếu bạn cố gắng truy cập một chỉ số nằm ngoài phạm vi [0, length - 1], Java sẽ ném ra một ngoại lệ (ArrayIndexOutOfBoundsException), giúp bạn phát hiện và xử lý lỗi dễ dàng hơn so với C/C++.

Ngoài array cố định, Java còn cung cấp lớp ArrayList trong gói java.util, đây là một cấu trúc dữ liệu linh hoạt hơn, có thể thay đổi kích thước động và lưu trữ các đối tượng. ArrayList được xây dựng dựa trên array nhưng tự động quản lý việc mở rộng khi cần.

Array trong Python

Python không có kiểu dữ liệu "array" nguyên thủy giống như array tĩnh, cố định kích thước và cùng kiểu dữ liệu của C/Java mà thường sử dụng List làm cấu trúc dữ liệu đa năng thay thế. List trong Python linh hoạt hơn array vì có thể chứa các phần tử với kiểu dữ liệu khác nhau và có kích thước động.

Tuy nhiên, Python có module chuẩn array cung cấp các kiểu array có hiệu suất cao và hiệu quả về bộ nhớ, lưu trữ các phần tử cùng kiểu dữ liệu, tương tự như array truyền thống. Module này hữu ích khi làm việc với lượng lớn dữ liệu số (như xử lý số, I/O file nhị phân) và cần tối ưu hiệu quả về bộ nhớ và tốc độ.

Để sử dụng array từ module array, bạn cần nhập module này: from array import array. Sau đó, khai báo array bằng cách chỉ định "type code" (mã kiểu dữ liệu) và danh sách các giá trị ban đầu.

Type code là một ký tự quy định kiểu dữ liệu của các phần tử (ví dụ: 'i' cho số nguyên có dấu, 'f' cho số thực dấu phẩy động).

Ví dụ sử dụng module array trong Python:

Python

from array import array # Import module array

# Khai bao array cac so thuc ('f' la type code cho float)
diem_mon_hoc = array('f', [7.5, 8.0, 6.5, 9.0])

print("Array diem mon hoc:", diem_mon_hoc) # In ra array

# Truy cap phan tu bang chi so (bat dau tu 0)
print("Diem mon hoc dau tien (chi so 0):", diem_mon_hoc[0]) # In ra 7.5

# Su dung chi so am (phan tu cuoi cung)
print("Diem mon hoc cuoi cung:", diem_mon_hoc[-1]) # In ra 9.0

# Kich thuoc cua array (so luong phan tu)
print("So luong mon hoc:", len(diem_mon_hoc)) # Su dung ham len()

# Them phan tu vao cuoi array (array tu dong tang kich thuoc bo nho neu can)
diem_mon_hoc.append(8.5)
print("Array sau khi them 8.5:", diem_mon_hoc) # In ra array moi

# Thay doi gia tri phan tu
diem_mon_hoc[2] = 7.0
print("Array sau khi sua diem:", diem_mon_hoc)

Trong ví dụ này, array('f', [7.5, 8.0, 6.5, 9.0]) tạo một array chứa các số thực (type code 'f') với các giá trị ban đầu. Truy cập phần tử vẫn dùng chỉ số []. Python hỗ trợ cả chỉ số dương (từ 0) và chỉ số âm (từ -1).

Mặc dù module array tồn tại và hữu ích cho các ứng dụng cần tối ưu, trong hầu hết các trường hợp thông thường khi học và lập trình Python, bạn sẽ làm việc với List nhiều hơn là array từ module array vì tính linh hoạt và dễ sử dụng của List.

Array trong JavaScript

Trong JavaScript, khái niệm array được triển khai dưới dạng một kiểu đối tượng đặc biệt (Array), có tính linh hoạt cao. Array trong JavaScript không có kích thước cố định (có thể thay đổi dynamically) và có thể chứa các phần tử với kiểu dữ liệu khác nhau trong cùng một mảng.

Điều này làm cho array trong JavaScript giống với List trong Python hơn là array truyền thống, tĩnh, cùng kiểu dữ liệu của C/Java.

Để khai báo một array trong JavaScript: let danhSachSo = [10, 20, 30]; hoặc let danhSachTen = ["Alice", "Bob"];. Đây là cách khai báo ngắn gọn (literal notation). Bạn cũng có thể tạo array rỗng: let arrayRong = [];.

Hoặc sử dụng constructor: let anotherArray = new Array(5); (tạo array rỗng với 5 vị trí trống - undefined).

Truy cập phần tử cũng dùng chỉ số trong ngoặc vuông [], bắt đầu từ 0: danhSachSo[0], danhSachTen[1]. Thuộc tính .length trả về số lượng phần tử hiện có.

Ví dụ về array trong JavaScript:

JavaScript

// Khai bao array chua cac kieu du lieu khac nhau
let danhSachTongHop = [10, "Hello", true, {name: "Test"}];

console.log("Array ban dau:", danhSachTongHop);
console.log("Kich thuoc array ban dau:", danhSachTongHop.length); // 4

// Truy cap phan tu thu hai (chi so 1)
console.log("Phan tu thu hai:", danhSachTongHop[1]); // "Hello"

// Gan gia tri moi cho phan tu cuoi cung (chi so 3)
danhSachTongHop[3] = "New Value";
console.log("Array sau khi sua phan tu cuoi:", danhSachTongHop);

// Them phan tu vao cuoi array su dung push()
danhSachTongHop.push("World");
console.log("Array sau khi them 'World':", danhSachTongHop);
console.log("Kich thuoc array sau khi them:", danhSachTongHop.length); // 5

// Xoa phan tu cuoi cung su dung pop()
let phanTuCuoi = danhSachTongHop.pop();
console.log("Phan tu vua xoa:", phanTuCuoi); // "World"
console.log("Array sau khi xoa cuoi:", danhSachTongHop);
console.log("Kich thuoc array sau khi xoa:", danhSachTongHop.length); // 4

JavaScript array rất linh hoạt trong việc thay đổi kích thước (thêm/xóa phần tử dễ dàng bằng các phương thức như push, pop, shift, unshift, splice) và có thể chứa các kiểu dữ liệu hỗn hợp. Tuy nhiên, sự linh hoạt này đôi khi kém hiệu quả hơn về bộ nhớ và tốc độ xử lý so với array tĩnh trong các ngôn ngữ khác, đặc biệt khi xử lý lượng lớn dữ liệu số cùng loại trong các tác vụ hiệu năng cao.

Các loại Array thường gặp

Mặc dù array cơ bản là một dãy tuyến tính các phần tử, chúng ta có thể mở rộng khái niệm này để làm việc với dữ liệu có nhiều chiều hơn, biểu diễn các cấu trúc phức tạp hơn như bảng, ma trận hoặc không gian nhiều chiều. Các loại array phổ biến nhất là mảng một chiều và mảng hai chiều.

Mảng một chiều (1D Array) là gì?

Mảng một chiều (One-Dimensional Array - 1D Array) là loại array cơ bản nhất mà chúng ta đã thảo luận từ đầu bài viết. Nó là một dãy tuyến tính (theo một hàng hoặc một cột) của các phần tử được sắp xếp theo một thứ tự duy nhất.

Mỗi phần tử trong mảng một chiều chỉ cần một chỉ số duy nhất để xác định vị trí của nó. Chỉ số này thường là một số nguyên không âm, bắt đầu từ 0 hoặc 1 tùy theo quy ước của ngôn ngữ.

Hình dung mảng một chiều giống như một hàng dài các ngăn tủ được đánh số thứ tự, mỗi ngăn chỉ chứa một món đồ. Bạn chỉ cần biết số thứ tự của ngăn để thao tác với món đồ bên trong.

Ví dụ: [10, 20, 30, 40, 50] là một mảng một chiều chứa 5 số nguyên. Phần tử có giá trị 10 nằm ở chỉ số 0, phần tử có giá trị 20 nằm ở chỉ số 1, và cứ thế tiếp tục cho đến phần tử 50 ở chỉ số 4.

Hầu hết các ví dụ cơ bản về array bạn gặp trong các bài giới thiệu hoặc khi mới bắt đầu học đều là mảng một chiều vì tính đơn giản và dễ hiểu của nó.

Cách làm việc với mảng một chiều bao gồm: khai báo, khởi tạo giá trị, truy cập phần tử bằng chỉ số, và duyệt qua tất cả các phần tử (ví dụ, dùng vòng lặp for).

Mảng hai chiều (2D Array) là gì?

Mảng hai chiều (Two-Dimensional Array - 2D Array) là một array mà các phần tử của nó được tổ chức dưới dạng lưới hoặc bảng (giống ma trận trong toán học). Mảng hai chiều mở rộng khái niệm từ một chiều sang hai chiều.

Mảng hai chiều có thể được xem như "một mảng của các mảng một chiều". Mỗi "hàng" trong mảng hai chiều thực chất là một mảng một chiều riêng biệt.

Để xác định vị trí của một phần tử trong mảng hai chiều, bạn cần sử dụng hai chỉ số: một chỉ số cho hàng (row) và một chỉ số cho cột (column). Thứ tự này thường là [chỉ_số_hàng][chỉ_số_cột].

Hình dung mảng hai chiều giống như một bảng tính Excel hoặc một bàn cờ vua. Mỗi ô trong bảng/bàn cờ là một phần tử. Bạn xác định ô đó bằng cách biết nó nằm ở hàng nào và cột nào. Hàng và cột đều được đánh số.

Ví dụ về khai báo mảng 2 chiều 3 hàng, 4 cột kiểu int trong C++: int ma_tran[3][4];. Array này có tổng cộng $3 \times 4 = 12$ phần tử.

Truy cập phần tử ở hàng thứ i (với chỉ số i) và cột thứ j (với chỉ số j) sẽ là ma_tran[i][j]. Chỉ số hàng i chạy từ 0 đến 2, chỉ số cột j chạy từ 0 đến 3.

Ví dụ đơn giản về mảng 2 chiều chứa điểm danh (1: có mặt, 0: vắng mặt) của 3 học sinh trong 2 môn học:

C++

int diem_danh[3][2] = {
    {1, 1}, // Hang 0: Du lieu diem danh HS 1 (Mon 1, Mon 2)
    {1, 0}, // Hang 1: Du lieu diem danh HS 2 (Mon 1, Mon 2)
    {0, 1}  // Hang 2: Du lieu diem danh HS 3 (Mon 1, Mon 2)
};
// de lay diem danh mon 2 cua HS 2 (Hang 1, Cot 1), ta dung diem_danh[1][1]. Gia tri la 0.

Mảng hai chiều thường được sử dụng để biểu diễn các cấu trúc dữ liệu có tính chất bảng như ma trận trong các bài toán tính toán, hình ảnh (mỗi pixel là một phần tử với tọa độ (x, y)), hoặc dữ liệu địa lý (ví dụ: bản đồ nhiệt độ).

Khái niệm array có thể mở rộng ra mảng ba chiều (3D Array), bốn chiều (4D Array) hoặc thậm chí nhiều chiều hơn nữa (N-Dimensional Array), tùy thuộc vào nhu cầu biểu diễn dữ liệu phức tạp của bạn. Mảng N chiều sẽ cần N chỉ số để truy cập một phần tử.

Tại sao sử dụng Array trong lập trình? (Lợi ích của Array)

Sau khi hiểu Array là gì, cấu trúc và các loại cơ bản của nó, câu hỏi đặt ra là: tại sao các lập trình viên lại cần và sử dụng Array phổ biến đến vậy? Array mang lại nhiều lợi ích đáng kể trong việc quản lý dữ liệu, đặc biệt trong các tình huống cụ thể.

Lưu trữ và quản lý dữ liệu cùng loại hiệu quả

Lợi ích lớn nhất và rõ ràng nhất của array là khả năng lưu trữ và quản lý một số lượng lớn các dữ liệu có cùng kiểu một cách có tổ chức dưới một tên biến duy nhất.

Hãy tưởng tượng bạn cần lưu trữ tên của 1000 sinh viên. Nếu không có array, bạn sẽ phải khai báo và quản lý 1000 biến riêng lẻ, mỗi biến lưu một tên: string tenSinhVien1; string tenSinhVien2; ... string tenSinhVien1000;. Cách này cực kỳ cồng kềnh, dễ mắc lỗi khi gõ tên biến và rất khó để xử lý hàng loạt (ví dụ: in tất cả tên ra màn hình).

Sử dụng array, bạn chỉ cần một biến duy nhất, ví dụ string tenSinhVien[1000];, và bạn có thể truy cập từng tên bằng chỉ số: tenSinhVien[0], tenSinhVien[1], ..., tenSinhVien[999].

Điều này giúp mã nguồn của bạn trở nên gọn gàng, dễ đọc, dễ viết và dễ bảo trì hơn rất nhiều, đặc biệt khi làm việc với các tập dữ liệu lớn hoặc khi số lượng mục dữ liệu có thể thay đổi (dù với array tĩnh là cố định sau khi khai báo, bạn vẫn quản lý chúng hiệu quả hơn).

Các thao tác như duyệt qua tất cả các phần tử để in, tính tổng, tìm kiếm giá trị lớn nhất/nhỏ nhất trở nên rất đơn giản và hiệu quả khi dữ liệu được tổ chức trong array.

Truy cập phần tử nhanh chóng

Như đã giải thích trong phần cấu trúc, việc array thường lưu trữ dữ liệu tại các vị trí bộ nhớ liền kề cho phép truy cập ngẫu nhiên (random access) vào bất kỳ phần tử nào với tốc độ rất nhanh, gần như tức thời (thời gian hằng số - O(1)).

Chỉ cần biết chỉ số của phần tử, hệ thống có thể tính toán trực tiếp địa chỉ bộ nhớ của nó mà không cần phải duyệt qua các phần tử đứng trước đó. Đây là kết quả trực tiếp của cách array được lưu trữ trong bộ nhớ.

Ví dụ, nếu bạn có một array 1 triệu phần tử và muốn truy cập phần tử ở chỉ số 999999, thời gian truy cập sẽ tương đương với thời gian truy cập phần tử ở chỉ số 0.

Điều này cực kỳ quan trọng trong các ứng dụng yêu cầu truy xuất dữ liệu nhanh chóng dựa trên vị trí, ví dụ như xử lý hình ảnh (truy cập pixel tại tọa độ X, Y), xử lý âm thanh, hoặc các thuật toán tìm kiếm nhị phân trên tập dữ liệu đã sắp xếp.

Các cấu trúc dữ liệu khác như danh sách liên kết (Linked List) không có đặc điểm này; để truy cập phần tử thứ i, bạn phải đi lần lượt từ đầu danh sách đến vị trí đó, thao tác này có độ phức tạp O(N) (tuyến tính theo kích thước mảng).

Tiết kiệm tài nguyên (so với biến độc lập)

Việc sử dụng array có thể giúp tối ưu hóa việc sử dụng bộ nhớ so với việc khai báo một lượng lớn biến riêng lẻ, đặc biệt trong các ngôn ngữ cấp thấp và khi làm việc với các kiểu dữ liệu có kích thước nhỏ.

Khi khai báo một array, hệ thống có thể yêu cầu cấp phát một khối bộ nhớ liên tục có kích thước đủ lớn cho tất cả các phần tử cùng lúc. Việc cấp phát một khối lớn thường hiệu quả hơn so với việc yêu cầu cấp phát bộ nhớ rời rạc, nhỏ lẻ cho từng biến độc lập.

Đối với các ngôn ngữ như C/C++, array còn tránh được chi phí "overhead" (chi phí phụ trợ) như lưu trữ con trỏ (pointers) hoặc cấu trúc dữ liệu bổ sung mà các cấu trúc dữ liệu động khác cần có để quản lý các phần tử không liền kề, góp phần vào hiệu quả tài nguyên.

Tuy nhiên, cần lưu ý rằng trong một số ngôn ngữ cấp cao hơn (như Python), sự khác biệt về hiệu quả bộ nhớ giữa array từ module array và List có thể không quá rõ rệt đối với các ứng dụng thông thường do cơ chế quản lý bộ nhớ tự động phức tạp hơn.

Để biến kiến thức lập trình thành sản phẩm thực tế như website hay ứng dụng, bạn cần có nơi để chạy mã nguồn đó. Tại InterData, bạn có thể tìm thấy dịch vụ Hosting giá rẻ chất lượng uy tín được xây dựng trên nền tảng phần cứng chuyên dụng thế hệ mới.

Nếu cần môi trường mạnh mẽ và tùy chỉnh hơn, dịch vụ VPS giá rẻ uy tín tốc độ cao hoặc dịch vụ Cloud Server chất lượng giá rẻ cấu hình cao là lựa chọn đáng cân nhắc. Chúng sử dụng bộ xử lý AMD EPYC Gen 3th, SSD NVMe U.2 và công nghệ ảo hóa tiên tiến, mang lại hiệu năng ổn định, băng thông cao và dung lượng được tối ưu cho dự án của bạn.

Phân biệt Array và List (Điểm khác biệt cơ bản)

Khi học về array, đặc biệt là trong các ngôn ngữ động như Python hay các thư viện trong Java, bạn sẽ thường nghe nhắc đến List. Mặc dù cả hai đều là cấu trúc dùng để lưu trữ một tập hợp các mục dữ liệu, Array và List (cụ thể là các implementation linh hoạt như Python List hay Java ArrayList) có những khác biệt cơ bản về đặc điểm và cách sử dụng.

Điểm khác biệt rõ nhất và quan trọng nhất là về kích thước. Array truyền thống (như trong C/C++, hoặc array nguyên thủy trong Java) có kích thước cố định (static size) sau khi được tạo. Bạn không thể dễ dàng thêm hoặc bớt phần tử làm thay đổi kích thước ban đầu của nó.

Ngược lại, List (Python List, Java ArrayList) có kích thước động (dynamic size). Bạn có thể dễ dàng thêm (append, insert) hoặc xóa (remove, pop) các phần tử vào List bất cứ lúc nào, và List sẽ tự động điều chỉnh kích thước bộ nhớ cần thiết.

Sự khác biệt thứ hai nằm ở tính đồng nhất về kiểu dữ liệu. Array truyền thống yêu cầu tất cả phần tử phải cùng kiểu dữ liệu (homogeneous). Nếu bạn khai báo array số nguyên, nó chỉ chứa số nguyên.

Trong khi đó, List trong Python có thể chứa các phần tử với nhiều kiểu dữ liệu khác nhau (heterogeneous) trong cùng một List. Java ArrayList (khi sử dụng không có ràng buộc kiểu dữ liệu hoặc với kiểu Object) cũng có thể chứa các đối tượng thuộc các lớp khác nhau, hoặc khi dùng Generic, nó ràng buộc cùng một kiểu đối tượng cụ thể.

Về hiệu suất, array tĩnh thường cho phép truy cập phần tử theo chỉ số nhanh hơn (O(1)) nhờ cơ chế lưu trữ liền kề và công thức tính địa chỉ đơn giản. Thao tác thêm/xóa phần tử ở giữa array tĩnh thường tốn kém (phải di chuyển nhiều phần tử).

List động cho phép thêm/xóa phần tử linh hoạt hơn, nhưng việc truy cập ngẫu nhiên theo chỉ số có thể kém hiệu quả hơn một chút tùy vào cách triển khai bên dưới (ví dụ: List dựa trên Linked List), và thao tác thêm/xóa ở giữa vẫn có thể tốn kém do cần điều chỉnh cấu trúc bên trong.

Cách lưu trữ trong bộ nhớ cũng khác nhau. Array tĩnh lưu các phần tử liền kề trong một khối bộ nhớ duy nhất. List động thường được triển khai dựa trên array (như ArrayList, cần cấp phát lại khi đầy) hoặc cấu trúc liên kết các node (như Linked List, các phần tử không liền kề vật lý) với cơ chế quản lý bộ nhớ phức tạp hơn để hỗ trợ thay đổi kích thước và cấu trúc.

Nguồn tham khảo: Array là gì? Định nghĩa, Ví dụ & Đặc điểm cơ bản của Mảng - Interdata.vn

(0 ratings)