OOP UNIT 6
OOP UNIT 6
1. Containers
Containers in STL are classes or data structures that store collections of objects.
The most commonly used containers are:
a) Sequence Containers:
These containers store objects in a linear sequence.
vector: Dynamic array that can grow and shrink in size. Provides fast access
to elements using indices.
deque: Double-ended queue, supports fast insertion and deletion from both
ends.
list: Doubly linked list, allows fast insertions and deletions at any position.
array: Fixed-size array, size is determined at compile-time.
forward_list: Singly linked list, uses less memory compared to list.
b) Associative Containers:
These containers store elements in a sorted or structured order (key-value pairs).
set: A collection of unique elements, automatically sorted.
map: A collection of key-value pairs, where keys are unique and elements
are sorted based on keys.
multiset: Like a set, but allows duplicate values.
multimap: Like a map, but allows duplicate keys.
c) Unordered Containers:
These containers store elements without any specific order.
unordered_set: A set that does not guarantee any order.
unordered_map: A map that does not guarantee order.
unordered_multiset: Like unordered_set, but allows duplicate elements.
unordered_multimap: Like unordered_map, but allows duplicate keys.
d) Container Adaptors:
These are specialized containers that modify the behavior of other container types.
stack: Implements a last-in, first-out (LIFO) data structure.
queue: Implements a first-in, first-out (FIFO) data structure.
priority_queue: A queue where elements are retrieved in priority order (e.g.,
max or min).
2. Algorithms
STL provides a wide variety of built-in algorithms that can be applied to
containers. These algorithms work with iterators to perform operations like
searching, sorting, modifying, and more.
Common algorithms include:
sort(): Sorts elements in ascending or descending order.
find(): Finds the first occurrence of an element.
reverse(): Reverses the order of elements in a range.
accumulate(): Sums the elements in a range.
copy(): Copies elements from one container to another.
count(): Counts occurrences of a specific element in a range.
lower_bound() and upper_bound(): Used to find the first and last elements
that do not exceed a certain value in a sorted container.
The algorithms are implemented as functions that accept iterators to specify the
range of elements on which the algorithm will operate.
3. Iterators
Iterators are objects that allow access to elements in a container. They are similar
to pointers and provide a way to traverse the contents of a container. Iterators are
important because algorithms in STL work with iterators to operate on container
elements.
Types of Iterators:
input_iterator: Can read elements from a container, but can only move
forward.
output_iterator: Can write elements to a container, but can only move
forward.
forward_iterator: Can read and write elements and can move forward.
bidirectional_iterator: Can move both forward and backward through the
container.
random_access_iterator: Can move both forward and backward and
supports direct access to any element (like pointers).
Example of Iterators in Action:
Here’s a simple example of using iterators with a vector container:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// Using an iterator to traverse the vector
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " "; // Dereference the iterator to access the element
}
std::cout << std::endl;
return 0;
}
Iterator Operations:
begin(): Returns an iterator to the first element of the container.
end(): Returns an iterator to the position past the last element.
rbegin(): Returns a reverse iterator pointing to the last element.
rend(): Returns a reverse iterator pointing to the position before the first
element.
advance(): Moves the iterator by a specified number of steps.
distance(): Returns the number of steps between two iterators.
return 0;
}
Summary
Containers in STL allow you to store and manage data efficiently.
Algorithms in STL provide a range of operations that can be performed on
containers.
Iterators allow you to traverse containers in a uniform way, making the
algorithms reusable with different container types.
The STL makes C++ programming more efficient, as it provides generic and
reusable components that you can use in your applications. It encourages code
reusability and allows you to focus more on logic rather than the underlying data
structures.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iostream>: This header is included for input and output operations,
specifically to use std::cout for printing to the console.
#include <algorithm>: This header is needed to use algorithms such as std::sort and
std::find.
std::cout << *it << " ": Dereferences the iterator (*it) to access the value it points
to and prints it followed by a space.
After this loop, all the elements of the sorted vector are printed: 1 2 3 4 5 6 7 8 9.
std::find() will return an iterator pointing to the first occurrence of the element 5. If
the element is not found, it returns vec.end().
If the element is not found, it would be equal to vec.end(), and the message
"Element not found." will be printed.
6. Program Ending
return 0;
}
This line returns 0 to indicate that the program executed successfully. It's the
typical way of signaling the operating system that the program has finished.
Example Output
If you run the program, the output would look like this:
123456789
Element found: 5
First, the sorted vector is printed: 1 2 3 4 5 6 7 8 9.
Then, the program finds the element 5 and prints: Element found: 5.
Sequence
They provide ways to store data, access it, and perform various operations
efficiently. The containers in the STL are divided into two main categories:
1. Sequence Containers
2. Associative Containers
Let’s break them down in detail:
1. Sequence Containers
Sequence containers store elements in a linear sequence. They maintain the order
of insertion and allow random access to the elements. These containers allow
insertion, removal, and access to elements at any position in the sequence.
Common sequence containers include:
a) vector
A dynamic array that can grow and shrink in size as needed.
Provides fast access to elements using indices.
Insertion and deletion at the end of the vector are efficient (amortized
constant time).
Example Usage: Used when you need a dynamically sized array.
std::vector<int> vec = {1, 2, 3};
vec.push_back(4); // Adds 4 to the end of the vector
b) deque (Double-ended Queue)
A container that allows fast insertion and deletion at both the front and back
of the container.
Unlike vector, deque does not guarantee fast access for random elements but
is optimized for operations at both ends.
Example Usage: When you need efficient insertion/removal from both ends
(like a queue or stack).
std::deque<int> dq = {1, 2, 3};
dq.push_front(0); // Adds 0 to the front
dq.push_back(4); // Adds 4 to the back
c) list
A doubly linked list that allows fast insertion and deletion at both ends and
in the middle.
Elements are not stored in contiguous memory, so random access is slow
(linear time).
Example Usage: Useful when you need frequent insertion/deletion of
elements in the middle of the container.
std::list<int> lst = {1, 2, 3};
lst.push_back(4); // Adds 4 to the end
lst.push_front(0); // Adds 0 to the front
d) forward_list
A singly linked list similar to list but with less memory overhead.
Can only be traversed forward (no backward iteration).
Example Usage: Used when you need a lightweight list with forward-only
traversal.
std::forward_list<int> flst = {1, 2, 3};
flst.push_front(0); // Adds 0 to the front
e) array
A fixed-size container that stores elements contiguously.
The size of the array is specified at compile-time and cannot change.
Example Usage: When the size of the container is known at compile time
and doesn’t change.
std::array<int, 3> arr = {1, 2, 3}; // Array of size 3
2. Associative Containers
Associative containers store elements in a sorted or structured order and provide
fast access based on keys (for associative containers that hold key-value pairs).
These containers use a specific internal ordering (like a binary search tree) to
provide fast searching, insertion, and deletion.
Common associative containers include:
a) set
A collection of unique elements that are automatically sorted.
Elements in a set are stored in ascending order by default (using the <
operator).
Provides fast search, insertion, and deletion operations, all of which are
logarithmic in time complexity (O(log n)).
Example Usage: When you need a collection of unique elements without
duplicates.
std::set<int> s = {5, 3, 8, 1};
s.insert(4); // Inserts 4 into the set (automatically sorted)
b) map
A collection of key-value pairs where each key is unique.
The keys are automatically sorted.
Provides efficient lookup, insertion, and deletion by key.
Example Usage: Used when you want to associate a value with a specific
key, and each key must be unique.
std::map<int, std::string> m;
m[1] = "one"; // Insert key-value pair (1, "one")
m[2] = "two"; // Insert key-value pair (2, "two")
c) multiset
Similar to set, but allows duplicate elements.
Elements are automatically sorted, and duplicates are allowed.
Example Usage: Used when you need a collection of elements where
duplicates are allowed, and you still want sorted data.
std::multiset<int> ms = {1, 2, 2, 3, 4};
ms.insert(2); // Duplicates are allowed
d) multimap
Similar to map, but allows duplicate keys.
A collection of key-value pairs where multiple pairs can share the same key.
Example Usage: Used when you want to associate multiple values with the
same key.
std::multimap<int, std::string> mm;
mm.insert({1, "one"});
mm.insert({1, "uno"}); // Multiple values for key 1
mm.insert({2, "two"});
e) unordered_set
A collection of unique elements, but does not guarantee sorting.
Elements are stored in an unordered fashion, typically using a hash table.
Provides faster average-case performance for search, insertion, and deletion
(constant time O(1) on average).
Example Usage: When you don’t care about the order of elements and need
faster operations.
std::unordered_set<int> us = {5, 3, 8, 1};
us.insert(4); // No specific order
f) unordered_map
A collection of key-value pairs, where keys are unique, but the order of
keys is not maintained.
It uses a hash table internally, so lookup, insertion, and deletion are typically
O(1) on average.
Example Usage: When you need fast access to key-value pairs without
caring about order.
std::unordered_map<int, std::string> um;
um[1] = "one";
um[2] = "two"; // No specific order
g) unordered_multiset
Similar to unordered_set, but allows duplicates.
Uses a hash table for fast operations.
Example Usage: Used when you want a collection of elements without
order but still need to allow duplicates.
std::unordered_multiset<int> ums = {1, 2, 2, 3, 4};
ums.insert(2); // Duplicates allowed
h) unordered_multimap
Similar to unordered_map, but allows duplicate keys.
Stores key-value pairs in an unordered fashion with fast average-time
operations.
Example Usage: When you want multiple values for the same key and don’t
care about the order.
std::unordered_multimap<int, std::string> umm;
umm.insert({1, "one"});
umm.insert({1, "uno"}); // Multiple values for key 1
umm.insert({2, "two"});
Summary of Differences Between Sequence and Associative Containers
Feature Sequence Containers Associative Containers
int main() {
std::stack<int> st;
return 0;
}
Output:
Copy
321
int main() {
std::queue<int> q;
3. priority_queue
A priority_queue is a container adapter that always keeps its elements sorted in a
way that allows access to the highest priority element. By default, the highest
priority element is the largest element, but this can be customized.
Key Operations:
push(): Adds an element to the priority queue, maintaining the heap
property.
pop(): Removes the element with the highest priority.
top(): Accesses the element with the highest priority (the top element).
empty(): Checks whether the priority queue is empty.
size(): Returns the number of elements in the priority queue.
Underlying Container:
The priority_queue is typically implemented using a heap (binary heap),
which allows efficient access to the largest or smallest element.
By default, priority_queue uses a max-heap where the largest element has
the highest priority. You can customize the comparison function to create a
min-heap instead.
Example (Default Max-Heap):
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> pq;
int main() {
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
return 0;
}
Output:
Copy
5 10 15 20
In the second example, by specifying std::greater<int> as the comparison function,
we change the priority_queue to behave like a min-heap, where the smallest
element has the highest priority.
Conclusion
Container Adapters are a powerful feature in C++ that allow you to use specific
types of containers with simplified or restricted interfaces. They are built on top of
sequence containers and offer specialized functionalities:
stack for LIFO operations.
queue for FIFO operations.
priority_queue for prioritized element access.
int main() {
// A simple array of integers
int arr[] = {10, 20, 30, 40, 50};
int size = sizeof(arr) / sizeof(arr[0]); // Size of the array
int target = 30; // Element to search for
// Call the linearSearch function
int result = linearSearch(arr, size, target);
if (result != -1) {
cout << "Element " << target << " found at index " << result << endl;
} else {
cout << "Element " << target << " not found!" << endl;
}
return 0;
}
Explanation:
Array: The array arr[] contains the numbers {10, 20, 30, 40, 50}.
Target: We are searching for the number 30.
Linear Search: The linearSearch() function checks each element of the
array one by one. If it finds the element, it returns the index where the
element is found.
Result: If the element is found, it prints the index; otherwise, it says
"Element not found!"
Output:
pgsql
Copy
Element 30 found at index 2
Summary:
This is a simple example where we use Linear Search to search for the number 30
in the array {10, 20, 30, 40, 50}. It finds 30 at index 2.
Bubble Sort
Bubble sort is a sorting algorithm that compares two adjacent elements and swaps
them until they are in the intended order.
Just like the movement of air bubbles in the water that rise up to the surface, each
element of the array move to the end in each iteration. Therefore, it is called a
bubble sort.
Compare minimum with the third element. Again, if the third element is
smaller, then assign minimum to the third element otherwise do nothing. The
process goes on until the last element.
Compare minimum with the
remaining elements
3. After each iteration, minimum is placed in the front of the unsorted list.
The second
iteration The third
iteration The
fourth iteration
Insertion sort
Insertion sort is a sorting algorithm that places an unsorted element at its suitable
place in each iteration.
Insertion sort works similarly as we sort cards in our hand in a card game.
We assume that the first card is already sorted then, we select an unsorted card. If
the unsorted card is greater than the card in hand, it is placed on the right
otherwise, to the left. In the same way, other unsorted cards are taken and put in
their right place.
A similar approach is used by insertion sort.
Working of Insertion Sort
Suppose we need to sort the following array.
Initial array
1. The first element in the array is assumed to be sorted. Take the second
element and store it separately in key.
Compare key with the first element. If the first element is greater than key,
then key is placed in front of the first element.
2. If the first element is greater than key, then key is placed in front of the first
element.
3. Now, the first two elements are sorted.
Take the third element and compare it with the elements on the left of it.
Placed it just behind the element smaller than it. If there is no element
smaller than it, then place it at the beginning of the array.
int main() {
int arr[] = {3, 1, 9, 7, 5, 4, 8}; // Example array
int size = sizeof(arr) / sizeof(arr[0]); // Size of the array
int min, max;
// Call the findMinMax function to get the min and max values
findMinMax(arr, size, min, max);
return 0;
}
Explanation:
Input Array: The array arr[] = {3, 1, 9, 7, 5, 4, 8} is provided as input.
Min and Max Initialization: The algorithm initializes the min and max
variables to the first element of the array.
Traversal: It then traverses the rest of the array and updates the min and
max values as it finds smaller or larger elements.
Output: After the loop, the program prints the minimum and maximum
values found in the array.
Output:
yaml
Copy
Minimum value: 1
Maximum value: 9