STL :
In C++, "STL" stands for the Standard Template Library. It is a powerful set of C++ template classes to provide general-purpose classes and functions with templates that implement many popular and commonly used algorithms and data structures like vectors, lists, queues, and stacks.
FOUR COMPONENTS OF STL:
Containers:
Containers are objects that store data. They can be sequences (like vectors, lists, and dequeues), associative (like sets and maps), or adapters (like stack and queue).
HOW TO CHOOSE THE RIGHT CONTAINERS:
Array:
Use when the size is known at compile time.
Offers constant time complexity for random access.
Limited flexibility for resizing.
Vector:
Use for dynamic arrays with automatic resizing.
Offers random access with efficient insertions and deletions at the end.
Good for scenarios where the size is not known in advance.
Deque (Double-Ended Queue):
Use when you need efficient insertions and deletions at both ends.
Provides constant time complexity for these operations.
Suitable for scenarios where elements need to be added or removed from both the front and the back.
List:
Use for efficient insertions and deletions at any position.
Provides constant time complexity for insertions and deletions, but slower for random access compared to arrays and vectors.
Stack:
Use for Last In First Out (LIFO) behavior.
Suitable for scenarios where elements need to be added and removed from the same end (usually the top).
Queue:
Use for First In First Out (FIFO) behavior.
Suitable for scenarios where elements need to be added at one end (rear) and removed from the other end (front).
Priority Queue:
Use when elements need to be processed in order of priority.
Maintains elements in a way that the element with the highest priority is served before others.
Set:
Use when you need a collection of unique elements.
Provides efficient lookup operations.
Unordered_set:
Similar to set but does not guarantee any specific order of elements.
Provides constant time complexity for insertion, deletion, and lookup on average.
Multiset:
Similar to unordered_set but allows duplicate elements.
Map:
Use when you need key-value associations with unique keys.
Provides efficient lookup based on keys.
Unordered_map:
Similar to map but does not guarantee any specific order of key-value pairs.
Provides constant time complexity for insertion, deletion, and lookup on average.
Multimap:
Similar to unordered_map but allows duplicate keys
Algorithms:
A collection of functions for performing various operations on data structures. These include searching, sorting, manipulating, and more.
Sorting:
Works: Sorting a vector in ascending order.
Syntax: sort(first_iterator, last_iterator)
Example:
vector<int> myVector = {3, 1, 4, 1, 5, 9, 2, 6, 5};
sort(myVector.begin(), myVector.end());
Sorting(Descending order)
Works: Sorting a vector in descending order.
Syntax: sort(first_iterator, last_iterator, greater<int>())
Example:
vector<int> myVector = {3, 1, 4, 1, 5, 9, 2, 6, 5};
sort(myVector.begin(), myVector.end(), greater<int>());
Reversing:
Works: Reversing the order of elements in a vector.
Syntax: reverse(first_iterator, last_iterator)
Example:
vector<int> myVector = {1, 2, 3, 4, 5};
reverse(myVector.begin(), myVector.end());
Finding Maximum and Minimum:
Works: Finding the maximum element in a vector.
Syntax: max_element(first_iterator, last_iterator)
Example:
vector<int> myVector = {3, 1, 4, 1, 5, 9, 2, 6, 5};
auto maxElement = max_element(myVector.begin(), myVector.end());
Works: Finding the minimum element in a vector.
Syntax: min_element(first_iterator, last_iterator)
Example:
vector<int> myVector = {3, 1, 4, 1, 5, 9, 2, 6, 5};
auto minElement = min_element(myVector.begin(), myVector.end());
Accumulation:
Works: Calculating the sum of all elements in a vector.
Syntax: accumulate(first_iterator, last_iterator, initial_value_of_sum)
Example:
vector<int> myVector = {1, 2, 3, 4, 5};
int sum = accumulate(myVector.begin(), myVector.end(), 0);
Counting:
Works: Counting occurrences of a value in a vector.
Syntax: count(first_iterator, last_iterator, x)
Example:
vector<int> myVector = {1, 2, 1, 4, 1, 5, 1};
int countOfOnes = count(myVector.begin(), myVector.end(), 1);
Finding Elements:
Works: Finding the first occurrence of a value in a vector.
Syntax: find(first_iterator, last_iterator, x)
Example:
vector<int> myVector = {1, 2, 3, 4, 5};
auto it = find(myVector.begin(), myVector.end(), 3);
Binary Search:
Works: Checking if a value exists in a sorted vector.
Syntax: binary_search(first_iterator, last_iterator, x)
Example:
vector<int> myVector = {1, 2, 3, 4, 5};
bool exists = binary_search(myVector.begin(), myVector.end(), 3);
Lower and Upper Bounds:
Works: Finding the lower bound of a value in a sorted vector.
Syntax: lower_bound(first_iterator, last_iterator, x)
Example:
vector<int> myVector = {1, 2, 4, 4, 5};
auto lb = lower_bound(myVector.begin(), myVector.end(), 3);
Works: Finding the upper bound of a value in a sorted vector.
Syntax: upper_bound(first_iterator, last_iterator, x)
Example:
vector<int> myVector = {1, 2, 4, 4, 5};
auto ub = upper_bound(myVector.begin(), myVector.end(), 4);
Next Permutation:
Works: Rearranging elements into the next lexicographically greater permutation.
Syntax: next_permutation(first_iterator, last_iterator)
Example:
vector<int> myVector = {1, 2, 3};
next_permutation(myVector.begin(), myVector.end());
Previous Permutation:
Works: Rearranging elements into the previous lexicographically smaller permutation.
Syntax: prev_permutation(first_iterator, last_iterator)
Example:
vector<int> myVector = {3, 2, 1};
prev_permutation(myVector.begin(), myVector.end());
Functors:
Function objects or functors are objects that can be called as if they were functions. They are frequently used in conjunction with algorithms.
Iterators:
Iterators are objects that allow the traversal of elements in a container. They provide a way to access the elements of a container sequentially.
Types of iterators: In C++, iterators are objects that allow the traversal of elements in a container. The categorization of iterators is based on their capabilities and the operations they support. Here are three main categories of iterators:
Forward Iterator:
Capabilities: Supports single-pass traversal of elements in one direction (forward).
Operations: Can be incremented (++) to move to the next element.
Example Containers: std::forward_list, std::unordered_set, std::unordered_map.
#include <forward_list>
forward_list<int> myList = {1, 2, 3, 4, 5};
auto it = myList.begin(); // Forward iterator
++it; // Move to the next element
Bidirectional Iterator:
Capabilities: Supports both forward and backward traversal of elements.
Operations: Can be incremented (++) to move forward and decremented (--) to move backward.
Example Containers: std::list, std::set, std::map, std::multiset, std::multimap.
#include <list>
list<int> myList = {1, 2, 3, 4, 5};
auto it = myList.begin(); // Bidirectional iterator
++it; // Move to the next element
--it; // Move back to the previous element
Random Access Iterator:
Capabilities: Supports all operations of forward and bidirectional iterators, plus random access operations like addition and subtraction.
Operations: Supports ++, --, +, -, and comparisons (<, >, <=, >=).
Example Containers: std::vector, std::deque, std::array.
#include <vector>
vector<int> myVector = {1, 2, 3, 4, 5};
auto it = myVector.begin(); // Random access iterator
++it; // Move to the next element
it = it + 2; // Move two positions forward
N.B: The choice of iterator category depends on the specific requirements of the algorithm or operation you are performing. For example, if you only need to traverse elements in one direction and don't need to move backward, a forward iterator might be sufficient. If you need to move both forward and backward, a bidirectional iterator is appropriate. If you also need random access capabilities, then a random access iterator is necessary for more efficient indexing and movement within the container.
Below is a summary of various C++ containers and the types of iterators they support:
Array:
Supports random access iterators.
Example:
int myArray[5] = {1, 2, 3, 4, 5};
int *it = &myArray[2]; // Random access iterator
Vector:
Supports random access iterators.
Example:
#include <vector>
vector<int> myVector = {1, 2, 3, 4, 5};
auto it = myVector.begin() + 2; // Random access iterator
Deque:
Supports random access iterators.
Example:
#include <deque>
deque<int> myDeque = {1, 2, 3, 4, 5};
auto it = myDeque.begin() + 2; // Random access iterator
List:
Supports bidirectional iterators.
Example:
#include <list>
list<int> myList = {1, 2, 3, 4, 5};
auto it = next(myList.begin(), 2); // Bidirectional iterator
Stack and Queue:
Do not support direct iterators.
Elements are accessed through operations like top() for the top of the stack or front() for the front of the queue.
Priority Queue:
Does not support direct iterators.
Access elements through operations like top().
Set:
Supports bidirectional iterators.
Example:
#include <set>
set<int> mySet = {1, 2, 3, 4, 5};
auto it = mySet.find(3); // Bidirectional iterator
Unordered_set:
Supports forward iterators.
Example:
#include <unordered_set>
unordered_set<int> myUnorderedSet = {1, 2, 3, 4, 5};
auto it = myUnorderedSet.find(3); // Forward iterator
Multiset:
Supports bidirectional iterators.
Example:
#include <set>
multiset<int> myMultiset = {1, 2, 2, 3, 4};
auto it = myMultiset.find(2); // Bidirectional iterator
Map:
Supports bidirectional iterators.
Example:
#include <map>
map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};
auto it = myMap.find(2); // Bidirectional iterator
Unordered_map:
Supports forward iterators.
Example:
#include <unordered_map>
unordered_map<int, std::string> myUnorderedMap = {{1, "one"}, {2, "two"}, {3, "three"}};
auto it = myUnorderedMap.find(2); // Forward iterator
Multimap:
Supports bidirectional iterators.
Example:
#include <map>
multimap<int, std::string> myMultimap = {{1, "one"}, {2, "two"}, {2, "another two"}};
auto it = myMultimap.find(2); // Bidirectional iterator
CONGRATULATION NOW YOU ARE THE MASTER OF STL