Xem thêm

Hướng dẫn thực hành C++ Standard Template Library (STL)

Huy Erick
C++ STL là một phần không thể thiếu trong lập trình C++. Nếu bạn đang học đại học và muốn làm việc trong các công ty công nghệ, các câu hỏi về C++ STL sẽ...

C++ STL là một phần không thể thiếu trong lập trình C++. Nếu bạn đang học đại học và muốn làm việc trong các công ty công nghệ, các câu hỏi về C++ STL sẽ thường được đặt trong buổi phỏng vấn.

C++ STL là một bộ sưu tập các cấu trúc dữ liệu và thuật toán mà chúng ta thường sử dụng khi viết code. Ví dụ, khi giải quyết một vấn đề, bạn có thể muốn sử dụng danh sách liên kết. Nhưng liệu bạn có tạo danh sách liên kết từ đầu không? Câu trả lời đó là không. Thay vào đó, bạn có thể sử dụng danh sách liên kết có sẵn trong thư viện C++ STL.

Các thuật toán

Các thuật toán trong C++ STL cung cấp nhiều hoạt động khác nhau cho các cấu trúc dữ liệu. Dưới đây là một số thuật toán nổi tiếng:

  • sort() - sắp xếp dữ liệu.
  • binary_search() - tìm kiếm phần tử.

Các container

Container là lớp chứa các đối tượng và dữ liệu. Dưới đây là một số container phổ biến:

  • vector
  • list
  • forward_list
  • queue
  • priority_queue
  • stack
  • set
  • multiset
  • map
  • multimap
  • unordered_set

Trình lặp (Iterators)

Như tên gọi, iterators được sử dụng để làm việc trên một chuỗi các giá trị.

Hàm tử (Functors)

STL bao gồm các lớp nạp chồng toán tử để gọi hàm. Các thể hiện của các lớp này được gọi là functors hoặc hàm tử.

Trước khi đi vào chi tiết, hãy tìm hiểu về Pair.

alt text

Bây giờ chúng ta sẽ bắt đầu với container cơ bản nhất là vector.

Vector - Container đa năng

alt text

Trong hàm displayVector đầu tiên, chúng ta sử dụng phương pháp thông thường. Đây là một trình lặp hằng số đối với vector của int. Ban đầu nó là một con trỏ đến phần tử đầu tiên trong vector.

Trong hàm autoVector thứ hai, chúng ta sử dụng từ khóa auto để tự động lấy kiểu dữ liệu. Tuy nhiên, auto không được hỗ trợ bởi tất cả các trình biên dịch cũ.

Hàm thứ ba là một viết tắt của hai hàm trên. Nó có nghĩa là "đối với mọi phần tử x kiểu int trong vectơ, in ra các phần tử đó".

Hãy nhớ là trong tất cả các hàm trên, chúng ta đã truyền vector bằng tham chiếu để không tạo bản sao khi truyền vector và đã đặt vector là const để đảm bảo rằng vector không bị thay đổi và trình biên dịch có thể tối ưu hóa một số tương tác với vector kiểu const.

Ma trận 2D sử dụng vector

alt text

Đây là một số phép toán cơ bản trên vector. Nhưng nếu chúng ta muốn chèn nhiều phần tử nhiều lần và thực hiện các truy vấn như lower_bound và upper_bound nhiều lần, việc sắp xếp vector nhiều lần sẽ mất thời gian O(NlogN) với chi phí (logN) để duy trì các phần tử đã sắp xếp bên trong. Vì vậy, tại đó, set trở thành ứng viên hoàn hảo để sử dụng.

alt text

Set duy trì các phần tử theo thứ tự bên trong để chúng ta không cần phải gọi phương thức sắp xếp, tiết kiệm thời gian O(NlogN) với chi phí (logN) để duy trì các phần tử đã sắp xếp bên trong set.

alt text

Bản đồ liên kết các khóa với giá trị tương ứng. Bạn có thể truy cập giá trị dựa trên khóa của chúng.

Bản đồ được triển khai như một cây tìm kiếm nhị phân (BST).

alt text

Bên trong, unordered_map được triển khai dưới dạng một mảng. Bất kể khóa bạn cung cấp, nó sẽ được chuyển thành một hàm băm (hashing function) và một chỉ mục sẽ được tạo bởi hàm băm đó, sau đó giá trị sẽ được lưu trữ tại chỉ mục tương ứng trong mảng.

Để tìm hiểu thêm về hashing, bạn có thể đọc thêm bài viết về chủ đề này.

Stack là một cấu trúc dữ liệu hoạt động theo cơ chế LIFO (Last In, First Out), có nghĩa là phần tử cuối cùng được chèn là phần tử đầu tiên được truy xuất. Hãy tưởng tượng nó như một chồng sách.

alt text

Queue là một cấu trúc dữ liệu hoạt động theo cơ chế FIFO (First In, First Out), có nghĩa là phần tử được chèn đầu tiên sẽ được truy xuất đầu tiên.

alt text

Danh sách (list) trong C++ STL được triển khai như một "danh sách liên kết kép". Điều này có nghĩa là mỗi phần tử trong danh sách lưu trữ một con trỏ đến phần tử trước và phần tử tiếp theo.

alt text

Danh sách chuyển tiếp (forward_list) được triển khai dưới dạng một "danh sách liên kết đơn". Các phần tử trong danh sách chỉ trỏ đến phần tử tiếp theo.

alt text

Đừng hiển thị danh sách như trong phương thức displayWrong dưới đây. Hãy sử dụng phương thức displayCorrect hoặc phương thức rút gọn để hiển thị danh sách đúng.

Điểm sai trong phương thức displayWrong là bạn không thể so sánh iterator với l.end() trong khi trường hợp cấu trúc dữ liệu không liên tục.

it < l.end() là sai, nó phải là it != l.end().

Hàng đợi ưu tiên là một cấu trúc dữ liệu cho phép trích xuất phần tử lớn nhất theo thời gian không đổi, với mức độ phức tạp logarit. Trình lặp theo mặc định là binary-max-heap.

Bạn cũng có thể triển khai binary-min-heap như sau:

alt text

Lưu ý rằng bạn không thể lặp lại trên priority_queue, bạn chỉ có thể lấy phần tử trên cùng và tăng nó để xem tất cả các phần tử.

priority_queue được sử dụng rộng rãi trong nhiều thuật toán tiêu chuẩn như thuật toán Dijkstra cho đường đi ngắn nhất.

alt text

Ở đây, chúng ta sử dụng macro để tiết kiệm thời gian và tăng năng suất khi làm việc với C++. Nếu bạn muốn tìm hiểu cách tăng năng suất của mình với C++, hãy tham khảo bài viết này.

C++ STL cũng cung cấp các phép toán trên tập hợp như union, intersection, và nhiều phép toán khác.

alt text

Hãy nhớ rằng trước khi áp dụng các phép toán tập hợp, vector phải được sắp xếp. Nếu không, kết quả có thể không như mong muốn. Vì vậy, xin hãy xem dòng code số 15.

Ở đây, bên phải của dấu bằng, chúng ta tạo một vector từ đầu (temp.begin()) và một iterator kết thúc. Iterator cuối cùng này được trả về bởi set_union.

Một ứng dụng khác là khi tìm các hoán vị.

alt text

Tóm lại, C++ STL cung cấp cho chúng ta một bộ công cụ mạnh mẽ để làm việc với cấu trúc dữ liệu và thuật toán. Hi vọng bài viết này giúp bạn hiểu thêm về thư viện quan trọng này và làm việc hiệu quả hơn với C++.

1