A brief introduction to each container of C++ STL

1. What is STL?

1. STL (Standard Template Library), the standard template library, is an efficient C++ program library that contains many commonly used basic data structures and basic algorithms. It provides a scalable application framework for the majority of C++ programmers, which highly embodies the reusability of the software.

2. From the logical level, the idea of ​​generic programming is embodied in STL . In this idea, most of the basic algorithms are abstracted and generalized, independent of the corresponding data structures, and used to deal with various situations in the same or similar way.

3. From the perspective of implementation level, the entire STL is implemented in a type parameterized way, based on templates .

STL has six major components, but mainly includes three parts: container, iterator and algorithm.

  • Containers : used to manage a collection of objects of a certain type. Each container has its advantages and disadvantages, so in order to meet the different needs of the program, STL has prepared seven basic container types.
  • Iterators (Iterators): Used to perform traversal actions on the elements of an object collection. This collection of objects may be a container, or it may be part of a container. Each kind of container provides its own iterators, and these iterators know the internal structure of that kind of container.
  • Algorithms (Algorithms): used to process the elements in the object collection, such as Sort, Search, Copy, Erase those elements. With the help of iterators, we only need to write an algorithm once and apply it to any container, because the iterators of all containers provide a consistent interface.

The basic concept of STL is to separate data and operations. The data is managed by the container, the operation is performed by the algorithm, and the iterator acts as the glue between the two, so that any algorithm can interact with any container.

2. Containers

Containers are used to manage certain types of objects. In order to meet the different needs in the program, STL has prepared two types of seven basic container types:

  • Sequence containers, which are orderable clusters in which each element has a fixed position—depending on when and where it is inserted, independent of element value. If you append six elements to a cluster, they will be in the same order as they were inserted. STL provides three serial containers: vector (vector), double-ended queue (deque), and list (list). In addition, you can also use string and array as a serial container.
  • Associative containers (Associative containers), this is an ordered cluster, the element position depends on a specific sorting criterion and element value, and has nothing to do with the insertion order. If you put six elements into such a cluster, their positions depend on the element values, not on the order of insertion. STL provides four associative containers: collection (set), multiple collections (multiset), mapping (map) and multiple mapping (multimap).

The schematic diagram is shown in the figure below:

2.1 vector

Vector (vector): It is a serial container, which is similar to an array in fact, but it is superior to an array. Generally speaking, the array cannot be expanded dynamically, so when the program is running, it either wastes memory or causes out-of-bounds. The vector just makes up for this defect. When the memory space is not enough, it is necessary to re-apply for a large enough memory and copy the memory.

features

  • It has a continuous memory space, so it can support random access very well, that is, the [] operator and .at(), and the random access is fast. (advantage)
  • When inserting or deleting elements to its head or middle, in order to maintain the original relative order, all elements after the insertion or deletion point must be moved, so the efficiency of insertion or deletion is relatively low. (shortcoming)
  • It is fastest to insert and delete elements later, and generally there is no need to move memory at this time. (advantage)
  • Summary: Equivalent to an expandable array (dynamic array), random access is fast, insertion or deletion at the head and middle is inefficient, but insertion or deletion at the end is efficient.

Applicable scene

It is suitable for scenes with simple objects, small changes, and frequent random access.

example

The following example defines a vector of integers, inserts 6 elements, and prints all elements:

#include <iostream>
#include <vector>

  C++资料领取进裙:580475042
  
using namespace std;

int main(int argc, char* argv[])
{
	vector<int> vecTemp;

	for (int i = 0; i<6; i++)
		vecTemp.push_back(i);

	for (int i = 0; i<vecTemp.size(); i++)
		cout << vecTemp[i] <<" "; // 输出:0 1 2 3 4 5

	return 0;
}

2.2 and

Deque (double-ended queue) is a continuous memory space with two-way openings (dynamically joining multiple continuous spaces together through pointer arrays), and a new space can be added at any time. The biggest task of deque is to maintain the illusion of overall continuity in these segmented continuous spaces and provide a random access interface.

features

  • Once a new space is to be added at the head and tail of the deque, a certain amount of continuous space is configured and strung at the head or tail of the entire deque, so inserting elements at the head or tail is very fast. (advantage)
  • Placing elements in the middle is more time-consuming because other elements have to be moved. (shortcoming)
  • deque is a compromise between list and vector. It has both the advantages of list and the advantages of high random access efficiency of vector.
  • Summary: Random access is supported, but the efficiency is not as high as vector. The insertion or deletion at the head and tail is efficient, but the insertion or deletion in the middle is inefficient.

Applicable scene

It is suitable for scenarios that require frequent random access and care about the insertion and deletion of data at both ends.

example

The following example declares a deque of floating point type, inserts 6 elements at the end of the container, and finally prints out all elements.

#include <iostream>
#include <deque>

using namespace std;

int main(int argc, char* argv[])
{
	deque<float> dequeTemp;

	for (int i = 0; i<6; i++)
		dequeTemp.push_back(i);

	for (int i = 0; i<dequeTemp.size(); i++)
		cout << dequeTemp[i] << " "; // 输出:0 1 2 3 4 5

	return 0;
}

2.3 list

List is implemented by a doubly linked list, elements are stored in the heap, and each element is placed in a piece of memory. There is no space reservation habit, so each allocation of an element will be allocated from memory, and each deletion of an element will free the memory it occupies.

features

  • The memory space can be discontinuous, and data is accessed through pointers. This feature makes its random access very inefficient, so it does not provide overloading of the [] operator. (shortcoming)
  • Due to the characteristics of the linked list, the efficiency of insertion and deletion at any position is high. (advantage)
  • Only direct access to the first and last two elements is supported. If you want to get other elements (with the same access time), you need to traverse the linked list. (shortcoming)
  • Summary: Random access is not supported, and insertion and deletion at any position are more efficient.

Applicable scene

It is suitable for scenarios where insertion and deletion operations are frequently performed and random access is infrequent.

example

The following example produces an empty list, ready to place characters, and then inserts all characters from 'a' to 'z' into it, and uses a loop to print and remove the first element of the set each time, thereby printing out all elements:

#include <iostream>
#include <list>

using namespace std;

  C++资料领取进裙:580475042

int main(int argc, char* argv[])
{
	list<char> listTemp;

	for (char c = 'a'; c <= 'z'; ++c)
		listTemp.push_back(c);

	while (!listTemp.empty())
	{
		cout <<listTemp.front() << " ";
		listTemp.pop_front();
	}

	return 0;
}

The return value of the member function empty() tells us whether there are elements left in the container, and as long as this function returns false, the loop continues. Inside the loop, the member function front() returns the first element, and the pop_front() function deletes the first element.

Note: list<pointer> is the lowest performance method. It is better to use list<object> or vector<pointer> directly, because the pointer has no construction and destruction, and does not occupy a lot of memory.

2.4 set

Set (collection), as the name suggests, is a mathematical collection - each element appears at most once, and the elements in the set have been sorted from small to large.

features

  • It is implemented using a red-black tree, and its internal elements are automatically sorted according to its value. Each element value can only appear once, and duplication is not allowed.
  • Every time a value is inserted, the red-black tree needs to be adjusted, which has a certain impact on efficiency. (shortcoming)
  • The insertion or deletion efficiency of map and set is higher than that of other sequence containers, because for associative containers, there is no need to do memory copy and memory movement. (advantage)
  • Summary: It is implemented by a red-black tree, and its internal elements are automatically sorted according to its value. Each element value can only appear once, no repetition is allowed, and the insertion and deletion efficiency is higher than that of other sequence containers.

Applicable scene

It is suitable for scenarios where it is often checked whether an element is in a certain collection and needs to be sorted.

example

The following example demonstrates two characteristics of set (collection):

#include <iostream>
#include <set>

using namespace std;

int main(int argc, char* argv[])
{
	set<int> setTemp;

	setTemp.insert(3);
	setTemp.insert(1);
	setTemp.insert(2);
	setTemp.insert(1);

	set<int>::iterator it;
	for (it = setTemp.begin(); it != setTemp.end(); it++)
	{
		cout << *it << " ";
	}

	return 0;
}

Output result: 1 2 3. A total of 4 numbers are inserted, but there are only 3 numbers in the set and they are ordered. It can be seen that the two characteristics of the set set mentioned above are ordered and non-repetitive.

When the elements in the set collection are structures, the structure must implement the overload of operator '<':

#include <iostream>
#include <set>
#include <string>

using namespace std;

struct People
{
	string name;
	int age;

	bool operator <(const People p) const
	{
		return age < p.age;
	}
};

int main(int argc, char* argv[])
{
	set<People> setTemp;

	setTemp.insert({"张三",14});
	setTemp.insert({ "李四", 16 });
	setTemp.insert({ "隔壁老王", 10 });

	set<People>::iterator it;
	for (it = setTemp.begin(); it != setTemp.end(); it++)
	{
		printf("姓名:%s 年龄:%d\n", (*it).name.c_str(), (*it).age);
	}

	return 0;
}

  C++资料领取进裙:580475042
/*
输出结果
姓名:王二麻子 年龄:10
姓名:张三 年龄:14
姓名:李四 年龄:16 
*/ 

You can see that the results are arranged in descending order of age. In addition, the string needs to be converted using c_str(), otherwise it will print out garbled characters.

In addition, Multiset is the same as set, except that it allows repeated elements, that is to say, multiset can include multiple elements with the same value. I won't do too much introduction here.

2.5 map

The map is implemented by a red-black tree, and its elements are a pair (key/value pairs) formed by "key value/real value".

map is mainly used for one-to-one mapping of data. A red-black tree is built inside the map. This tree has the function of automatically sorting data, so all the data inside the map is in order. For example, in a class, there is a one-to-one mapping relationship between each student's student number and his name.

features

  • Each element has a key and can only appear once, no duplicates are allowed.
  • Quickly search for records based on the key value. The complexity of the search is basically O(logN). If there are 1000 records, the binary search can be searched up to 10 times (1024). (advantage)
  • Every time a value is inserted, the red-black tree needs to be adjusted, which has a certain impact on efficiency. (shortcoming)
  • Adding and deleting nodes has little effect on the iterator, except for the operation node, it has no effect on other nodes. (advantage)
  • For iterators, real values ​​can be modified, but keys cannot be modified.
  • Summary: Elements are key-value pairs, key and value can be any type you need, each element has a key, and can only appear once, no repetition is allowed, and quickly find records according to the key.

Applicable scene

It is suitable for scenarios where a data dictionary needs to be stored and values ​​can be easily found based on keys.

example

#include "stdafx.h"
#include <iostream>
#include <map>
#include <string>

using namespace std;

int main(int argc, char* argv[])
{
	map<int, string> mapTemp;

	mapTemp.insert({ 5,"张三" });
	mapTemp.insert({ 3, "李四"});
	mapTemp.insert({ 4, "隔壁老王" });

	map<int, string>::iterator it;
	for (it = mapTemp.begin(); it != mapTemp.end(); it++)
	{
		printf("学号:%d 姓名:%s\n", (*it).first, (*it).second.c_str());
	}

	return 0;
}

/*
输出结果:
学号:3 姓名:李四
学号:4 姓名:隔壁老王
学号:5 姓名:张三
*/

Multimap is the same as map, but allows repeated elements, that is to say, multimap can contain multiple elements with the same key value (key). I won't do too much introduction here.

2.6 Container Adapter

In addition to the above seven basic container categories, in order to meet special needs, STL also provides some special (and pre-defined) container adapters, which are implemented based on the basic container categories. include:

1、stack

The name says it all, the stack container adopts a LIFO (last-in-first-out) management strategy for elements.

2、queue

The queue container adopts FIFO (first in first out) management strategy for elements. In other words, it is an ordinary buffer (buffer).

3、priority_queue

Elements in a priority_queue container can have different priorities. The so-called priority is defined based on the sorting criteria provided by the programmer (operators are used by default). The effect of Priority queue is equivalent to such a buffer: "The next element is always the element with the highest priority in the queue". If several elements have the highest priority at the same time, their order is undefined.

3. Summary

Summary of the characteristics of each container

  • Vector supports random access, inserting or deleting at the head and middle is inefficient, but inserting or deleting at the tail is efficient.
  • It supports random access, but the efficiency is not as high as that of vector. It is efficient to insert or delete at the head and tail, but it is inefficient to insert or delete in the middle.
  • The list does not support random access, and the efficiency of insertion and deletion at any position is high.
  • The set is implemented by a red-black tree, and its internal elements are automatically sorted according to its value. Each element value can only appear once, no repetition is allowed, and the insertion and deletion efficiency is higher than that of other sequence containers.
  • The elements of the map are key-value pairs. The key and value can be any type you need. Each element has a key and can only appear once. Duplication is not allowed. Quickly search for records based on the key.

In the actual use process, which one of these containers should be selected should follow the following principles:

1. If you need efficient random access and don't care about the efficiency of insertion and deletion, use vector.
2. If you need random access and care about the insertion and deletion efficiency of data at both ends, use deque.
3. If you need to insert and delete a large number of elements and don't care about the efficiency of random access, use list.
4. If you often check whether an element is in a certain set and needs to be sorted, use set if it exists only, and use multiset if it does not exist uniquely.
5. If you plan to store the data dictionary, and you need to find the value conveniently based on the key, use map for one-to-one situations, and use multimap for one-to-many situations.

Time complexity analysis of each container

  • The time complexity of vector insertion and deletion at the head and middle is O(N), the time complexity of insertion and deletion at the tail is O(1), the time complexity of random access is O(1), and the search time The complexity is O(N);
  • The time complexity of deque insertion and deletion in the middle is O(N), the time complexity of insertion and deletion at the head and tail is O(1), the time complexity of random access is O(1), and the search time The complexity is O(N);
  • The time complexity of list insertion and deletion at any position is O(1), and the time complexity of search is O(N);
  • Both set and map are implemented through red-black trees, so the time complexity of insertion, deletion and search operations is O(log N).

Commonality of each container

Each container generally has the following functions: default constructor, copy constructor, destructor, empty(), max_size(), size(), operator=, operator<, operator<=, operator>, operator>= , operator==, operator!=, swap().

Both sequential and associative containers share the following functions:

  • begin() : returns the iterator pointer of the first element of the container;
  • end(): Returns the iterator pointer one bit behind the last element of the container;
  • rbegin(): returns a reverse iterator pointer, pointing to the last element of the container;
  • rend(): returns a reverse iterator pointer, pointing to one bit before the first element of the container;
  • clear(): delete all elements in the container;
  • erase(it): Delete the element at the iterator pointer it.

Guess you like

Origin blog.csdn.net/QtCompany/article/details/131688999