What is std::list?
At its core, std::list is a doubly linked list container in C++'s STL. Unlike array-based containers such as std::vector or std::deque which utilizes contiguous memory storage, std::list organizes elements as nodes linked together through pointers. Each node in the list contains both a data element and pointers to the previous and next nodes, facilitating efficient traversal and manipulation of the list.
Characteristics
- Doubly Linked List Structure:
std::list
is implemented as a doubly linked list, where each element (node) contains pointers to both the previous and next elements in the sequence.- This doubly linked structure allows for efficient traversal in both forward and backward directions.
- Dynamic Memory Allocation:
std::list
dynamically allocates memory for each node as elements are added to the list.- This dynamic memory allocation enables
std::list
to grow or shrink dynamically, without the need for contiguous memory allocation.
- Efficient Insertion and Deletion:
- Insertion and deletion operations at any position within the list have a constant-time complexity O(1).
- These operations involve updating pointers to link or unlink nodes, which can be done efficiently regardless of the list's size.
- Bidirectional Iteration:
std::list
supports bidirectional iteration, allowing traversal of elements both forwards and backwards.- Iterators provide efficient means to navigate through the elements of the list in either direction.
- No Random Access:
- Unlike array-based containers such as
std::vector
,std::list
does not support random access to elements. - Accessing elements by index or position requires sequential traversal from the beginning or end of the list.
- Unlike array-based containers such as
- Memory Efficiency:
std::list
offers efficient memory utilization, as it only requires memory for the data elements and the pointers to the previous and next nodes.- Each node in the list consumes memory for the data element and two pointer variables, resulting in a memory-efficient data structure.
- No Contiguous Memory Allocation:
- Unlike array-based containers that store elements in contiguous memory locations,
std::list
does not require contiguous memory allocation. - This characteristic allows for efficient insertion and deletion operations without the overhead of reallocation or shifting of elements.
- Unlike array-based containers that store elements in contiguous memory locations,
Syntax
#include <list> // Include the list header
int main() {
// Declaration and Initialization
std::list<DataType> myList; // Create an empty list
std::list<DataType> myList = {val1, val2, val3}; // Create and initialize with values
// Iterators
myList.begin(); // Iterator pointing to the first element
myList.end(); // Iterator pointing to one past the last element
// Capacity
myList.empty(); // Returns true if the list is empty, false otherwise
myList.size(); // Returns the number of elements in the list
// Modifiers
myList.push_front(value); // Insert element at the beginning
myList.pop_front(); // Remove the first element
myList.push_back(value); // Insert element at the end
myList.pop_back(); // Remove the last element
myList.insert(iterator, value); // Insert element at the specified position
myList.erase(iterator); // Remove element at the specified position
myList.clear(); // Remove all elements
// Element Access
myList.front(); // Access the first element
myList.back(); // Access the last element
// Iteration
for (auto it = myList.begin(); it != myList.end(); ++it) {
// Access each element using the iterator 'it'
}
return 0;
}
Functions
1 push_front()
It is used to insert an element at the beginning of the list.
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {2, 3, 4};
// Inserting an element at the beginning
myList.push_front(1);
// Displaying elements
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
Complexity: The time complexity of push_front()
is constant O(1)
because it involves inserting the element at the beginning of the list, which can be done in constant time regardless of the list's size.
2 pop_front()
It is used to remove the first element from the list.
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 4};
// Removing the first element
myList.pop_front();
// Displaying elements
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
Complexity:
- The time complexity is constant
O(1)
because it involves removing the first element of the list, which can be done in constant time regardless of the list's size.
3 push_back()
It is used to insert an element at the end of the list.
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3};
// Inserting an element at the end
myList.push_back(4);
// Displaying elements
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
Complexity:
- The time complexity is constant
O(1)
because it involves inserting the element at the end of the list, which can be done in constant time regardless of the list's size.
4 pop_back()
It is used to remove the last element from the list.
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 4};
// Removing the last element
myList.pop_back();
// Displaying elements
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
Complexity:
- The time complexity is constant
O(1)
because it involves removing the last element of the list, which can be done in constant time regardless of the list's size.
5 insert()
It is used to insert an element at a specified position in the list.
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 4};
// Inserting an element at the second position
auto it = myList.begin();
std::advance(it, 1); // Move iterator to the desired position
myList.insert(it, 3);
// Displaying elements
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
Complexity:
The time complexity is linear O(N)
in the worst case because it involves traversing the list to reach the specified position. However, for inserting at the beginning or end (with iterators), it's constant O(1)
.
6 erase()
It is used to remove an element from a specified position in the list.
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 4};
// Removing the second element
auto it = myList.begin();
std::advance(it, 1); // Move iterator to the desired position
myList.erase(it);
// Displaying elements
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
Complexity:
- The time complexity is constant
O(1)
because it involves removing the element at the specified position, which can be done in constant time regardless of the list's size.