STLs

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

STLs

Let's go through the pros and cons of each of these data structures:

1. Vector:

Pros:

Fast access time for elements using indexing.

Dynamic sizing, allowing for easy resizing.

Contiguous memory allocation, which can improve cache performance.

Cons:

Costly insertions or deletions in the middle, as elements may need to be shifted.

Resizing can be expensive when the vector grows beyond its capacity.

2. Deque (Double-ended Queue):

Pros:

Fast O(1) insertions and deletions at both ends.

Dynamic sizing with efficient memory management.

Cons:

Slightly more memory overhead compared to vectors.

Slower access time at non-ends compared to vectors.

3. List:

Pros:

Fast insertions and deletions anywhere in the list.

Minimal memory overhead for elements.

Cons:

Slower access times due to the need to traverse the list.

Non-contiguous memory allocation.

4. Forward List:

Pros:

Same advantages as a regular list.

Cons:

Lack of bidirectional traversal, unlike the regular list.

STLs 1
5. Pair:

Pros:

Convenient for storing two related values together.

Cons:

Limited to two elements, not suitable for more complex data structures.

6. Set:

Pros:

Provides unique elements.

Offers efficient lookup and insertion operations.

Cons:

Elements are usually ordered, which may not be desired in all cases.

7. Map:

Pros:

Associates values with unique keys.

Efficient lookup and insertion operations.

Cons:

Elements are usually ordered, which may not be desired in all cases.

8. Unordered Set & Unordered Multiset:

Pros:

Fast lookup and insertion, usually with constant time complexity.

No requirement for elements to be ordered.

Cons:

Lack of ordering can be a drawback in some scenarios.

9. Unordered Map & Unordered Multimap:

Pros:

Fast lookup and insertion, usually with constant time complexity.

No requirement for elements to be ordered.

Cons:

Lack of ordering can be a drawback in some scenarios.

10. Stack:

STLs 2
Pros:

Provides Last-In-First-Out (LIFO) behavior.

Efficient push and pop operations.

Cons:

Limited functionality compared to other data structures.

11. Queue:

Pros:

Provides First-In-First-Out (FIFO) behavior.

Efficient enqueue and dequeue operations.

Cons:

Limited functionality compared to other data structures.

12. Priority Queue:

Pros:

Elements are stored in a way that allows retrieval of the highest-priority element efficiently.

Useful for applications where elements have different priorities.

Cons:

Limited functionality, not suitable for all scenarios.

Slower insertion compared to other data structures like heaps.

Here's a summary of the time complexities for common operations on these data structures

1. Vector:

Access (by index): O(1)

Insertion/Deletion (end): O(1) amortized, O(n) when resizing is required

Insertion/Deletion (middle): O(n)

Search: O(n)

#include <vector>
std::vector<int> myVector = {1, 2, 3, 4, 5};
myVector.push_back(6); // Add element to the end (Amortized O(1))
myVector.pop_back(); // Remove element from the end (O(1))
int thirdElement = myVector[2]; // Access element by index (O(1))

2. Deque:

Access (by index): O(1)

STLs 3
Insertion/Deletion (beginning/end): O(1)

Insertion/Deletion (middle): O(n)

Search: O(n)

#include <deque>
std::deque<int> myDeque = {1, 2, 3, 4, 5};
myDeque.push_back(6); // Add element to the end (Amortized O(1))
myDeque.push_front(0); // Add element to the beginning (Amortized O(1))
int firstElement = myDeque.front(); // Access the first element (O(1))

3. List:

Access (by index): O(n)

Insertion/Deletion (anywhere): O(1) with a reference, O(n) without

Search: O(n)

#include <list>
std::list<int> myList = {1, 2, 3, 4, 5};
myList.push_back(6); // Add element to the end (O(1))
myList.insert(++myList.begin(), 10); // Insert 10 after the first element (O(1))
myList.remove(3); // Remove all occurrences of 3 (O(n))

4. Forward List:

Access (by index): Not supported

Insertion/Deletion (anywhere): O(1) with a reference, O(n) without

Search: O(n)

#include <forward_list>
std::forward_list<int> myForwardList = {1, 2, 3, 4, 5};
myForwardList.push_front(0); // Add element to the beginning (O(1))
myForwardList.insert_after(myForwardList.begin(), 10); // Insert 10 after the first element (O(1))

5. Pair:

Access: O(1)

#include <utility>
std::pair<std::string, int> myPair = std::make_pair("Alice", 25);
std::string name = myPair.first; // Access first element of the pair (O(1))
int age = myPair.second; // Access second element of the pair (O(1))

6. Set:

STLs 4
Insertion/Deletion/Lookup: O(log n) on average for ordered sets (e.g., std::set in C++), O(1) on
average for unordered sets (e.g., std::unordered_set in C++ with a good hash function).

#include <set>
std::set<int> mySet = {3, 1, 4, 1, 5, 9, 2, 6, 5};
mySet.insert(7); // Add unique elements (O(log n))
mySet.erase(4); // Remove element (O(log n))
bool containsFive = (mySet.find(5) != mySet.end()); // Check if an element exists (O(log n))

7. Map:

Insertion/Deletion/Lookup: O(log n) on average for ordered maps (e.g., std::map in C++), O(1) on
average for unordered maps (e.g., std::unordered_map in C++ with a good hash function).

#include <map>
std::map<std::string, int> myMap;
myMap["Alice"] = 25; // Associate values with keys (O(log n))
myMap["Bob"] = 30;
int aliceAge = myMap["Alice"]; // Retrieve value by key (O(log n))

8. Unordered Set & Unordered Multiset:

Insertion/Deletion/Lookup: O(1) on average with a good hash function.

#include <unordered_set>
std::unordered_set<int> myHashSet = {3, 1, 4, 1, 5, 9, 2, 6, 5};
myHashSet.insert(7); // Add unique elements (Amortized O(1))
myHashSet.erase(4); // Remove element (Amortized O(1))
bool containsFive = (myHashSet.find(5) != myHashSet.end()); // Check if an element exists (Amortized O(1))

9. Unordered Map & Unordered Multimap:

Insertion/Deletion/Lookup: O(1) on average with a good hash function.

#include <unordered_map>
std::unordered_map<std::string, int> myHashMap;
myHashMap["Alice"] = 25; // Associate values with keys (Amortized O(1))
myHashMap["Bob"] = 30;
int aliceAge = myHashMap["Alice"]; // Retrieve value by key (Amortized O(1))

10. Stack:

Push/Pop: O(1)

Access (by index): Not supported

#include <stack>
std::stack<int> myStack;
myStack.push(1); // Push elements onto the stack (O(1))
myStack.push(2);

STLs 5
int topElement = myStack.top(); // Access the top element (O(1))
myStack.pop(); // Pop the top element (O(1))

11. Queue:

Enqueue/Dequeue: O(1)

Access (by index): Not supported

#include <queue>
std::queue<int> myQueue;
myQueue.push(1); // Enqueue elements (O(1))
myQueue.push(2);
int frontElement = myQueue.front(); // Access the front element (O(1))
myQueue.pop(); // Dequeue the front element (O(1))

12. Priority Queue:

Insertion: O(log n)

Extraction of highest-priority element: O(log n)

Access (by index): Not supported

#include <queue>
std::queue<int> myQueue;
myQueue.push(1); // Enqueue elements (O(1))
myQueue.push(2);
int frontElement = myQueue.front(); // Access the front element (O(1))
myQueue.pop(); // Dequeue the front element (O(1))

Keep in mind that these time complexities are generalized and can vary depending on the specific
implementation and language you are using. For example, the actual performance of hash-based data
structures like unordered sets and maps can be affected by the quality of the hash function and the load
factor. Additionally, some operations, like sorting, may not be mentioned in the above summaries but are
possible with some of these data structures.

Amortized time complexity

Amortized time complexity is a way of analyzing the time cost of an algorithm or a data structure over
a sequence of operations. It considers the average time per operation over a series of operations
rather than focusing on the worst-case scenario for each operation.

In the context of data structures like vectors, deques, and unordered sets/maps, amortized time
complexity takes into account the cost of resizing or rehashing, which may be necessary to maintain
the data structure's properties or capacity. These resizing or rehashing operations can be relatively
expensive, but they don't occur with every insert or delete operation.

For example, in a vector, if the capacity is exceeded, a resizing operation may double the vector's
size. While this resizing operation has a time complexity of O(n), it doesn't happen with every
element insertion. Instead, it occurs periodically, and the cost is spread out over multiple insertions.

STLs 6
On average, the cost per insertion is much lower than O(n). This average cost per operation is what's
referred to as the amortized time complexity.

So, when we say that an operation on a data structure like a vector has an "amortized time
complexity of O(1)," we mean that, on average, the operation is relatively cheap, even though some
individual operations might be more expensive. It provides a more realistic view of the overall
performance of the data structure when used over a sequence of operations.

STLs 7

You might also like