Think Like a Programmer #4 — Solving Problems with Arrays: Khi dữ liệu cần được tổ chức

Phong

🧠 Think Like a Programmer #4 — Solving Problems with Arrays: Khi dữ liệu cần được tổ chức

Ảnh: Brett Jordan — Pexels

Mở đầu

Hồi trước mình có kể về Chương 2 — Pure Puzzles, nơi Spraul dùng mấy bài toán in hình với vòng lặp để luyện tư duy "chia nhỏ vấn đề" và "nhận diện pattern". Lúc đó mình nghĩ: "Ừ cũng hay, nhưng mà mấy bài đó có vẻ đơn giản quá, áp dụng vô thực tế kiểu gì?"

Rồi tới Chương 3 — Solving Problems with Arrays, mình mới vỡ lẽ. Hoá ra tác giả đang chuẩn bị nền tảng: từ những pattern đơn giản (vòng lặp, điều kiện) lên tới cách tổ chức dữ liệu bằng array — một bước tiến hoá từ "làm sao để code chạy được" thành "làm sao để code thông minh".

Và đây mới là thứ mình cần.

Giải quyết vấn đề với mảng — Chương 3

Array có gì mà phải học cả một chương? Ai chẳng biết tạo mảng, gán giá trị, loop qua từng phần tử — cơ bản mà, đúng không?

Spraul không dạy cách dùng array. Ổng dạy cách nghĩ bằng array. Và đó là khác biệt lớn.

1. Mode — Bài toán tưởng dễ mà không dễ

Mở đầu chương là bài toán tính mode (giá trị xuất hiện nhiều nhất) trong một mảng số. Nghe đơn giản ha? Sort cái mảng lên, rồi đếm — hoặc dùng hash map (hay C++ gọi là histogram).

Nhưng cái mình tâm đắc là cách Spraul dẫn dắt. Ổng không đưa ra giải pháp tối ưu ngay. Ổng bắt đầu với cách sort + scan — dễ hiểu, dễ code, nhưng O(n log n). Rồi ổng mới chỉ ra rằng có cách histogram — dùng một cái array khác để đếm tần suất — chạy nhanh hơn nhiều (O(n)).

Bài học ở đây là gì? Một câu trong sách mình thấy rất hay: "a long journey is not a waste of time if you learned something". Đôi khi code giải pháp đơn giản trước — dù không tối ưu — cũng là một cách học quý giá. Rồi từ đó mới thấy được đường lên tối ưu.

Ảnh: Lukas Blazek — Pexels

2. Lookup Tables — "Nếu không nghĩ ra, tra mảng"

Một trong những kỹ thuật mình thấy hay nhất chương này là lookup tables. Thay vì viết cả đống câu lệnh if-else hay switch-case dài ngoằng, sao không đưa dữ liệu vào một cái mảng và tra cứu?

Spraul đưa ra ví dụ về giải mã thông điệp (decoding) — thay vì dùng mớ logic phức tạp để xử lý từng ký tự punctuation, ổng dùng một cái mảng lookup đơn giản. Code ngắn hơn, dễ đọc hơn, ít bug hơn.

Ngoài đời mình cũng gặp hoài cái pattern này. Ví dụ: ánh xạ status_code → tên trạng thái, hoặc error_code → message. Thay vì:

if (code == 1) return "Pending";
else if (code == 2) return "Approved";
else if (code == 3) return "Rejected";
...

Chỉ cần:

string statuses[] = {"", "Pending", "Approved", "Rejected", ...};
return statuses[code];

Gọn nhẹ, dễ maintain. Thêm status mới chỉ cần sửa cái mảng, không cần đụng tới logic.

3. Non-scalar Arrays — Theo dõi vị trí, không theo dõi giá trị

Một ý hay nữa: non-scalar arrays. Thay vì lưu giá trị (scalar) trong mảng, nhiều khi ta cần lưu vị trí hay trạng thái. Ví dụ: bài toán tìm đường đi, game, tracking positions.

Cái này mình thấy rất quen — làm front-end React hay gặp. State management mà chỉ lưu giá trị (value) thì không đủ, nhiều khi cần lưu index, vị trí, hoặc trạng thái phức hợp. Cùng một tư duy, áp dụng khung cảnh khác nhau.

4. Multidimensional Arrays — Tổ chức dữ liệu không gian

Mảng đa chiều — ai học matrix trong toán cũng gặp. Nhưng Spraul có một tip hay: dùng struct để gom các chiều lại thay vì dùng mảng 2D với address arithmetic phức tạp. Code sáng sủa hơn, ít sai sót hơn.

Ví dụ: thay vì board[row][col], tạo struct Cell { int row; int col; } rồi pass cái struct đó vào hàm — dễ đọc, dễ bảo trì. Cái này sau này mình thấy cũng là tiền đề cho OOP với classes ở Chương 5.

5. Array hay Vector? — Chọn đúng công cụ

Cuối chương, Spraul dành hẳn một phần để bàn về khi nào dùng array, khi nào dùng vector (trong C++). Quy tắc rất thực tế:

  • Array — khi biết trước kích thước và cần truy cập ngẫu nhiên nhanh
  • Vector — khi không biết trước kích thước, cần linh hoạt thêm/xoá
  • Process-as-you-go — nếu dữ liệu chỉ cần xử lý một lần, không cần lưu trữ

Cái này tưởng đơn giản nhưng nhiều lập trình viên (kể cả mình) từng chọn sai. Hồi mới học, mình toàn dùng array cho mọi thứ — rồi một hôm data đầu vào lớn hơn kích thước mảng khai báo, chương trình crash. Từ đó mới biết thương vector/ArrayList. 😅

Cảm nhận của mình

Đọc chương này, mình nhận ra một điều: Array không phải là thứ "basic" để học một lần rồi bỏ qua. Nó là một công cụ tư duy. Cách bạn tổ chức dữ liệu trong mảng quyết định cách bạn giải quyết vấn đề.

Kỹ thuật lookup tables là cái mình thấy áp dụng được ngay lập tức. Trong dự án thực tế, mình từng gặp một đoạn code xử lý loại giao dịch với 15 cái if-else lồng nhau. Đọc vào là muốn xỉu. Sau khi refactor thành lookup table (chỉ là một mảng ánh xạ transaction_type → handler), code ngắn đi 70% và bug cũng biến mất theo.

Một điểm trừ nho nhỏ: các ví dụ trong chương này chạy trên console C++ với dữ liệu tĩnh nên hơi khô khan. Mình ước gì tác giả đưa ra nhiều tình huống thực tế hơn — như xử lý log file, phân tích dữ liệu CSV, hoặc game đơn giản — để thấy rõ sức mạnh của array.

Nhưng nhìn chung, đây là chương mà bất kỳ lập trình viên nào (kể cả senior) cũng nên đọc để ôn lại fundamentals. Đôi khi cái đơn giản nhất lại bị xem nhẹ, nhưng nó mới là thứ làm nên sự khác biệt giữa code "chạy được" và code "tốt".

Ảnh: Christina Morillo — Pexels

Kết

Chương 3 là bước chuyển từ "tư duy thuần tuý" (vòng lặp, rẽ nhánh) sang "tư duy có cấu trúc dữ liệu". Array là cấu trúc dữ liệu cơ bản nhất, nhưng nếu dùng đúng cách, nó có thể giải quyết được rất nhiều vấn đề phức tạp.

Bài học lớn nhất mình rút ra: đừng coi thường những thứ cơ bản. Array, loop, conditional — ghép chúng lại đúng cách chính là chìa khoá để giải quyết vấn đề. Và lookup table là một kỹ thuật cực kỳ đơn giản nhưng cực kỳ hiệu quả mà mình sẽ dùng nhiều hơn.

Hẹn mấy bạn ở bài tiếp theo — Chương 4: Pointers & Dynamic Memory — nơi Spraul đưa ta vào thế giới heap, con trỏ, và những cạm bẫy memory leak kinh điển. Chắc sẽ đau đầu lắm đây! 🚀

📋 Phụ lục thuật ngữ
- Array — mảng, cấu trúc dữ liệu lưu các phần tử cùng kiểu, truy cập theo chỉ số
- Mode — giá trị xuất hiện nhiều nhất trong một tập hợp
- Lookup table — bảng tra cứu, dùng mảng để thay thế logic điều kiện phức tạp
- Histogram — mảng đếm tần suất, mỗi index đại diện cho một giá trị
- Vector — cấu trúc dữ liệu động trong C++, tương tự ArrayList trong Java / List trong C#
- Multidimensional array — mảng đa chiều, thường dùng cho ma trận, bảng, lưới

Tags: #sách #thinklikeaprogrammer #problemsolving #softwareengineering #arrays #datastructures