Có một khoảng cách rất lớn giữa việc biết cú pháp lập trình và khả năng giải quyết một bài toán chưa từng gặp. Bạn có thể viết hàm, vòng lặp, mảng và con trỏ, nhưng vẫn không biết bắt đầu từ đâu khi đề bài yêu cầu xử lý hàng trăm nghìn phần tử trong thời gian ngắn.
Algorithmic Thinking: Learn Algorithms to Level Up Your Coding Skills, 2nd Edition của Daniel Zingaro được viết cho chính khoảng cách ấy. Đây không phải cuốn sách đưa ra một danh sách thuật toán để ghi nhớ. Tác giả đặt người đọc trước những bài toán thực tế, cùng thử một lời giải tự nhiên, tìm ra nguyên nhân khiến nó chậm hoặc sai, rồi từng bước lựa chọn cấu trúc dữ liệu và thuật toán phù hợp hơn.
Khi đọc đúng cách, cuốn sách giống một khóa hướng dẫn giải bài có người kèm bên cạnh. Người đọc không chỉ nhìn thấy lời giải cuối cùng mà còn theo dõi những ngõ cụt, các phương án chưa tối ưu và bước chuyển quan trọng dẫn tới lời giải hiệu quả.
Daniel Zingaro xây cuốn sách dựa trên năm nguyên tắc giảng dạy rất rõ:
Nhờ cách tiếp cận này, bảng băm, quy hoạch động, đồ thị hoặc cây phân đoạn không xuất hiện như những khái niệm trừu tượng. Mỗi công cụ chỉ được đưa vào khi lời giải hiện tại gặp một nút thắt mà công cụ đó có thể tháo gỡ.
Đây là khác biệt quan trọng với giáo trình thuật toán truyền thống: sách không chỉ nói “thuật toán này có độ phức tạp O(n log n)”, mà cho thấy vì sao lời giải O(n²) không vượt qua giới hạn, thao tác nào đang bị lặp quá nhiều và thay đổi cấu trúc dữ liệu ra sao để giảm chi phí.
Xuyên suốt cuốn sách là một quy trình tư duy có thể tái sử dụng:
Quy trình này có giá trị vượt ra ngoài lập trình thi đấu. Nó cũng là cách một kỹ sư phân tích code chậm, thiết kế thành phần xử lý dữ liệu hoặc giải thích lựa chọn kỹ thuật trong phỏng vấn.
Sách sử dụng thử thách từ DMOJ, SPOJ, IOI, USACO, Olympic Tin học Canada và các hệ thống chấm trực tuyến khác. Những bối cảnh như nhận diện bông tuyết, kiểm tra mật khẩu, tìm đường trong mê cung, truy tìm quân mã, quản lý cộng đồng hoặc ghép nắp với chai khiến bài học bớt khô cứng.
Điểm quan trọng không nằm ở câu chuyện của đề bài mà ở cấu trúc ẩn phía sau. Một bài toán về bông tuyết có thể dẫn tới bảng băm; một bài toán di chuyển có thể trở thành đồ thị; một yêu cầu tối ưu tưởng như phải thử mọi khả năng có thể được chuyển thành tìm kiếm nhị phân trên đáp án.
Người đọc được rèn kỹ năng nhận dạng: nhìn qua lớp mô tả đời thường để tìm ra mô hình thuật toán thực sự.
Chương mở đầu không dùng bảng băm như một định nghĩa có sẵn. Tác giả bắt đầu từ những bài toán mà việc so sánh từng phần tử khiến chương trình quá chậm. Từ nút thắt đó, người đọc xây hàm băm, tổ chức bucket, xử lý xung đột và học cách đánh giá chất lượng của cách phân bố khóa.
Các bài toán về bông tuyết, hỗn loạn đăng nhập và kiểm tra chính tả cho thấy bảng băm hữu ích khi cần phát hiện trùng lặp hoặc tra cứu rất nhiều lần. Đây cũng là chương giúp người học hiểu vì sao một cấu trúc dữ liệu có thể thay đổi hoàn toàn hiệu năng dù thuật toán tổng thể nhìn qua vẫn giống nhau.
Sách xây cây nhị phân bằng C, làm việc trực tiếp với node, con trỏ và cấp phát bộ nhớ. Người đọc so sánh cách duyệt bằng ngăn xếp với lời giải đệ quy, từ đó hiểu đệ quy không phải “một hàm tự gọi lại chính nó” đơn thuần mà là cách giải một đối tượng thông qua những đối tượng con cùng dạng.
Khi chuyển sang cây gia phả và các cấu trúc phân nhánh khác, người đọc học cách xác định trường hợp cơ sở, thông tin cần trả về từ mỗi lời gọi và cách kết hợp kết quả của các nhánh.
Quy hoạch động thường là phần khiến nhiều người học thuật toán nản nhất. Cuốn sách dành hai chương liên tiếp để tháo gỡ chủ đề này.
Chương đầu dẫn từ lời giải đệ quy có nhiều phép tính lặp lại đến ghi nhớ kết quả, sau đó chuyển sang bảng quy hoạch động. Người đọc học cách xác định trạng thái, quan hệ giữa các bài toán con, điều kiện cơ sở và thứ tự tính toán.
Chương quy hoạch động nâng cao – nội dung mới nổi bật của ấn bản thứ hai – đi sâu vào những tình huống khó hơn:
Giá trị lớn nhất của hai chương này là người đọc được thấy công thức quy hoạch động hình thành từng bước, thay vì xuất hiện đột ngột như một đáp án phải học thuộc.
Không phải đề bài nào cũng dùng các từ “đỉnh” hoặc “cạnh”. Trạng thái của một trò chơi, vị trí của quân mã hoặc các bước biến đổi dữ liệu đều có thể tạo thành một đồ thị nếu ta xác định được trạng thái và phép chuyển hợp lệ.
Sách hướng dẫn biểu diễn đồ thị và triển khai tìm kiếm theo chiều rộng (BFS) để tìm số bước ít nhất trong đồ thị không trọng số. Nội dung còn mở rộng sang BFS 0-1 và so sánh khi nào nên mô hình hóa bài toán bằng đồ thị, khi nào quy hoạch động tự nhiên hơn.
Đây là kỹ năng quan trọng: lựa chọn đúng mô hình thường quyết định lời giải trước cả khi bắt đầu viết mã.
Khi các cạnh không còn có cùng chi phí, BFS thông thường không đủ. Chương này xây dựng thuật toán Dijkstra, giải thích danh sách kề, ma trận kề, cách chọn đỉnh tiếp theo và vai trò của trọng số không âm.
Người đọc còn được tiếp cận cách đếm số đường đi ngắn nhất, xem xét cạnh có trọng số âm và hiểu vì sao một lựa chọn biểu diễn đồ thị có thể ảnh hưởng tới cả bộ nhớ lẫn thời gian chạy. Phụ lục tiếp tục mở rộng bằng cách dùng heap để tăng tốc Dijkstra.
Phần thú vị nhất của chương tìm kiếm nhị phân không phải tìm một số trong mảng đã sắp xếp. Sách mở rộng kỹ thuật này sang các bài toán tối ưu bằng cách chuyển câu hỏi “đáp án tốt nhất là bao nhiêu?” thành “giá trị này có khả thi hay không?”.
Nếu tính khả thi thay đổi theo một ranh giới đơn điệu, tìm kiếm nhị phân có thể xác định ranh giới ấy nhanh hơn rất nhiều so với thử lần lượt từng giá trị. Đây là mẫu tư duy thường xuất hiện trong lập trình thi đấu và phỏng vấn kỹ thuật nhưng dễ bị bỏ qua trong các bài giảng nhập môn.
Heap phù hợp khi chương trình liên tục cần phần tử nhỏ nhất hoặc lớn nhất nhưng không cần sắp xếp toàn bộ dữ liệu sau mỗi thay đổi. Cây phân đoạn hỗ trợ truy vấn và cập nhật trên các đoạn của mảng với hiệu năng ổn định.
Thay vì gọi thư viện rồi bỏ qua chi tiết, người đọc tự triển khai cả hai cấu trúc bằng C. Quá trình này làm rõ cách dữ liệu được lưu, cách node cha liên hệ với node con và vì sao mỗi thao tác có chi phí logarithmic.
Union-find giải quyết một lớp bài toán mà điều quan trọng là biết hai phần tử có thuộc cùng nhóm hay không và hợp nhất hai nhóm khi có quan hệ mới.
Sách đi từ triển khai cơ bản đến hợp nhất theo kích thước và nén đường đi. Qua các bài toán cộng đồng, bạn–thù và phân loại, người đọc thấy vì sao union-find có thể đơn giản và nhanh hơn việc duyệt lại toàn bộ đồ thị sau mỗi thay đổi.
Đây là chương hoàn toàn mới của ấn bản thứ hai. Tác giả phân biệt thuật toán tất định với thuật toán ngẫu nhiên, Monte Carlo với Las Vegas và giải thích cách sử dụng lựa chọn ngẫu nhiên để tránh những trường hợp đầu vào bất lợi.
Các ví dụ và Quicksort giúp người đọc thấy ngẫu nhiên hóa không phải “đoán mò”. Đó là một công cụ thiết kế có thể làm thuật toán đơn giản hơn hoặc mang lại hiệu năng tốt trong thực tế, miễn là hiểu loại bảo đảm mà thuật toán cung cấp.
Sách dành phụ lục riêng cho phân tích thời gian chạy và ký hiệu Big O. Phần này giúp người đọc đánh giá số thao tác tăng như thế nào khi dữ liệu lớn lên, phân biệt thời gian hằng, logarithmic, tuyến tính, n log n, bậc hai và các mức cao hơn.
Big O trong cuốn sách không được trình bày như một bài toán biến đổi toán học. Nó được dùng như công cụ ra quyết định: với giới hạn đầu vào này, lời giải nào có khả năng chạy kịp và lời giải nào chắc chắn thất bại?
Người đọc thấy cả cách làm ban đầu, lý do nó không đủ nhanh và quá trình thay thế từng phần. Đây là kiến thức có thể chuyển sang bài toán mới, khác với việc ghi nhớ một đoạn mã mẫu.
Bảng băm xuất hiện khi tra cứu quá chậm; heap xuất hiện khi liên tục lấy phần tử cực trị; union-find xuất hiện khi phải duy trì các nhóm. Mối quan hệ vấn đề–công cụ giúp kiến thức dễ nhớ và dễ nhận dạng hơn.
Hai chương liên tiếp trình bày đệ quy, ghi nhớ kết quả, công thức xuôi/ngược, trạng thái nhiều chiều và tối ưu bộ nhớ. Đây là lợi thế rõ rệt cho người từng biết định nghĩa quy hoạch động nhưng chưa tự xây được lời giải.
Con trỏ, cấp phát bộ nhớ, mảng và cấu trúc được sử dụng để xây bảng băm, cây, heap, cây phân đoạn và union-find. Người đọc hiểu dữ liệu nằm ở đâu và mỗi thao tác phải làm những gì, thay vì phụ thuộc hoàn toàn vào thư viện.
Các thử thách có giới hạn đầu vào, thời gian chạy và trường hợp biên thực tế. Hệ thống chấm trực tuyến giúp người học nhận phản hồi ngay từ mã của chính mình.
Bản mới cải thiện nhiều giải thích, bổ sung bài toán bảng băm khó hơn, mở rộng cách tìm bài toán con trong quy hoạch động, thêm chương quy hoạch động nâng cao và chương ngẫu nhiên hóa.
Code Companion tập hợp mã theo chương, nối lại các listing bị chia qua ranh giới trang và ghi vị trí tương ứng trong bản dịch. Người học có thể tìm nhanh hàm, cấu trúc dữ liệu, dữ liệu mẫu hoặc phiên bản hoàn chỉnh của chương trình.
Đây không phải sách dạy lập trình C từ con số 0. Người đọc nên biết biến, vòng lặp, hàm, mảng và cách đọc chương trình; con trỏ cùng cấp phát bộ nhớ động có thể ôn trước hoặc học song song.
Cuốn sách ưu tiên trực giác, triển khai và giải bài hơn chứng minh toán học hình thức. Người cần một giáo trình hàn lâm về tính đúng đắn hoặc phân tích thuật toán chuyên sâu nên sử dụng thêm tài liệu lý thuyết.
Trước mỗi lời giải, hãy dừng lại và viết ra cách tiếp cận đầu tiên của mình. Sau đó mới đọc phân tích của tác giả, ghi lại thao tác khiến lời giải chậm và dấu hiệu dẫn tới công cụ mới.
Khi đọc mã, nên tự gõ lại, chạy với dữ liệu mẫu rồi chủ động tạo trường hợp biên. Một ghi chú tốt cho mỗi bài toán nên có bốn mục: dấu hiệu nhận dạng, mô hình dữ liệu, thuật toán được chọn và độ phức tạp thời gian.
Sau khi hoàn thành chương, hãy nộp lời giải lên hệ thống chấm được sách giới thiệu. Phản hồi từ test ẩn giúp phát hiện những giả định sai hoặc trường hợp biên mà việc đọc mã bằng mắt khó nhận ra.
Bản Expert Version gồm bản dịch chính 498 trang, được đối chiếu với sách gốc và sử dụng hệ thuật ngữ thuật toán nhất quán. Code Companion 166 trang hỗ trợ tra cứu mã theo chương; eBook tiếng Anh tặng kèm giúp đối chiếu thuật ngữ và nội dung khi cần.
Sách in ruột đen trắng, bìa mềm full màu, giấy chống lóa mắt hiện đại và chữ in sắc nét. Nội dung được cam kết chuẩn 100% từ sách gốc; sản phẩm áp dụng bảo hành lỗi in 1 đổi 1 trong 90 ngày kể từ ngày nhận sách.
Sách phù hợp với người mới học thuật toán nhưng đã biết lập trình cơ bản. Đây không phải sách dạy C từ đầu; người đọc nên hiểu biến, vòng lặp, hàm, mảng và có thể học thêm con trỏ trong quá trình đọc.
Toàn bộ mã chính được viết bằng C. Tuy nhiên, thuật toán và phương pháp giải bài có thể áp dụng khi lập trình bằng C++, Java, Python hoặc các ngôn ngữ khác.
Không. Tác giả chủ động ưu tiên trực giác, ví dụ và chương trình chạy được thay cho trình bày toán học nặng. Phụ lục Big O giải thích đủ kiến thức để phân tích hiệu năng các lời giải trong sách.
Ấn bản mới bổ sung bài toán bảng băm, cải thiện phần nhận diện bài toán con, thêm chương quy hoạch động nâng cao, mở rộng giải thích về heap và thêm chương hoàn toàn mới về thuật toán ngẫu nhiên.
Có. Sách rèn cách phân tích giới hạn, xây lời giải đơn giản, tìm nút thắt, lựa chọn cấu trúc dữ liệu và giải thích độ phức tạp – những kỹ năng quan trọng trong phỏng vấn coding.
Code Companion 166 trang tập hợp mã theo chương, nối các listing bị tách qua trang và chỉ rõ vị trí tương ứng trong bản dịch, giúp người đọc tra cứu nhanh khi thực hành.
Gồm bản dịch tiếng Việt chính 498 trang, Code Companion 166 trang và eBook tiếng Anh tặng kèm. Sách được bảo hành lỗi in 1 đổi 1 trong 90 ngày kể từ ngày nhận sản phẩm.