STL Complete Notes
STL Complete Notes
Standard Template Library (STL) of C++ is a collection of template classes that provide data structures
such as arrays, vectors, queue, etc. STL is a library consisting of containers, algorithms, and iterators.
Standard Template Library (STL) is a collection of algorithms, data structures, and other
components that can be used to simplify the development of C++ programs
One of the key benefits of the STL is that it provides a way to write generic, reusable code that can
be applied to different data types. This means that you can write an algorithm once, and then use
it with different types of data without having to write separate code for each type.
As STL consists of a collection of template classes, it’s a generalized library that is independent of data
types.
Components Of STL
o Containers
o Algorithms
o Iterators
STL Containers
A container is an object that stores a collection of objects of a specific type. For example, if
we need to store a list of names, we can use a vector .
Sequential Containers
Associative Containers
Internally, sequential containers are implemented as arrays or linked lists data structures.
Types of Sequential Containers
Vector
List
Array
Stack
Deque
List :
Lists are sequence containers that allow non-contiguous memory allocation.
As compared to the vector, the list has slow traversal, but once a position has been found,
insertion and deletion are quick (constant time).
Operation :
front() – Returns the value of the first element in the list.
back() – Returns the value of the last element in the list.
push_front() – Adds a new element ‘g’ at the beginning of the list.
push_back() – Adds a new element ‘g’ at the end of the list.
pop_front() – Removes the first element of the list, and reduces the size of the list by 1.
pop_back() – Removes the last element of the list, and reduces the size of the list by 1.
insert() – Inserts new elements in the list before the element at a specified position.
size() – Returns the number of elements in the list.
begin() – begin() function returns an iterator pointing to the first element of the list.
end() – end() function returns an iterator pointing to the theoretical last element which follows
the last element.
Vector
Vectors are the same as dynamic arrays with the ability to resize itself automatically when an
element is inserted or deleted, with their storage being handled automatically by the container.
Vector elements are placed in contiguous storage so that they can be accessed and traversed
using iterators. In vectors, data is inserted at the end.
Operations :
1. size() – Returns the number of elements in the vector.
2. max_size() – Returns the maximum number of elements that the vector can hold.
3. capacity() – Returns the size of the storage space currently allocated to the vector
expressed as number of elements.
4. empty() – Returns whether the container is empty.
5. reserve() – Requests that the vector capacity be at least enough to contain n elements.
6. assign() – It assigns new value to the vector elements by replacing old ones
7. push_back() – It push the elements into a vector from the back
8. pop_back() – It is used to pop or remove elements from a vector from the back.
9. insert() – It inserts new elements before the element at the specified position
10. erase() – It is used to remove elements from a container from the specified position or
range.
11. swap() – It is used to swap the contents of one vector with another vector of same type.
Sizes may differ.
12. clear() – It is used to remove all the elements of the vector container
13. front() – Returns a reference to the first element in the vector
14. back() – Returns a reference to the last element in the vector
15. data() – Returns a direct pointer to the memory array used internally by the vector to store
its owned elements.
16. begin() – Returns an iterator pointing to the first element in the vector
17. end() – Returns an iterator pointing to the theoretical element that follows the last element
in the vector
In this example, we will be using the vector class to demonstrate the working of a sequential
container.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> numbers = {1, 100, 10, 70, 100}; // initialize a vector of int type
cout << "Numbers are: "; // print the vector
for(auto &num: numbers) {
cout << num << ", ";
}
return 0;
}
Output
Array :
Array classes knows its own size, whereas C-style arrays lack this property. So when passing
to functions, we don’t need to pass size of Array as a separate parameter.
With C-style array there is more risk of array being decayed into a pointer. Array classes don’t
decay into pointers
Array classes are generally more efficient, light-weight and reliable than C-style arrays.
Operations on array :-
1. at() :- This function is used to access the elements of array.
2. get() :- This function is also used to access the elements of array. This function is not the
member of array class but overloaded function from class tuple.
3. operator[] :- This is similar to C-style arrays. This method is also used to access
4. front() :- This returns reference to the first element of array.
5. back() :- This returns reference to the last element of array.
6. size() :- It returns the number of elements in array. This is a property that C-style arrays
lack.
7. max_size() :- It returns the maximum number of elements array can hold i.e, the size with
which array is declared. The size() and max_size() return the same value.
8. swap() :- The swap() swaps all elements of one array with other.
9. empty() :- This function returns true when the array size is zero else returns false.
10. fill() :- This function is used to fill the entire array with a particular value.
Deque
Double-ended queues are sequence containers with the feature of expansion and contraction
on both ends. They are similar to vectors, but are more efficient in case of insertion and deletion of
elements. Unlike vectors, contiguous storage allocation may not be guaranteed.
Double Ended Queues are basically an implementation of the data structure double-ended queue.
Operation :
Inserts an element. And returns an iterator that points
deque::insert()
to the first of the newly inserted elements.
Output
The deque gquiz is : 15 20 10 30
gquiz.size() : 4
gquiz.max_size() : 4611686018427387903
gquiz.at(2) : 10
gquiz.front() : 15
gquiz.back() : 30
gquiz.pop_front() : 20 10 30
gquiz.pop_back() : 20 10
Time complexity: O(1).
Stack
Stacks are a type of container adaptors with LIFO(Last In First Out) type of working, where a new
element is added at one end (top) and an element is removed from that end only.
Stack uses an encapsulated object of either vector or deque (by default) or list (sequential
container class) as its underlying container, providing a specific set of member functions to access
its elements.
A stack is a container adapter that uses the sequential container deque and provides a
restricted interface to support push() and pop() operations only.
#include <iostream>
#include <stack>
using namespace std;
int main() {
stack<int> numbers; // create a stack of ints
numbers.push(1); // push into stack
numbers.push(100);
numbers.push(10);
cout << "Numbers are: ";
// we can access the element by getting the top and popping
// until the stack is empty
while(!numbers.empty()) {
cout << numbers.top() << ", "; // print top element
numbers.pop();// pop top element from stack }
return 0;}
Output
Associative containers implement sorted data structures that can be quickly searched (O(log n)
complexity).
standard Library offers a variety of efficient containers
All container types in C++ are class templates, which implies they can store a wide variety of data types.
The Map Container differs from other Associative Containers in that it stores data as key-value pairs. The
Key is similar to the value's Identification feature in that it serves to retrieve and obtain values from the
Map. Credentials must be distinct, but values may not be.
If you're familiar with Dictionaries in Python, Maps in C++ are very similar.
Maps are associative containers that store elements in a mapped fashion. Each element has a key
value and a mapped value. No two mapped values can have the same key values.
Multimap
Multimap is similar to a map with the addition that multiple elements can have the same keys.
Also, it is NOT required that the key-value and mapped value pair have to be unique in this case.
One important thing to note about multimap is that multimap keeps all the keys in sorted order
always. These properties of multimap make it very much useful in competitive programming.
Some Basic Functions associated with multimap:
begin() – Returns an iterator to the first element in the multimap
end() – Returns an iterator to the theoretical element that follows last element in the multimap
size() – Returns the number of elements in the multimap
max_size() – Returns the maximum number of elements that the multimap can hold
empty() – Returns whether the multimap is empty
pair<int,int> insert(keyvalue,multimapvalue) – Adds a new element to the multimap
Multimaps are part of the C++ STL (Standard Template Library). Multimaps are the
associative containers like map that stores sorted key-value pair, but unlike maps which
store only unique keys, multimap can have duplicate keys. By default it uses <
operator to compare the keys.
For example: A multimap of Employees where employee age is the key and name is the
value can be represented as:
Keys Values
23 Nikita
28 Robin
25 Deep
25 Aman
Syntax
1. template < class Key, // multimap::key_type
2. class T, // multimap::mapped_type
3. class Compare = less<Key>, // multimap::key_compare
4. class Alloc = allocator<pair<const Key,T> > // multimap::allocator_type
5. > class multimap;
6. #include <iostream>
7. #include <map>
8. #include <string>
9. using namespace std;
10. int main()
11. {
12. multimap<string, string> m = {
13. {"India","New Delhi"},
14. {"India", "Hyderabad"},
15. {"United Kingdom", "London"},
16. {"United States", "Washington D.C"}
17. };
18. cout << "Size of map m: " << m.size() <<endl;
19. cout << "Elements in m: " << endl;
20. for (multimap<string, string>::iterator it = m.begin(); it != m.end(); ++it)
21. {
22. cout << " [" << (*it).first << ", " << (*it).second << "]" << endl;
23. }
24. return 0;
25. }
Output:
Size of map m: 4
Elements in m:
[India, New Delhi]
[India, Hyderabad]
[United Kingdom, London]
[United States, Washington D.C]
Operations of iterators :-
1. begin() :- This function is used to return the beginning position of the container.
2. end() :- This function is used to return the after end position of the container
3. advance() :- This function is used to increment the iterator position till the specified number
mentioned in its arguments.
4. next() :- This function returns the new iterator that the iterator would point after advancing
the positions mentioned in its arguments.
5. prev() :- This function returns the new iterator that the iterator would point after
decrementing the positions mentioned in its arguments.
6. inserter() :- This function is used to insert the elements at any position in the container. It
accepts 2 arguments, the container and iterator to position where the elements have to be
inserted.
#include<iostream>
#include<vector>
#include<iterator>
using namespace std;
int main()
{
vector<int> v{1,2,3,4,5};
vector<int>::iterator itr;
for(int i=0;i<5;i++) // Traversal without using an iterator.
{
cout<<v[i]<<" ";
}
cout<<'\n';
for(itr=v.begin();itr!=v.end();itr++) // Traversal by using an iterator.
{
cout<<*itr<<" ";
}
return 0;
}
O/p : 1 2 3 4 5
12345
Output:
The position of iterator after advancing is : 4
Iterator Categories
An iterator can be categorized in the following ways:
o Input Iterator
o Output Iterator
o Forward Iterator
o Bidirectional Iterator
Providers Of Iterators
Iterator categories Provider
Forward iterator
Advantages of iterator
Ease in programming: It is convenient to use iterators rather than using a subscript operator[] to access
the elements of a container
Disadvantages of iterator
o If we want to move from one data structure to another at the same time, iterators won't work.
o If we want to update the structure which is being iterated, an iterator won?t allow us to do
because of the way it stores the position.
o If we want to backtrack while processing through a list, the iterator will not work in this case.
STL algorithms
STL provide different types of algorithms that can be implemented upon any of the container with the
help of iterators
STL algorithms are built-in and thus save a lot of time and are more reliable too.
They also enhance code reusability. These algorithms are normally just one function call and we need not
write exhaustive code to implement them.
Thus now we don’t have to define complex algorithm instead we just use the built in functions provided
by the algorithm library in STL.
Algorithm functions provided by algorithm library works on the iterators, not on the containers.
1) find( )
find is a function defined inside <algorithm> header file that finds the element in the given range. It
returns an iterator to the first occurrence of the specified element in the given sequence. If the element
is not found, an iterator to the end is returned.
Syntax:
input_iterator find(input_iterator first, input_iterator last, const T& value);
Parameters:
Return Value :
If the value is found in the sequence, the iterator to its position is returned.
If the value is not found, the iterator to the last position is returned.
Program
#include <iostream>
using namespace std;
#include <bits/stdc++.h>
int main()
{
vector<int> v{10, 20, 30, 40};
vector<int>::iterator it; // Iterator used to store the
position of searched element
cout << "Original vector :"; // Print Original Vector
for (int i = 0; i < v.size(); i++)
cout << " " << v[i]<<endl;
int ser = 30; // Element to be searched
it = find(v.begin(),v.end(), ser); // std::find function call
if (it != v.end())
{
cout << "Element " << ser << " found at position : ";
cout << it - v.begin() << " (counting from zero) \n";
}
else
cout << "Element not found.\n\n";
return 0;
}
Output :
Original vector : 10 20 30 40
Element 30 found at position : 2
2) count( )
count() returns the number of occurrences of an element in a given range. Returns the number of
elements in the range [first, last) that compare equal to val.
If the val is not found at any occurrence then it returns 0(Integer value).
1) Counting occurrences in an array.
#include <iostream>
using namespace std;
#include <bits/stdc++.h>
int main() // C++ program for count in C++ STL for array
{
int a[] = { 3, 2, 1, 3, 3, 5, 3 };
cout << " Number of times 3 appears : "<< count(a, a + 7, 3);
return 0;
}
Output :
#include <iostream>
using namespace std;
#include <bits/stdc++.h>
int main()
{
vector<int> v{ 3, 2, 1, 3, 3, 5, 3 };
cout << "Number of times 3 appears : " << count(v.begin(),
v.end(), 3);
return 0;
}
Output :
#include <iostream>
using namespace std;
#include <bits/stdc++.h>
int main()
{
string s = "abcdeafghjand";
cout << "Number of times 'a' appears : "<< count(s.begin(),
s.end(), 'a');
return 0;
}
Output :
Number of times 'a' appears : 3
Time complexity: O(n) Here n is the length of string.
Auxiliary Space: O(1) As constant extra space is used.
3) sort( )
STL provides a similar function sort that sorts a vector or array (items with random access)
It generally takes two parameters, the first one being the point of the array/vector from where the
sorting needs to begin and the second parameter being the length up to which we want the
array/vector to get sorted. The third parameter is optional and can be used in cases such as if we
want to sort the elements lexicographically.
1)the sort() function sorts the elements in ascending order.
#include <iostream>
using namespace std;
#include <bits/stdc++.h>
int main()
{
int arr[] = { 1, 5, 8, 9, 6, 7, 3, 4, 2, 0 };
int n = sizeof(arr) / sizeof(arr[0]);
sort(arr, arr + n);
cout << "\nArray after sorting using default sort is : \n";
for (int i = 0; i < n; ++i) cout << arr[i] << " ";
return 0;
}
OutPut:
Array after sorting using default sort is :
0 1 2 3 4 5 6 7 8 9
Time Complexity: O(N log N)
Auxiliary Space: O(1)
2)the sort() function sorts the elements in descending order.
#include <iostream>
using namespace std;
#include <bits/stdc++.h>
int main()
{
int arr[] = { 1, 5, 8, 9, 6, 7, 3, 4, 2, 0 };
int n = sizeof(arr) / sizeof(arr[0]);
sort(arr, arr + n, greater<int>());
cout << "Array after sorting : \n";
for (int i = 0; i < n; ++i)
cout << arr[i] << " ";
return 0;
}
merge( )
C++ offers in its STL library a merge( ) which is quite useful to merge sort two containers into
a single container. It is defined in header “algorithm“.
It is implemented in two ways.
Syntax 1 : Using operator “<”
Syntax 2 : Using comparator function
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> arr1 = { 1, 4, 6, 3, 2 }; // initializing 1st container
vector<int> arr2 = { 6, 2, 5, 7, 1 };
vector<int> arr3(10); // declaring resultant container
merge(arr1.begin(), arr1.end(), arr2.begin(), arr2.end(), arr3.begin()); // using merge() to merge the
initial containers
for (int i = 0; i < arr3.size(); i++)
cout << arr3[i] << " ";
return 0;
}
Output: 1 4 6 3 2 6 2 5 7 1
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> arr1 = { 1, 4, 6, 3, 2 }; // initializing 1st container
vector<int> arr2 = { 6, 2, 5, 7, 1 };
vector<int> arr3(10); // declaring resultant container
sort(arr1.begin(), arr1.end()); // sorting initial containers
sort(arr2.begin(), arr2.end());
merge(arr1.begin(), arr1.end(), arr2.begin(), arr2.end(), arr3.begin()); // using merge() to merge the
initial containers
for (int i = 0; i < arr3.size(); i++)
cout << arr3[i] << " ";
return 0;
}
Output: 1 1 2 2 4 5 5 6 6 7
search( )
search is defined in the header file <algorithm> and used to find out the presence of a
subsequence satisfying a condition (equality if no such predicate is defined) with respect to
another sequence.
It can be used in either of the two versions, as depicted below :
1. For comparing elements using == :
2. For comparison based on a predicate (or condition) :
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int i, j;
vector<int> v1 = { 1, 2, 3, 4, 5, 6, 7 }; // Declaring the sequence to be searched into
vector<int> v2 = { 3, 4, 5 }; // Declaring the subsequence to be searched for
vector<int>::iterator i1; // Declaring an iterator for storing the returning pointer
i1 = search(v1.begin(), v1.end(), v2.begin(), v2.end()); // Using std::search and storing the result in
iterator i1
if (i1 != v1.end()) { // checking if iterator i1 contains end pointer of v1 or not
cout << "vector2 is present at index " << (i1 - v1.begin());
} else {
cout << "vector2 is not present in vector1";
}
return 0;
}
for_each( )
Apart from the generic looping techniques, such as “for, while and do-while”, C++ in its language also
allows us to use another functionality which solves the same purpose termed “for-each” loops. This
loop accepts a function which executes over each of the container elements. This loop is defined in the
header file “algorithm”: #include<algorithm>, and hence has to be included for successful operation of
this loop.
It is versatile, i.e. Can work with any container.
It reduces chances of errors one can commit using generic for loop
It makes code more readable
for_each loops improve overall performance of code
Syntax:
for_each (InputIterator start_iter,InputIterator last_iter,Function fnc)
#include <bits/stdc++.h>
using namespace std;
void printx2(int a) // helper function 1
{
cout << a * 2 << " ";
}
struct Class2 // helper function 2 object type function
{
void operator() (int a)
{
cout << a * 3 << " ";
}
} ob1;
int main()
{
int arr[5] = { 1, 5, 2, 4, 3 }; // initializing array
cout << "Using Arrays:" << endl;
cout << "Multiple of 2 of elements are : ";
for_each(arr, arr + 5, printx2); // printing array using for_each using function
cout << endl;
cout << "Multiple of 3 of elements are : "; // printing array using for_each using object function
for_each(arr, arr + 5, ob1);
cout << endl;
vector<int> arr1 = { 4, 5, 8, 3, 1 }; // initializing vector
cout << "Using Vectors:" << endl;
cout << "Multiple of 2 of elements are : "; // printing array using for_each using function
for_each(arr1.begin(), arr1.end(), printx2);
cout << endl;
cout << "Multiple of 3 of elements are : "; // printing array using for_each using object function
for_each(arr1.begin(), arr1.end(), ob1);
cout << endl;
return 0;
}
Output
Using Arrays:
Multiple of 2 of elements are : 2 10 4 8 6
Multiple of 3 of elements are : 3 15 6 12 9
Using Vectors:
Multiple of 2 of elements are : 8 10 16 6 2
Multiple of 3 of elements are : 12 15 24 9 3
transform( )
Consider the problem of adding contents of two arrays into a third array. It is given that all arrays
are of same size.
int main()
{
int arr1[] = {1, 2, 3};
int arr2[] = {4, 5, 6};
int n = sizeof(arr1)/sizeof(arr1[0]);
int res[n];
// Code to add two arrays
for (int i=0; i<n; i++)
res[i] = arr1[i] + arr2[i];
#include <bits/stdc++.h>
using namespace std;
int main()
{
int arr1[] = {1, 2, 3};
int arr2[] = {4, 5, 6};
int n = sizeof(arr1)/sizeof(arr1[0]);
int res[n];
// Single line code to add arr1[] and arr2[] and
// store result in res[]
transform(arr1, arr1+n, arr2, res, plus<int>());
for (int i=0; i<n; i++)
cout << res[i] << " ";
}
Output : 5 7 9