Summary of standard template library knowledge points Summary of C++ programming and algorithm notes (8) Guo Wei, Peking University

insert image description here

Standard template library

https://blog.csdn.net/shaozheng0503/article/details/129101932?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168802585416800211563089%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168802585416800211563089&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-2-129101932-null-null.268v1koosearch&utm_term=stl&spm=1018.2226.3001.4450

Standard Template Library (Standard Template Library, STL) is an important part of C++, providing a rich set of data structure and algorithm containers. Containers, algorithms, and iterators in the STL are the three main components.

The following are several common components in STL:

  1. Containers: STL provides a variety of containers, including vectors, linked lists, deques, sets, and maps. These containers are similar to data structures and are used to store and manage data.

  2. Algorithms: STL provides a large number of algorithms, including operations such as sorting, searching, merging, and searching. These algorithms can be applied to a variety of different containers, making it very simple to operate on containers.

  3. Iterators: Iterators are a general way of traversing elements in a container. Iterators in the STL provide a unified interface that allows different types of containers to be accessed in the same way.

  4. Function Objects: Function objects are callable objects (such as function pointers or class objects that overload the function call operator), which are used to define custom operations in algorithms.

  5. Allocators: Allocators are used to manage memory allocation and release, and different allocators can be used in STL containers and algorithms.

The design goal of STL is to provide efficient, flexible, and general-purpose data structures and algorithms, so that C++ developers can program more conveniently. Using STL can greatly reduce development time and code complexity, and provides portability and reuse. In C++ development, using STL can greatly improve code quality and development efficiency.

generic programming

One of the core strengths of the C++ language is the ease of software reuse

There are two aspects of reuse in C++:

1. Object-oriented thinking: inheritance and polymorphism, standard class library
2. Generic programming thinking: template mechanism, and standard template library STL

Simply put, it is the programming method using templates.

Write some commonly used data structures (such as linked lists, arrays, binary trees) and algorithms (such as sorting, searching) as templates. In the future, no matter what objects are placed in the data structures or what objects the algorithms target, there is no need to reimplement the data Structure, rewrite the algorithm.

The Standard Template Library (Standard Template Library) is a collection of templates for some commonly used data structures and algorithms. With STL, there is no need to write most of the standard data structures and algorithms, and very high performance can be obtained.

Basic concepts in STL

Container: a general data structure that can accommodate various data types, which is a class template

Iterator: It can be used to sequentially access elements in the container, similar to pointers

Algorithms: function templates for manipulating elements in a container

 sort() to sort the data in a vector

 find() to search for objects in a list

The algorithms themselves are independent of the type of data they operate on, so they can be used on any data structure from simple arrays to highly complex containers.

int array[100];
该数组就是容器,而 int * 类型的指针变量就可以作为迭代器,sort算
法可以作用于该容器上,对其进行排序:
sort(array,array+70); //将前70个元素排序
迭代器

container overview

You're right, you mentioned some common containers and container adapters in the STL. Let me introduce them in detail:

  1. Sequence container:
  • Vector: A vector is a dynamic array capable of efficiently adding and removing elements from its end. It also supports random access, i.e. elements can be accessed directly by index.
  • Double-ended queue (deque): A deque is similar to a vector, but also allows efficient insertion and deletion at the head.
  • Linked list (list): A linked list is a doubly linked list that can perform efficient insertion and deletion operations at any position, but cannot directly access elements through indexes.
  1. Associative container:
  • Set (set): A set is a container that is ordered and does not contain duplicate elements. It supports efficient search, insertion and deletion operations.
  • Multiset: A multiset is similar to a set, but allows duplicate elements in the container.
  • Map (map): A map is a container of key-value pairs, each element has a unique key. It supports efficient search, insertion, and deletion operations by key.
  • Multimap: A multimap is similar to a map, but allows duplicate keys in the container.
  1. Container adapter:
  • Stack: A stack is a last-in-first-out (LIFO) container adapter. It is implemented based on a deque or list, which only allows insertion and deletion at the top.
  • Queue: A queue is a first-in-first-out (FIFO) container adapter. It is implemented based on a double-ended queue or list, which allows insertion at one end and deletion at the other end.
  • Priority queue (priority_queue): A priority queue is a heap-based container adapter that guarantees that the element with the highest priority in the queue is always at the front of the queue.

These containers and container adapters provide different data organization methods and operational characteristics, and the most suitable container can be selected to store and manage data according to needs. Each container has its unique advantages and applicable scenarios. Understanding their characteristics and usage methods will be very helpful for programming.

When an object is inserted into a container, a copy of the object is inserted. Many algorithms, such as sorting and searching, require the elements in the container to be compared, and some containers are themselves sorted. Therefore, the class of the object placed in the container should often overload the == and < operators.

iterator

 Used to point to elements in sequential containers and associative containers
The usage of iterators is similar to that of pointers element


Iterator is an important concept in STL, it is used to traverse the elements in the container and access them. Through iterators, we can operate on different types of containers in a unified way without caring about the implementation details of specific containers.

There are many types of iterators defined in STL, and each iterator has different characteristics and functions. The following are common iterator categories in STL:

  1. Input Iterator (Input Iterator): The input iterator is used to traverse the elements in the container from front to back, and supports reading the value of the element. The input iterator can only move in one direction, and does not support modifying the elements in the container.

  2. Output Iterator: The output iterator is used to traverse the elements in the container from front to back, and supports modifying the value of the element. The output iterator can only move in one direction, and does not support reading the value of the element.

  3. Forward Iterator (Forward Iterator): Forward iterators can be traversed and modified in one direction just like input iterators and output iterators. Unlike input iterators and output iterators, forward iterators support multiple traversals, that is, multiple iterations can be performed on the same container.

  4. Bidirectional Iterator: A bidirectional iterator is more powerful than a forward iterator, and it supports traversing elements in a container from front to back and from back to front. Bidirectional iterators can be incremented and decremented.

  5. Random Access Iterator (Random Access Iterator): Random Access Iterator is the most powerful iterator type, it has the functions of all other iterators, and supports random access to the elements in the container. Random access iterators can perform arithmetic operations like pointers, allowing us to access any element in the container in constant time.

Using iterators, we can iterate over the elements in a container by:

for (auto it = container.begin(); it != container.end(); ++it) {
    
    
    // 访问迭代器指向的元素
    // *it 可以获取当前迭代器指向的元素
}

It should be noted that different types of containers may provide different types of iterators. For example, vectors and deques provide random access iterators, while linked lists only provide bidirectional iterators. When writing code, we need to select the appropriate iterator type to operate according to the characteristics of the specific container.

Iterator is one of the core concepts of STL. Mastering the use of iterator can operate the elements in the container more flexibly and efficiently.
Moving on to your question about iterators, of course.

In STL, in addition to the common iterator types, there are some other types of iterators and related concepts:

  1. Reverse Iterator (Reverse Iterator): A reverse iterator is a special iterator that traverses the elements in a container in reverse order. The start and end positions of the reverse iterator can be obtained through the rbegin()and member functions.rend()
for (auto rit = container.rbegin(); rit != container.rend(); ++rit) {
    
    
    // 访问反向迭代器指向的元素
}
  1. Constant Iterator (Const Iterator): The constant iterator is used for read-only access to the elements in the container, and does not allow modification of the value of the element. The start and end positions of a constant iterator can be obtained through the cbegin()and member functions.cend()
for (auto cit = container.cbegin(); cit != container.cend(); ++cit) {
    
    
    // 访问常量迭代器指向的元素,只允许进行读取操作
}
  1. Insert Iterator (Insert Iterator): An Insert Iterator is a special iterator that can insert new elements into a container. Insertion iterators are std::inserter()created via functions.
std::vector<int> vec;
std::fill_n(std::inserter(vec, vec.begin()), 5, 0);
// 在vec的开始位置插入5个0
  1. Stream Iterator (Stream Iterator): The stream iterator is used to read and write the elements in the container through the input and output streams. Input and output stream iterators can be created via std::istream_iteratorand .std::ostream_iterator
// 从标准输入流中读取整数,并存储到容器中
std::vector<int> vec(std::istream_iterator<int>(std::cin), std::istream_iterator<int>());

// 将容器中的元素通过空格分隔输出到标准输出流
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));

These special types of iterators extend STL's ability to operate on containers, allowing us to use iterators more flexibly to traverse, modify, and insert elements. According to actual needs, choosing the appropriate iterator type can improve the readability and efficiency of the code.

Iterators are objects used to point to elements in sequential containers (such as vectors, lists, etc.) and associative containers (such as maps, sets, etc.). Its usage is very similar to pointers, and elements in the container can be accessed and manipulated through iterators.

There are two types of iterators: const iterators and non-const iterators. A const iterator can only read the element it points to, while a non-const iterator can modify the element it points to in addition to reading.

Using iterators, elements can be read and modified in the following ways:

  • Read element: *Get the value of the element pointed to by the iterator through the dereference operator. For example, *itget the element pointed to by the iterator it.

  • Modify elements: Get the reference of the element through the dereference operator *, and then modify the reference. For example, set the element pointed to by *it = valuethe iterator to .itvalue

Here is a sample code for reading and modifying using iterators:

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> vec = {
    
    1, 2, 3, 4, 5};

    // 使用非const迭代器读取和修改元素
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
    
    
        // 读取元素的值
        int value = *it;
        std::cout << value << " ";

        // 修改元素的值
        *it = value * 2;
    }
    std::cout << std::endl;

    // 输出修改后的元素
    for (const auto& num : vec) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

output:

1 2 3 4 5
2 4 6 8 10

Through iterators, we can flexibly read and modify the elements in the container, making it easier for us to manipulate the data in the container.

Algorithm Introduction

Your description of the STL algorithm is very accurate!

Algorithms are function templates, most defined in header files. STL provides algorithms that can be used in various containers, such as searching and sorting.

These algorithms operate on the elements of a container through iterators. Many algorithms operate on a local range in a container, and thus require two arguments: an iterator to the starting element and an iterator to the element after the ending element. This is the case, for example, with sorting algorithms and search algorithms.

Some algorithms return an iterator as a result. For example, find()algorithms are used to find an element in a container and return an iterator pointing to that element.

Not only can handle containers, STL algorithms can also handle ordinary arrays. Since arrays can also be accessed using iterators, you can think of a pointer to an array as an iterator over its first element, and a pointer to (end+1) as an iterator over its terminating element.

Here is a sample code that demonstrates how to use STL algorithms to find elements in containers and arrays:

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    
    
    std::vector<int> vec = {
    
    1, 2, 3, 4, 5};

    // 在容器中使用find算法查找元素
    auto it = std::find(vec.begin(), vec.end(), 3);
    if (it != vec.end()) {
    
    
        std::cout << "找到了元素3" << std::endl;
    } else {
    
    
        std::cout << "未找到元素3" << std::endl;
    }

    int arr[] = {
    
    1, 2, 3, 4, 5};

    // 在数组中使用find算法查找元素
    auto ptr = std::find(arr, arr + 5, 3);
    if (ptr != arr + 5) {
    
    
        std::cout << "找到了元素3" << std::endl;
    } else {
    
    
        std::cout << "未找到元素3" << std::endl;
    }

    return 0;
}

output:

找到了元素3
找到了元素3

Through the STL algorithm, we can efficiently perform various operations in containers and arrays, which greatly improves the reusability and readability of the code.

vector and deque

vectorand dequeare both containers in the C++ standard library for storing and managing elements. They have some things in common and some differences.

Same point:

  1. Dynamic size: vectorand dequeboth are dynamic arrays, which can dynamically adjust the size of the container as needed at runtime.
  2. Random access: Both support random access, which can directly access the elements in the container through the index.
  3. Iterator Support: Both provide iterators to iterate over the elements in the container.
  4. Support for tail insertion and deletion: vectorboth dequecan insert and delete elements at the tail, and have a complexity close to constant time.

difference:

  1. Memory allocation method: vectorUse continuous memory blocks to store elements, so they are closely arranged in memory. Instead, dequemultiple small blocks of contiguous memory space are used to store elements separately, and these small blocks are linked by pointers. This enables dequeefficient insertion and deletion operations on both ends.
  2. Space occupation: Due to vectorthe use of continuous memory blocks, in the case of the same number of elements, vectorthe occupied space is relatively small, but dequerelatively large, because it needs to maintain additional pointers to connect different blocks.
  3. Insertion and deletion in the middle: vectorInsertion and deletion operations in the middle position are relatively inefficient because subsequent elements need to be moved; dequeinsertion and deletion operations can be performed efficiently at any position.
  4. Iterator invalidation: Since vectora contiguous block of memory is used, adding or removing elements may invalidate existing iterators. And dequebecause of the way of pointer linking, iterators at other positions are still valid except when insertion and deletion operations occur at both ends of the container.

Choosing to use vectoror not dequedepends on specific needs. If you need to frequently perform insertion and deletion operations at the front or middle of the container, and you don't care about the invalidation of the iterator, you can choose deque. If you have higher requirements on space occupation and iterator stability, or only insert and delete at the end, you can choose vector.

No matter which container you choose, they all provide rich member functions and algorithm support, which can facilitate element access, insertion, deletion, sorting and other operations.

Doubly linked listlist

Doubly linked list ( list) is a container in the C++ standard library, which has some unique characteristics and uses compared with vectorand .deque

The following are listsome characteristics about doubly linked list:

  1. Structure: listIt is composed of a series of nodes, each node contains a value and pointers to the previous node and the next node. This structure enables insertion and deletion operations to have constant time complexity at any position.

  2. Insertion and deletion: Due to the node pointer of the doubly linked list, listinsertion and deletion operations at any position are very efficient, and do not involve element movement and memory reallocation.

  3. Access: Unlike vectorand deque, listfast access via random indexing is not supported as it does not have contiguous blocks of memory. To access listthe elements in the linked list needs to be traversed.

  4. Memory usage: Since each node requires additional pointers to connect the front and back nodes, the space overhead will be greater than that of and vector.dequelist

  5. Iterator Stability: Unlike vector, listthe insertion and deletion operations of the iterator will not invalidate the iterator, because the pointer of the node will not change. However, iterators are invalidated when dereferencing a node that is being deleted.

Due to the above characteristics, listit has certain advantages in certain scenarios:

  1. Frequent insertion and deletion operations: If you need to perform frequent insertion and deletion operations anywhere in the container, and do not require high random access performance, then it is lista good choice.

  2. listHigh iterator stability requirements: Iterator stability is an important consideration when valid references to elements need to be maintained after insertion and deletion operations .

It should be noted that since listthe linked list needs to be traversed when accessing elements, if frequent random access operations are required, or there is a high requirement for storage space, you may choose to use vectoror deque.

Of course, here is a listsimple code example in use that demonstrates how to insert, delete, and traverse elements in a linked list:

#include <iostream>
#include <list>

int main() {
    
    
    std::list<int> myList;  // 创建一个int类型的双向链表

    // 在链表尾部插入元素
    myList.push_back(10);
    myList.push_back(20);
    myList.push_back(30);

    // 在链表头部插入元素
    myList.push_front(5);

    // 遍历输出链表中的元素
    std::cout << "Elements in the list: ";
    for (const auto& element : myList) {
    
    
        std::cout << element << " ";
    }
    std::cout << std::endl;

    // 删除链表中的特定元素
    myList.remove(20);

    // 遍历输出删除后的链表元素
    std::cout << "Elements after removing 20: ";
    for (const auto& element : myList) {
    
    
        std::cout << element << " ";
    }
    std::cout << std::endl;

    return 0;
}

In the example above, first we included <iostream>the and <list>header files to use listthe container. We then created an object called myListand listused the push_back()and push_front()function to insert some elements into the linked list.

Next, we use a range-based for loop to iterate over and output the elements in the linked list. Note that listthis is a doubly linked list, so we can use forward iterators ( const auto& element) to access elements.

We then remove()removed a specific element (here 20) in the linked list using a function.

Finally, we traverse again and output the deleted linked list elements.

OK, I'll introduce you to some listspecific operations.

  1. Insert element:

    • push_back(value): Insert an element at the end of the linked list.
    • push_front(value): Insert an element at the head of the linked list.
    • insert(position, value): Insert an element at the specified position.
  2. Remove element:

    • pop_back(): Delete the element at the end of the linked list.
    • pop_front(): Delete the element at the head of the linked list.
    • erase(position): Delete the element at the specified position.
    • remove(value): Delete all elements in the linked list that are equal to the given value.
  3. access element:

    • front(): Returns a reference to the element at the head of the linked list.
    • back(): Returns a reference to the element at the end of the linked list.
  4. Iterate over elements:

    • Use a loop or a range-based for loop to iterate over the elements in the linked list, using an iterator to access each element.
  5. Empty the container:

    • clear(): Clear the entire linked list and delete all elements.
  6. Get the size:

    • size(): Returns the number of elements in the linked list.
    • empty(): Check if the linked list is empty.

It should be noted that since listit is a doubly linked list, they have constant time complexity for insertion and deletion operations at any position. For finding elements, since the access is based on traversal, the time complexity is linear.

function object

In C++, a function object (Function Object), also known as a functor (Functor), is an operator()object that overloads the function call operator. Function objects can be called like ordinary functions, and can have their own state and member variables.

operator()The main advantage of function objects is that they can be used like ordinary functions, and the behavior of the function can be customized by defining a function call operator in the class . This makes function objects very flexible and can be customized as needed.

Here is a simple example showing how to create and use function objects:

#include <iostream>

// 定义一个函数对象类
class MultiplyBy {
    
    
public:
    MultiplyBy(int factor) : factor_(factor) {
    
    
    }

    int operator()(int value) {
    
    
        return value * factor_;
    }

private:
    int factor_;
};

int main() {
    
    
    MultiplyBy multiplyByTwo(2);

    // 使用函数对象进行计算
    int result = multiplyByTwo(5);  // 等同于 multiplyByTwo.operator()(5)

    std::cout << "Result: " << result << std::endl;  // 输出结果: 10

    return 0;
}

In the example above, we defined a MultiplyByfunction object class called . It takes an integer as a parameter to the constructor and stores it as a member variable of the class factor_. Then, we overload the function call operator operator()to multiply the input value by factor_and return the result.

In mainthe function, we create a multiplyByTwofunction object called and set the constructor parameter to 2. Then, we use the function object to perform calculations, 5pass it as an argument, and get the result 10.

Function objects are very useful and can replace ordinary functions or function pointers in many cases, such as using custom operations in algorithms in the standard library.

Of course, here is a more complete code example demonstrating the use of function objects to customize the collation:

#include <iostream>
#include <algorithm>
#include <vector>

// 定义一个函数对象类
class CompareLength {
    
    
public:
    bool operator()(const std::string& str1, const std::string& str2) {
    
    
        return str1.length() < str2.length();
    }
};

int main() {
    
    
    std::vector<std::string> words = {
    
    "apple", "banana", "cherry", "date"};

    // 使用函数对象进行排序
    CompareLength compare;
    std::sort(words.begin(), words.end(), compare);

    // 输出排序结果
    for (const auto& word : words) {
    
    
        std::cout << word << " ";
    }
    std::cout << std::endl;

    return 0;
}

In the example above, we defined a CompareLengthfunction object class called . It overloads the function call operator operator()and accepts two strings as arguments. Inside the function object, we compare the lengths of the two strings and return the result of the comparison.

In mainthe function, we create a container std::vector<std::string>of type wordsthat holds some words. Then, we create a comparefunction object instance named .

Sort using std::sortalgorithm and comparefunction object . wordsThe algorithm will sort according to the sorting rules we defined, that is, the length of the string.

Finally, we use a loop to output the sorted results.

Execute the above code, the output will be the results sorted by string length:

date apple banana cherry

This example shows how to use function objects to customize the collation. This flexibility makes function objects useful in many scenarios, such as STL algorithms and custom operations on containers.

std::setandstd::multiset

std::setand std::multisetare both associative containers in the C++ standard library, used to store elements, and automatically sorted according to the value of the elements. The main difference between them is the uniqueness of the elements.

  • std::set: is a collection container in which the value of each element is unique. Even if duplicate elements are inserted, only one will remain. It is implemented using a red-black tree data structure with logarithmic time complexity for insert, lookup, and delete operations.

  • std::multiset: It is a multiple collection container that can store multiple elements of the same value. It allows insertion of duplicate elements and sorts according to the order of the values. Again, it is implemented using a red-black tree data structure with logarithmic time complexity for insertion, lookup, and deletion operations.

Here is a sample code showing how to use std::setand std::multiset:

#include <iostream>
#include <set>

int main() {
    
    
    std::set<int> setOfNumbers;
    setOfNumbers.insert(10);
    setOfNumbers.insert(30);
    setOfNumbers.insert(20);
    setOfNumbers.insert(40);

    std::cout << "std::set:" << std::endl;
    for (const auto& num : setOfNumbers) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    std::multiset<int> multisetOfNumbers;
    multisetOfNumbers.insert(10);
    multisetOfNumbers.insert(30);
    multisetOfNumbers.insert(20);
    multisetOfNumbers.insert(30);

    std::cout << "std::multiset:" << std::endl;
    for (const auto& num : multisetOfNumbers) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

In the example above, we first created a std::set<int>container of type setOfNumbersand inserted some integers. Since std::setthe elements in are unique, only one element will be kept if it is inserted repeatedly. Then, we use a loop to print out the elements in the collection.

Next, we create a std::multiset<int>container of type multisetOfNumbersand insert some integers. Since std::multisetallows to store duplicate elements, we insert two 30elements with value . Then, use the same loop to print out the elements in the multiset.

Executing the above code, the output will be:

std::set:
10 20 30 40
std::multiset:
10 20 30 30

This example shows the basic usage and difference of std::setand std::multiset. According to your needs, you can choose the suitable container type.

map and multimap

std::mapand std::multimapare associative containers in the C++ standard library, which are used to store key-value pairs and are automatically sorted according to the value of the key. The main difference between them is the uniqueness of the key.

  • std::map: is a map container where the value for each key is unique. Even if multiple elements with the same key are inserted, only one will remain, and subsequent insertions will overwrite previous values. It is implemented using a red-black tree data structure with logarithmic time complexity for insert, lookup, and delete operations.

  • std::multimap: is a multimap container that can store multiple elements with the same key. It allows insertion of multiple elements with the same key, sorted according to the order of the keys. Again, it is implemented using a red-black tree data structure with logarithmic time complexity for insertion, lookup, and deletion operations.

Here is a sample code showing how to use std::mapand std::multimap:

#include <iostream>
#include <map>

int main() {
    
    
    std::map<int, std::string> mapOfNumbers;
    mapOfNumbers.insert(std::make_pair(3, "Three"));
    mapOfNumbers.insert(std::make_pair(1, "One"));
    mapOfNumbers.insert(std::make_pair(2, "Two"));
    mapOfNumbers.insert(std::make_pair(4, "Four"));

    std::cout << "std::map:" << std::endl;
    for (const auto& pair : mapOfNumbers) {
    
    
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    std::multimap<int, std::string> multimapOfNumbers;
    multimapOfNumbers.insert(std::make_pair(3, "Three"));
    multimapOfNumbers.insert(std::make_pair(1, "One"));
    multimapOfNumbers.insert(std::make_pair(2, "Two"));
    multimapOfNumbers.insert(std::make_pair(3, "Another Three"));

    std::cout << "std::multimap:" << std::endl;
    for (const auto& pair : multimapOfNumbers) {
    
    
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

In the above example, we first created a std::map<int, std::string>container of type mapOfNumbersand inserted some key-value pairs. Since std::mapkeys in are unique, repeated insertions of elements with the same key will overwrite previous values. Then, we use a loop to print out the key-value pairs in the map.

Next, we create a std::multimap<int, std::string>container of type multimapOfNumbersand insert some key-value pairs. Since std::multimapallows storing multiple elements with the same key, we insert two 3key-value pairs with values ​​. Then, also use a loop to print out the key-value pairs in the multimap.

Executing the above code, the output will be:

std::map:
1: One
2: Two
3: Three
4: Four
std::multimap:
1: One
2: Two
3: Three
3: Another Three

This example shows the basic usage and difference of std::mapand std::multimap. According to your needs, you can choose the suitable container type.

container adapter

Container Adapters (Container Adapters) are special containers provided in the C++ standard library. They provide different interfaces and functions based on existing container types. Common container adapters include stack, queue, and priority_queue.

The underlying implementation of these container adapters can use a variety of different types of containers, such as std::deque, std::listor std::vector. Let's introduce the characteristics and usage of each container adapter in detail:

  1. stack (stack):

    • Elements are manipulated in a LIFO (Last In First Out) fashion.
    • Can use std::deque, std::listor std::vectoras the underlying container.
    • Provide push(), pop(), top()and other operation functions.
    • Sample code:
      #include <iostream>
      #include <stack>
      
      int main() {
              
              
          std::stack<int> stackOfNumbers;
      
          stackOfNumbers.push(1);
          stackOfNumbers.push(2);
          stackOfNumbers.push(3);
      
          while (!stackOfNumbers.empty()) {
              
              
              std::cout << stackOfNumbers.top() << " ";
              stackOfNumbers.pop();
          }
      
          return 0;
      }
      
  2. Queue (queue):

    • Elements are manipulated in a FIFO (first in, first out) fashion.
    • Can use std::dequeor std::listas the underlying container.
    • Provide push(), pop(), front(), back()and other operation functions.
    • Sample code:
      #include <iostream>
      #include <queue>
      
      int main() {
              
              
          std::queue<int> queueOfNumbers;
      
          queueOfNumbers.push(1);
          queueOfNumbers.push(2);
          queueOfNumbers.push(3);
      
          while (!queueOfNumbers.empty()) {
              
              
              std::cout << queueOfNumbers.front() << " ";
              queueOfNumbers.pop();
          }
      
          return 0;
      }
      
  3. Priority queue (priority_queue):

    • Similar to a queue, but the elements are ordered according to a certain priority.
    • By default, <the operator is used for comparison, and it is also possible to customize the comparison function.
    • Can use std::vectoror std::dequeas the underlying container.
    • Provide push(), pop(), top()and other operation functions.
    • Sample code:
      #include <iostream>
      #include <queue>
      
      int main() {
              
              
          std::priority_queue<int> pqOfNumbers;
      
          pqOfNumbers.push(3);
          pqOfNumbers.push(1);
          pqOfNumbers.push(2);
      
          while (!pqOfNumbers.empty()) {
              
              
              std::cout << pqOfNumbers.top() << " ";
              pqOfNumbers.pop();
          }
      
          return 0;
      }
      

These container adapters provide specific interfaces and behaviors for us to use in specific scenarios. You can select a suitable container adapter to implement corresponding functions according to your needs.

Algorithms in STL

The algorithms in STL can be roughly divided into the following seven categories:
1) Invariant sequence algorithm
2) Variable value algorithm
3) Deletion algorithm
4) Variable order algorithm
5) Sorting algorithm
6) Ordered interval algorithm
7) Numerical algorithm

Of course, I can provide you with some code samples to demonstrate different classes of STL algorithms. Here is some simple sample code:

  1. Non-modifying Sequence Algorithms:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    int main() {
          
          
        std::vector<int> numbers = {
          
          1, 2, 3, 4, 5};
    
        // 使用 std::find 查找元素
        auto it = std::find(numbers.begin(), numbers.end(), 3);
    
        if (it != numbers.end()) {
          
          
            std::cout << "Element found at index: " << std::distance(numbers.begin(), it) << std::endl;
        } else {
          
          
            std::cout << "Element not found" << std::endl;
        }
    
        return 0;
    }
    
  2. Mutating Algorithms:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    int main() {
          
          
        std::vector<int> numbers = {
          
          1, 2, 3, 4, 5};
    
        // 使用 std::fill 将容器中的元素填充为指定值
        std::fill(numbers.begin(), numbers.end(), 0);
    
        // 输出变化后的容器
        for (const auto& num : numbers) {
          
          
            std::cout << num << " ";
        }
        std::cout << std::endl;
    
        return 0;
    }
    
  3. Removing Algorithms:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    int main() {
          
          
        std::vector<int> numbers = {
          
          1, 2, 3, 4, 5};
    
        // 使用 std::remove_if 删除满足条件的元素
        numbers.erase(std::remove_if(numbers.begin(), numbers.end(), [](int num) {
          
          
            return num % 2 == 0; // 删除偶数
        }), numbers.end());
    
        // 输出变化后的容器
        for (const auto& num : numbers) {
          
          
            std::cout << num << " ";
        }
        std::cout << std::endl;
    
        return 0;
    }
    
  4. Partitioning Algorithms:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    int main() {
          
          
        std::vector<int> numbers = {
          
          1, 2, 3, 4, 5};
    
        // 使用 std::partition 将容器中的元素根据奇偶分为两部分
        auto partitionIt = std::partition(numbers.begin(), numbers.end(), [](int num) {
          
          
            return num % 2 != 0; // 奇数在前,偶数在后
        });
    
        // 输出变化后的容器
        for (const auto& num : numbers) {
          
          
            std::cout << num << " ";
        }
        std::cout << std::endl;
    
        return 0;
    }
    
  5. Sorting Algorithms:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    int main() {
          
          
        std::vector<int> numbers = {
          
          5, 2, 4, 1, 3};
    
        // 使用 std::sort 对容器进行排序
        std::sort(numbers.begin(), numbers.end());
    
        // 输出变化后的容器
        for (const auto& num : numbers) {
          
          
            std::cout << num << " ";
        }
        std::cout << std::endl;
    
        return 0;
    }
    

This is just a small sample code, you can choose the appropriate algorithm and use it in combination with specific container types according to your needs.
Of course, I can further explain to you what each sample code does and how to use it.

  1. Non-modifying Sequence Algorithms:
    This example uses std::findthe algorithm to find a specified element in a container. It takes three arguments: the starting iterator of the range to find, the ending iterator, and the value to find. Returns an iterator to the element if the value is found, or an end iterator if not found.

  2. Mutating Algorithms:
    This example uses std::fillthe algorithm to fill the entire container with the specified value. It takes three arguments: the start iterator of the range to fill, the end iterator, and the value to fill.

  3. Removing Algorithms:
    This example uses std::remove_ifthe algorithm to remove elements from a container that meet certain criteria. It accepts three parameters: the start iterator of the range of elements to be deleted, the end iterator, and a callable object (function or function object) used to determine which elements should be deleted. This algorithm first moves the elements satisfying the condition to the end of the container, and then returns an iterator pointing to the new logical end. eraseThese elements are finally removed from the container by using the container's member function.

  4. Partitioning Algorithms:
    This example uses std::partitionthe algorithm to partition the elements in a container according to specified conditions. It takes three arguments: the start iterator of the range of elements to reorder, the end iterator, and a callable that determines which elements should come first and which should follow. This algorithm rearranges the elements in the container so that the elements that satisfy the condition are at the front and the elements that do not meet the condition are at the back. The return value is an iterator pointing to the first element that does not satisfy the condition.

  5. Sorting Algorithms:
    This example uses std::sortthe algorithm to sort the elements in the container. It accepts two arguments: the start iterator and the end iterator of the range of elements to sort. It will sort the elements according to the default ascending sorting rules. After sorting, the elements in the container will be arranged in ascending order.

Guess you like

Origin blog.csdn.net/shaozheng0503/article/details/131460028