Lịch sử Cấu trúc Dữ liệu: Từ Rơ-le điện đến CPU Cache
Bạn đã bao giờ tự hỏi: "Tại sao chúng ta phải học Linked List trong khi Array xài vừa dễ vừa nhanh?" hay "B-Tree sinh ra để làm gì khi đã có Binary Search Tree?"
Các sinh viên CNTT thường được dạy Cấu trúc dữ liệu (Data Structures) như những khái niệm toán học khô khan. Nhưng sự thật là: Không có cấu trúc dữ liệu nào tự nhiên sinh ra. Chúng được sinh ra trong máu, mồ hôi và nước mắt của các kỹ sư thời sơ khai để "thích nghi" với sự thiếu thốn và đắt đỏ kinh khủng của bộ nhớ phần cứng.
Bài viết này sẽ đưa bạn cỗ máy thời gian, quay về những năm 1950, để thấy rằng Khoa học máy tính thực chất là cuộc chạy đua giữa Sự thông minh của Thuật toán và Sự giới hạn của Phần cứng.
🕰️ Timeline: Cuộc chạy đua Phần cứng & Thuật toán
Trước khi đi vào chi tiết, hãy cùng nhìn lướt qua dòng thời gian tiến hóa vĩ đại này:
Kỷ nguyên 1: Rơ-le điện và Sự gò bó của Dữ liệu Tuần tự (1940s)
Hãy tưởng tượng bạn đang lập trình cho ENIAC hay Harvard Mark I. Không có màn hình, không có bàn phím. Bộ nhớ của máy tính là những chiếc Thẻ đục lỗ (Punch cards) hoặc Băng từ (Magnetic Tape).
Giới hạn phần cứng:
Băng từ hoạt động giống hệt cuộn băng cassette của ông bà chúng ta. Để nghe bài hát thứ 5, bạn bắt buộc phải tua qua 4 bài đầu tiên. Đây gọi là Truy cập Tuần tự (Sequential Access). Bạn không thể "nhảy" một phát đến vị trí mình muốn.
Cấu trúc dữ liệu tương ứng: Mảng (Array)
Vì tính chất vật lý của Băng từ là một dải dài liên tục, cấu trúc dữ liệu duy nhất có ý nghĩa lúc này là Array (Mảng) hoặc List tuần tự. Dữ liệu bắt buộc phải được xếp sát cạnh nhau. Nếu bạn muốn chèn một dữ liệu mới vào giữa băng từ? Bạn phải copy toàn bộ nửa sau của cuộn băng sang một cuộn băng khác để chừa chỗ trống. Một cơn ác mộng thực sự!
Kỷ nguyên 2: Lõi Từ Tính đắt đỏ và "Kẻ cứu rỗi" Linked List (1950s)
Đến giữa những năm 1950, nhân loại đón nhận một phát minh mang tính cách mạng: Magnetic Core Memory (Bộ nhớ Lõi từ tính). Lần đầu tiên trong lịch sử, máy tính có thể "chỉ tay" vào một vị trí bất kỳ trong bộ nhớ và lấy dữ liệu ra ngay lập tức mà không cần tua băng. Đây là khởi thủy của RAM (Random Access Memory).
Nhưng có một cái giá phải trả: Nó đắt một cách vô lý. Mỗi bit nhớ (chứa số 0 hoặc 1) là một vòng khuyên kim loại nhỏ xíu, được các nữ công nhân đan bằng tay qua kính hiển vi. Giá của bộ nhớ lõi từ tính thời điểm đó lên tới 1 USD cho MỖI BIT (Tương đương với việc thanh RAM 8GB hiện tại của bạn sẽ có giá khoảng... 64 tỷ USD!).
Vấn đề: Sự phân mảnh (Fragmentation)
Vì bộ nhớ quá đắt, mọi byte đều là vàng. Nếu bạn dùng Array, bạn bắt buộc phải xin Hệ điều hành một vùng nhớ Liên tục (Contiguous). Nhưng nếu RAM đang bị phân mảnh (chỗ này trống 2 byte, chỗ kia trống 3 byte) thì Hệ điều hành bó tay, không thể tạo ra mảng 5 byte được dù tổng dung lượng vẫn còn!
Giải pháp: Sự ra đời của Linked List (Danh sách liên kết)
Năm 1958, John McCarthy (Cha đẻ của Trí tuệ nhân tạo) đang thiết kế ngôn ngữ LISP trên siêu máy tính IBM 704. Ông tuyệt vọng trước việc quản lý bộ nhớ đắt đỏ.
Ông phát hiện ra một tính năng thú vị của phần cứng IBM 704: Mỗi thanh ghi (Word) của nó dài 36-bit, được chia làm 2 thanh ghi con: Address (15-bit) và Decrement (15-bit). Ý tưởng lóe sáng: Tại sao không dùng một nửa chứa Dữ liệu, nửa kia chứa ĐỊA CHỈ TRỎ TỚI vùng nhớ tiếp theo?
Thuật ngữ kinh điển của LISP ra đời:
car(Contents of the Address part of Register): Chứa Dữ liệu.cdr(Contents of the Decrement part of Register): Chứa Con trỏ (Pointer).
Bùm! Linked List chính thức ra đời. Nhờ con trỏ cdr, dữ liệu không cần nằm liên tiếp nhau nữa. Chúng có thể nằm rải rác ở bất kỳ đâu trong RAM, "tận thu" từng byte trống lẻ tẻ cuối cùng, giải cứu các máy tính khỏi thảm họa phân mảnh bộ nhớ.
Sự ma thuật của Random Access và Hash Table (1953)
Cũng trong chính kỷ nguyên Lõi từ tính đắt đỏ này, một đặc tính cực kỳ quan trọng của bộ nhớ mới đã thay đổi hoàn toàn cách chúng ta tìm kiếm: Random Access (Truy cập ngẫu nhiên). Khác với Băng từ phải tua đi tua lại, lõi từ tính cho phép máy tính lấy dữ liệu ở bất kỳ địa chỉ nào với cùng một tốc độ $O(1)$.
Năm 1953, Hans Peter Luhn (nhà nghiên cứu tại IBM) đã nảy ra một ý tưởng điên rồ: Thay vì phải chạy vòng lặp tìm kiếm trên một mảng dài ngoẵng (rất tốn CPU), tại sao không lấy "từ khóa" đem băm (Hash) ra thành một con số toán học, và dùng con số đó làm ĐỊA CHỈ TRỰC TIẾP trong RAM?
Ý tưởng này khai sinh ra Hash Table (Bảng băm) — cấu trúc dữ liệu sinh ra để vắt kiệt tối đa sức mạnh "Random Access" của phần cứng, biến việc tìm kiếm từ $O(N)$ xuống còn $O(1)$ thần tốc.