Common member functions in sequential containers are:
.begin() - Returns an iterator pointing to the start of the container
.end() - Returns an iterator pointing to the address next to the last element in the container
.empty() - Returns whether the container is empty
.clear() - Clear all contents present inside the container
.size() - Returns number of items present inside the container (absent in forward_list)
.max_size() - Returns theoretical maximum number of elements that the container can hold
.swap(other) - Swap content of current container with other container of same type
.resize(n) , resize(n,val) - Resize container to contain n elements. Can also pass default value val to set newly created elements as
The emplace member functions (like emplace_back, emplace_front, emplace_front, emplace etc.) perform insertions slightly faster than regular push or insert functions as they construct the new element in-place (instead of constructing the element and then copying it to that place). They take single list of variadic arguments required to construct the object. These are particularly useful for bulky composite types where copying is expensive.
Adaptors for Sequence Containers
They provide a wrapper interface for sequential containers. These are:
Supports O(1)insertion/deletion if location specified (only links get updated)
Operations at the start are O(1) and O(N) elsewhere.
Random access NOT present. Only the start i.e. head accessible
Uni-directional access to only forward adjacent element: next(pos)
Element Access: .front() to access first element in the list
Iterators: .before_begin()
Returns an iterator pointing to a placeholder element before the first element in the list
Trying to access it results in undefined behaviour
Incrementing this iterator gives .begin()
Generally used with .insert_after() , .emplace_after() , .erase_after() etc.
The list goes in only one direction i.e. forward. So, we can only do itr++ , but NOTitr-- (where itr is an iterator pointing to some element in the list)
Capacity: Note that .size() function is NOT provided
Modifiers:
.pop_front() - Remove first element from the list
.push_front(item) , .emplace_front(item) - Insert element item at start of the list
.insert_after() - Insertion takes O(1) per entry in these operations
.insert_after(pos,item) - Insert element item after the position pos
.insert_after(pos,count,item) - Insert count copies of item after position pos
.insert(pos,start,end) - Insert all elements within [start,end) after position pos
.emplace_after(pos,...args) - Construct element in-place after position pos
forward_list Demo
forward_list demo
voidprintList(constforward_list<int>&ll){
if(ll.empty()){
cout <<"List is empty\n";
return;
}
for(constint&num : ll) cout << num <<" -> ";
cout <<"\n";
}
intmain(){
forward_list<int> l1{3};
printList(l1); // 3 ->
forward_list<int>l2(3,2);
printList(l2); // 2 -> 2 -> 2 ->
forward_list<int> ll{7,1,3,5};
printList(ll); // 7 -> 1 -> 3 -> 5 ->
cout <<"First element: "<<ll.front()<<"\n"; // First element: 7
Unlike vector, the elements are not stored contiguously in memory.
Most deque implementations use a sequence of individually allocated fixed-size arrays, with additional bookkeeping. They can also expand/contract as needed on both ends. They also have a large minimal memory cost for allocating the individual arrays
Indexed access to deque must perform two pointer dereferences, compared to only one in indexed direct access to vector
Expansion is cheaper in deque than expansion of vector because it does not involve copying existing elements to new memory location
Note: The Adaptors for Sequence Containers i.e. stack, queue, heap are wrapper interfaces that enforce policies like LIFO/FIFO. They do NOT manage raw memory by themselves and offer fewer restricted operations (like push,pop) without any direct iterator access for traversal. The Adaptors do NOT support initializer list like the Sequence Containers
The stack adaptor provides a Stack data structure wrapper to the container, which has the property of Last-in First-out i.e. LIFO (also called first-in last-out)
Supports operations ONLY at the top of Stack, which take O(1) time each
It’s defined as shown below. The underlying Container can be deque (default) or vector or list
template<
classT,
class Container = deque<T>
>classstack;
Declaration: stack<Type> stk;
Element Access: .top() returns reference to first i.e. top-most element
Capacity: .size() , .empty()
Iterators: NONE provided
Modifiers:
.pop() - Remove element present at the top
.push(val) - Insert element at the top
.emplace(...args) - Construct element in-place at the top
The queue adaptor provides a Queue data structure wrapper to the container, which has the property of First-in First-out i.e. FIFO (also called last-in last-out)
Insertion occurs at the back and Deletion at the front, both taking O(1) time
It’s defined as shown below. The underlying Container can be deque (default) or list but NOT vector as it doesn’t support quick O(1) deletion at front
template<
classT,
class Container = deque<T>
>classqueue;
Declaration: queue<Type> q;
Element Access:
.front() - returns reference to first i.e. starting element
.back() - returns reference to last i.e. ending element
Capacity: .size() , .empty()
Iterators: NONE provided
Modifiers:
.pop() - Remove the first i.e. starting element
.push(val) - Insert element at the end
.emplace(...args) - Construct element in-place at the end
The priority_queue adaptor provides a Heap data structure wrapper to the container
The default ordering is to keep the lexicographically larger at the top via less<T> comparator i.e. a Max-heap. You can also provide a custom Compare function to determine the ordering
Priority Queue supports fast lookup of highest-priority element in O(1) time
Insertion/Deletions take up to O(logN) time
Priority Queue allows inserting duplicates, but there is no guarantee of stable order i.e. strict FIFO between the duplicates
It’s defined as shown below. The underlying Container can be either vector (default) or deque
template<
classT,
class Container = vector<T>,
class Compare = less<typenameContainer::value_type>
>classpriority_queue;
The adaptor internally uses the Heap operation algorithms like make_heap(), push_heap(), pop_heap() defined in <algorithm> header over the Container elements
Even though both vector and deque support O(1) random access, the fast direct access in vector allows for more efficient pointer arithmetic calculations to calculate child and parent indices as compared to the two-step dereferencing in deque. Thereby,
printHeapInfo(minDqHp); // Top element in Heap: 1, Size: 4
}
priority_queue with Custom Data-type
We create a custom Comparator struct with () operator overload to compare two objects of our custom data-type. We could also just overload the < operator within Employee struct to define comparison of two Employee objects but that is not generally extensible to more complex types and operations
priority_queue with Custom Data-type and Comparator