Introduction to STL: the Standard Template Library (STL) provides some very common data structures and algorithms
Standard Template Library (STL) defines a set of conceptual system and provides a logical basis for generic programming
The parameters of each class template and function template in STL are specified by the concepts in this system.
When using STL templates, type parameters can be either existing types in the C + + standard library or custom types - as long as these types are the model of the required concept.
The basic component of STL: container; iterator; function object; algorithms
Relationship between basic components of STL
Iterators are the bridge between algorithms and containers. The iterator is used as the parameter of the algorithm and the container is accessed through the iterator instead of directly using the container as the parameter of the algorithm.
Take the function object as the parameter of the algorithm, rather than the operation performed by the function as part of the algorithm.
Using iterators and function objects provided or customized in STL and STL algorithms, various functions can be combined.
The basic component of STL - container:
An object that holds and contains a set of elements.
Basic container class template: Sequence container: array, vector, deque, forward_list (single linked list), list (ordered)
Associated containers: set, multiset, map, multimap
Unordered Association containers: unordered set, unordered multiset, unordered map, unordered Multimap
Container adapter: stack, queue, priority_queue (priority queue)
To use a container, you need to include the corresponding header file
The basic component of STL - iterator:
Iterators are generalized pointers that provide a way to access each element in a container sequentially
Provides a method of sequentially accessing each element in the container;
You can use the "+ +" operator to get an iterator pointing to the next element;
You can use the "*" operator to access the element pointed to by an iterator. If the element type is a class or structure, you can also use the ">" operator to directly access a member of the element;
Some iterators also support obtaining an iterator pointing to the previous element through the "--" operator;
Iterators are generalized pointers: pointers have the same characteristics, so the pointer itself is an iterator;
To use iterators that are independent of STL containers, you need to include header files.
The basic component of STL - function object:
An object that behaves like a function and can be called like a function.
Function objects are generalized functions: any ordinary function and any class object overloaded with the "()" operator can be used as function objects
Function objects using STL need to contain header files
algorithms, the basic component of STL:
STL includes more than 70 algorithms, such as sorting algorithm, elimination algorithm, counting algorithm, comparison algorithm, transformation algorithm, replacement algorithm and container management
It can be widely used for different objects and built-in data types.
Using STL algorithm, you need to include header files.
#include "pch.h" #include <iostream> #include<vector> #include<iterator> #include<algorithm> #include<functional> using namespace std; int main(){ const int N = 5; vector<int> s(N); for (int i = 0; i < N; i++) cin >> s[i]; transform(s.begin(), s.end(), ostream_iterator<int>(cout, " "), negate<int>()); cout << endl; return 0; }
An implementation of transform algorithm:
template <class InputIterator, class OutputIterator, class UnaryFunction>
OutputIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryFunction op) {
for (;first != last; ++first, ++result)
*result = op(*first);
return result;
}
The transform algorithm traverses the elements pointed to by the first and last iterators in sequence;
Take the value of each element as the parameter of the function object op;
Output the return value of op in sequence through the iterator result;
After the traversal, the result iterator points to the next position of the last element of the output, and transform will return the iterator
iterator
Iterators are the bridge between algorithms and containers: iterators are used to access elements in containers; The algorithm does not directly manipulate the data in the container, but indirectly through the iterator
Algorithm and container independence: add a new algorithm without affecting the implementation of the container; Adding a new container, the original algorithm can also be applied
Input stream iterator and output stream iterator
Input stream iterator: Constructed with input stream (such as cin) as parameter; You can use * (p + +) to get the next input element
istream_iterator<T>
Output stream iterator: Output stream (such as cout) shall be provided during construction; Available (* p++) = x outputs x to the output stream
ostream_iterator<T>
Both belong to adapters: adapters are objects used to provide new interfaces for existing objects; The input stream adapter and the output stream adapter provide the interface of the iterator for the stream object
#include "pch.h" #include <iostream> #include<algorithm> #include<iterator> using namespace std; template<class T> T square(T x) { return x * x; } int main(){ //Read in several real numbers from standard input and output their squares respectively //template<class T> transform(istream_iterator<double>(cin), istream_iterator<double>(), ostream_iterator<double>(cout, "\t"), square<double>); cout << endl; return 0; }
Classification of iterators
Operations supported by iterators
Iterators are generalized pointers that provide pointer like operations (such as + +, *, - > operators)
Input iterator: can be used to read data from a sequence, such as an input stream iterator
Output iterator: allows writing data to a sequence, such as an output stream iterator
Forward iterator: it is both an input iterator and an output iterator, and can traverse the sequence unidirectionally
Bidirectional iterators: similar to forward iterators, but can traverse data in both directions
Random access iterator: it is also a bidirectional iterator, but it can jump between any two positions in the sequence, such as pointer and iterator obtained by using vector's begin() and end() functions
Iterator interval:
Two iterators represent an interval: [p1, p2)
STL algorithm often takes the interval of iterator as the input to transfer the input data
Legal interval: p1 meets p1 == p2 after n times (n > 0) auto increment (+ +) operation
The interval contains p1 but not p2
//Sort the n T-type values from the input iterator, and output the results through the output iterator result
template<class T,class InputIterator,class OutputIterator> void mySort(InputIterator first, InputIterator last, OutputIterator result) { //The input data is stored in the vector container s through the input iterator vector<T> s; for (; first != last; ++first) s.push_back(*first); //To sort s, the argument to the sort function must be a random access iterator sort(s.begin(), s.end()); copy(s.begin(), s.end(), result);//Output the s sequence through the output iterator } int main() { //Sort the contents of the s array and output double a[5] = { 1.2,2.4,0.9,3.3,3.2 }; mySort<double>(a, a + 5, ostream_iterator<double>(cout, " ")); cout << endl; //Read in several integers from the standard input and output the sorted results mySort<int>(istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>(cout, " ")); cout << endl; return 0; }
Results of operation:
0.9 1.2 2.4 3.2 3.3
2 -4 5 9 -1 3 6 -5
-5 -4 -1 2 3 5 6 9
Auxiliary function of iterator
advance(p, n): perform n auto increment operations on p
distance(first, last): calculate the distance between the two iterators first and last, that is, how many "+ +" operations can be performed on first to make first == last
Basic functions and classification of containers:
Container class is an object that holds and contains a group of elements or a collection of elements. It is based on the organization of elements in the container: sequential container and associated container. It is divided according to the iterator type associated with the container: reversible container and random access container
Sequential containers: array, vector, deque, forward_list (single linked list), list (list)
(ordered) association container: set, multiset, map, multimap
Unordered Association containers: unordered set (unordered set), unordered multiple set (unordered multiple set); Unordered map, unorder Multimap
Classification of containers
General functions of containers
The general function of container: construct an empty container with the defau lt constructor; Support relational operators: = =,! =, <, < =, >, > =;
begin(), end(): obtain the container's first and last iterators; clear(): empty the container; empty(): judge whether the container is empty; size(): get the number of container elements; s1.swap(s2): exchange the contents of s1 and s2 containers
Related data types (s indicates container type): S::iterator: iterator type pointing to container elements; S::const_iterator: constant iterator type
Access to reversible containers:
STL provides a reverse iterator for each reversible container. The reverse iterator can be obtained through the following member functions: rbegin(): the reverse iterator pointing to the end of the container; rend(): inverse iterator pointing to the container header
The type name of the inverse iterator is represented as follows: S::reverse_iterator: reverse iterator type; S: : converseiterator: inverse iterator type
Random access container: random access container supports random access to container elements: s[n]: get the nth element of container s
Characteristics of sequential containers:
Sequential containers: vectors, double ended queues, lists, one-way linked lists, and arrays
Vector
Features: an extensible dynamic array; Random access, inserting or deleting elements at the end; Inserting or deleting elements in the middle or head is slow
Vector capacity: the size of the actual allocated space; s.capacity(): returns the current capacity; s.reserve(n): if the capacity is less than N, expand s to make its capacity at least n
Dual ended queue (deque)
Features: insert or delete elements at both ends quickly; Random access is faster, but slower than vector containers; Inserting or deleting elements in the middle is slow
First output odd numbers in order from large to small, and then output even numbers in order from small to large.
#include "pch.h" #include <iostream> #include<deque> #include<vector> #include<algorithm> #include<iterator> using namespace std; int main(){ istream_iterator<int> i1(cin), i2;//Create a pair of input stream iterators vector<int> s1(i1, i2);//Input data from a standard input stream through an input stream iterator sort(s1.begin(), s1.end());//Sort the entered integers deque<int> s2; //The following loop traverses s1 for (vector<int>::iterator iter = s1.begin(); iter != s1.end(); ++iter) { if (*iter % 2 == 0)//Even number at the end of s2 s2.push_back(*iter); else s2.push_front(*iter); } //Output the result of s2 copy(s2.begin(), s2.end(), ostream_iterator<int>(cout, " ")); cout << endl; return 0; }
List
Features: insert and delete elements at any position quickly; Random access is not supported
Splice operation: s1.splice(p, s2, q1, q2): move [q1, q2) in s2 before the element pointed to by p in s1
#include "pch.h" #include <iostream> #include<string> #include<list> #include<iterator> using namespace std; int main(){ string names1[] = { "Alice","Helen","Lucy","Susan" }; string names2[] = { "Bob","David","Levin","Mike" }; //Construct the list s1 with the contents of the names1 array list<string>s1(names1, names1 + 4); //Construct the list s2 with the contents of the names2 array list<string>s2(names2, names2 + 4); //Put the first element of s1 at the end of s2 //Equivalent to s2.splice(s2.end(), s1, s1.begin(),++s1.begin()); s2.splice(s2.end(), s1, s1.begin()); //s2.end() points to the last element of s2 list<string>::iterator iter1 = s1.begin();//iter1 points to s1 header advance(iter1, 2);//iter1 advances two elements, which will point to s1 the third element list<string>::iterator iter2 = s2.begin();//iter2 points to s2 first ++iter2;//iter2 advances one element, which will point to s2 the second element list<string>::iterator iter3 = iter2;//Initialize iter3 with iter2 advance(iter3, 2);//iter3 advances two elements, which will point to the fourth element of s2 //Connect the nodes within the range of [iter2,iter3) to the node pointed to by iter1 in s1 s1.splice(iter1, s2, iter2, iter3); //Output s1 and s2 respectively copy(s1.begin(), s1.end(), ostream_iterator<string>(cout, " ")); cout << endl; copy(s2.begin(), s2.end(), ostream_iterator<string>(cout, " ")); cout << endl; return 0; }
Insert iterators for sequential containers:
An iterator that inserts elements into the head, tail, or middle of a container at a specified location
It includes frontinserter, backinserter and inserter at any position
list<int> s;
back_inserter iter(s);
*(iter++) = 5; / / insert 5 at the end of s through iter
Adapter for sequential container:
To build some common data structures based on sequential containers is to encapsulate sequential containers
Stack: the first pushed element is ejected
Queue: the first pushed element is ejected first
Priority_queue: the "largest" element is ejected first
Stack and queue templates
Stack template
template <class T, class Sequence = deque<T> > class stack;
Queue template
template <class T, class FrontInsertionSequence = deque<T> > class queue;
The stack can use any kind of sequential container as the base container, while the queue is only allowed to use the pre inserted sequential container (double ended queue or list)
Operations supported by stack and queue:
s1 op s2 op can be one of = =,! =, <, < =, >, > =. It compares the elements between the two container adapters in dictionary order
s.size() returns the number of elements of S
s.empty() returns whether s is empty
s.push(t) pushes element t into S
s.pop() pops an element from S. for the stack, the last pushed element is popped every time, while for the queue, the first pushed element is popped every time
Iterators are not supported because they do not allow access to any element
Stack and queue operations are different:
Stack operation: s.top() returns the reference of the top element of the stack
Queue operation: s.front() gets the reference of the queue head element; s.back() gets the reference of the queue tail element
Priority queue: like stack and queue, priority queue supports element push in and pop-up, but the order of element pop-up is related to the size of elements. Each pop-up is always the "largest" element in the container.
template <class T, class Sequence = vector<T> > class priority_queue;
The base container of the priority queue must be a sequential container that supports random access.
The size, empty, push and pop member functions of stack and queue are supported. The usage is the same as that of stack and queue.
Priority queues do not support comparison operations.
Similar to the stack, the priority queue provides a top function to obtain the reference of the next element to be ejected (i.e. the "largest" element).
#include "pch.h" #include <iostream> #include<queue> #include<time.h> #include<stdlib.h> using namespace std; const int SPLIT_TIME_MIN = 500;//Shortest time of cell division const int SPLIT_TIME_MAX = 2000;//Maximum cell division time class Cell; priority_queue<Cell> cellQueue; class Cell { //Cell class private: static int count; //Total number of cells int id; //Current cell number int time; //Cell division time public: Cell(int birth) :id(count++) {//Birth is the birth time of cells //Initialize and determine the cell division time time = birth + (rand() % (SPLIT_TIME_MAX - SPLIT_TIME_MIN)) + SPLIT_TIME_MIN; } int getId() const { return id; } //Get the cell number int getSplitTime() const { return time; } //Cell division time was obtained bool operator <(const Cell& s) const{ //Define "<" return time > s.time; } void split() { //cell division Cell child1(time), child2(time); //Two daughter cells were established cout << time << "s: Cell #" << id << " splits to #" << child1.getId() << " and #" << child2.getId() << endl; cellQueue.push(child1);//Push daughter cells into priority queue cellQueue.push(child2);//Push daughter cells into priority queue } }; int Cell::count = 0; int main(){ srand(static_cast<unsigned>(time(0))); int t;//Simulation time length cout << "Simulation time: "; cin >> t; cellQueue.push(Cell(0));//Push cells into priority queue while (cellQueue.top().getSplitTime() <= t) { Cell x = cellQueue.top(); x.split();//Simulate the division of the next cell cellQueue.pop();//Eject the newly divided cells } return 0; }