Una breve introducción a cada contenedor de C++ STL

1. ¿Qué es STL?

1. STL (Standard Template Library), la biblioteca de plantillas estándar, es una biblioteca de programas C++ eficiente que contiene muchas estructuras de datos básicas y algoritmos básicos de uso común. Proporciona un marco de aplicación escalable para la mayoría de los programadores de C ++, que representa en gran medida la reutilización del software.

2. Desde el nivel lógico, la idea de programación genérica se plasma en STL . En esta idea, la mayoría de los algoritmos básicos se abstraen y generalizan, independientemente de las estructuras de datos correspondientes, y se utilizan para tratar varias situaciones de la misma manera o de manera similar.

3. Desde la perspectiva del nivel de implementación, todo el STL se implementa de forma tipo parametrizada, en base a plantillas .

STL tiene seis componentes principales, pero principalmente incluye tres partes: contenedor, iterador y algoritmo.

  • Contenedores : se utiliza para gestionar una colección de objetos de un determinado tipo. Cada contenedor tiene sus ventajas y desventajas, por lo que para satisfacer las diferentes necesidades del programa, STL ha preparado siete tipos básicos de contenedores.
  • Iteradores (Iterators): Se utilizan para realizar acciones transversales sobre los elementos de una colección de objetos. Esta colección de objetos puede ser un contenedor o puede ser parte de un contenedor. Cada tipo de contenedor proporciona sus propios iteradores, y estos iteradores conocen la estructura interna de ese tipo de contenedor.
  • Algoritmos (Algorithms): se utilizan para procesar los elementos de la colección de objetos, como Ordenar, Buscar, Copiar, Borrar esos elementos. Con la ayuda de los iteradores, solo necesitamos escribir un algoritmo una vez y aplicarlo a cualquier contenedor, porque los iteradores de todos los contenedores brindan una interfaz consistente.

El concepto básico de STL es separar datos y operaciones. El contenedor administra los datos, el algoritmo realiza la operación y el iterador actúa como el pegamento entre los dos, de modo que cualquier algoritmo puede interactuar con cualquier contenedor.

2. Contenedores

Los contenedores se utilizan para administrar ciertos tipos de objetos. Para satisfacer las diferentes necesidades del programa, STL ha preparado dos tipos de siete tipos básicos de contenedores:

  • Contenedores de secuencia, que son grupos ordenables en los que cada elemento tiene una posición fija, según cuándo y dónde se inserte, independientemente del valor del elemento. Si agrega seis elementos a un grupo, estarán en el mismo orden en que se insertaron. STL proporciona tres contenedores en serie: vector (vector), cola de dos extremos (deque) y lista (lista). Además, también puede usar string y array como un contenedor en serie.
  • Contenedores asociativos (Contenedores asociativos), este es un grupo ordenado, la posición del elemento depende de un criterio de clasificación específico y el valor del elemento, y no tiene nada que ver con el orden de inserción. Si coloca seis elementos en un grupo de este tipo, sus posiciones dependen de los valores de los elementos, no del orden de inserción. STL proporciona cuatro contenedores asociativos: colección (conjunto), colecciones múltiples (conjunto múltiple), mapeo (mapa) y mapeo múltiple (mapa múltiple).

El diagrama esquemático se muestra en la siguiente figura:

2.1 vectores

Vector (vector): es un contenedor en serie, que es similar a una matriz, de hecho, pero es superior a una matriz. En términos generales, la matriz no se puede expandir dinámicamente, por lo que cuando el programa se está ejecutando, desperdicia memoria o provoca fuera de los límites. El vector solo compensa este defecto.Cuando el espacio de memoria no es suficiente, es necesario volver a solicitar una memoria lo suficientemente grande y copiar la memoria.

características

  • Tiene un espacio de memoria continuo, por lo que puede admitir muy bien el acceso aleatorio, es decir, el operador [] y .at(), y el acceso aleatorio es rápido. (ventaja)
  • Al insertar o eliminar elementos en su cabecera o centro, para mantener el orden relativo original, todos los elementos después del punto de inserción o eliminación deben moverse, por lo que la eficiencia de la inserción o eliminación es relativamente baja. (defecto)
  • Es más rápido insertar y eliminar elementos más tarde y, por lo general, no es necesario mover la memoria en este momento. (ventaja)
  • Resumen: equivalente a una matriz expandible (matriz dinámica), el acceso aleatorio es rápido, la inserción o eliminación en la cabeza y en el medio es ineficiente, pero la inserción o eliminación al final es eficiente.

escena aplicable

Es adecuado para escenas con objetos simples, pequeños cambios y acceso aleatorio frecuente.

ejemplo

El siguiente ejemplo define un vector de enteros, inserta 6 elementos e imprime todos los elementos:

#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 y

Deque (cola de dos extremos) es un espacio de memoria continuo con aberturas de dos vías (uniendo dinámicamente varios espacios continuos a través de matrices de punteros), y se puede agregar un nuevo espacio en cualquier momento. La mayor tarea de deque es mantener la ilusión de continuidad general en estos espacios continuos segmentados y proporcionar una interfaz de acceso aleatorio.

características

  • Una vez que se va a agregar un nuevo espacio en la cabeza y la cola del deque, se configura una cierta cantidad de espacio continuo y se encadena en la cabeza o la cola de todo el deque, por lo que la inserción de elementos en la cabeza o la cola es muy rápida. (ventaja)
  • Colocar elementos en el medio lleva más tiempo porque hay que mover otros elementos. (defecto)
  • deque es un compromiso entre lista y vector. Tiene tanto las ventajas de la lista como las ventajas de la alta eficiencia de acceso aleatorio del vector.
  • Resumen: se admite el acceso aleatorio, pero la eficiencia no es tan alta como el vector. La inserción o eliminación en la cabeza y la cola es eficiente, pero la inserción o eliminación en el medio es ineficiente.

escena aplicable

Es adecuado para escenarios que requieren un acceso aleatorio frecuente y se preocupan por la inserción y eliminación de datos en ambos extremos.

ejemplo

El siguiente ejemplo declara un deque de tipo punto flotante, inserta 6 elementos al final del contenedor y finalmente imprime todos los elementos.

#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 lista

La lista se implementa mediante una lista doblemente enlazada, los elementos se almacenan en el montón y cada elemento se coloca en una parte de la memoria. No existe el hábito de reserva de espacio, por lo que cada asignación de un elemento se asignará desde la memoria, y cada eliminación de un elemento liberará la memoria que ocupa.

características

  • El espacio de memoria puede ser discontinuo y se accede a los datos a través de punteros, esta característica hace que su acceso aleatorio sea muy ineficiente, por lo que no proporciona una sobrecarga del operador []. (defecto)
  • Debido a las características de la lista enlazada, la eficiencia de inserción y eliminación en cualquier posición es alta. (ventaja)
  • Solo se admite el acceso directo al primer y último elemento. Si desea obtener otros elementos (con el mismo tiempo de acceso), debe recorrer la lista vinculada. (defecto)
  • Resumen: no se admite el acceso aleatorio, y la inserción y eliminación en cualquier posición son más eficientes.

escena aplicable

Es adecuado para escenarios donde las operaciones de inserción y eliminación se realizan con frecuencia y el acceso aleatorio es poco frecuente.

ejemplo

El siguiente ejemplo produce una lista vacía, lista para colocar caracteres, y luego inserta todos los caracteres de 'a' a 'z' en ella, y usa un bucle para imprimir y eliminar el primer elemento del conjunto cada vez, imprimiendo así todos elementos:

#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;
}

El valor de retorno de la función miembro empty() nos dice si quedan elementos en el contenedor, y mientras esta función devuelva falso, el ciclo continúa. Dentro del bucle, la función miembro front() devuelve el primer elemento y la función pop_front() elimina el primer elemento.

Nota: list<pointer> es el método de rendimiento más bajo. Es mejor usar list<object> o vector<pointer> directamente, porque el puntero no tiene construcción ni destrucción, y no ocupa mucha memoria.

2,4 conjunto

El conjunto (colección), como sugiere el nombre, es una colección matemática: cada elemento aparece como máximo una vez y los elementos del conjunto se han ordenado de menor a mayor.

características

  • Se implementa mediante un árbol rojo-negro, y sus elementos internos se ordenan automáticamente según su valor. Cada valor de elemento solo puede aparecer una vez y no se permite la duplicación.
  • Cada vez que se inserta un valor, el árbol rojo-negro debe ajustarse, lo que tiene un cierto impacto en la eficiencia. (defecto)
  • La eficiencia de inserción o eliminación de mapas y conjuntos es mayor que la de otros contenedores de secuencias, porque para los contenedores asociativos, no hay necesidad de copiar ni mover la memoria. (ventaja)
  • Resumen: está implementado por un árbol rojo-negro, y sus elementos internos se clasifican automáticamente de acuerdo con su valor. Cada valor de elemento solo puede aparecer una vez, no se permite la repetición, y la eficiencia de inserción y eliminación es mayor que la de otra secuencia contenedores

escena aplicable

Es adecuado para escenarios en los que a menudo se comprueba si un elemento está en una determinada colección y necesita ser ordenado.

ejemplo

El siguiente ejemplo demuestra dos características del conjunto (colección):

#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;
}

Resultado de salida: 1 2 3. Se insertan un total de 4 números, pero solo hay 3 números en el conjunto y están ordenados, se puede observar que las dos características del conjunto mencionadas anteriormente son ordenadas y no repetitivas.

Cuando los elementos de la colección de conjuntos son estructuras, la estructura debe implementar la sobrecarga del operador '<':

#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 
*/ 

Puede ver que los resultados están ordenados en orden descendente de edad. Además, la cadena debe convertirse usando c_str(), de lo contrario, imprimirá caracteres ilegibles.

Además, Multiset es lo mismo que set, excepto que permite elementos repetidos, es decir, multiset puede incluir múltiples elementos con el mismo valor. No haré demasiada introducción aquí.

2.5 mapa

El mapa está implementado por un árbol rojo-negro, y sus elementos son un par (pares clave/valor) formado por "valor clave/valor real".

El mapa se utiliza principalmente para el mapeo de datos uno a uno. Se construye un árbol rojo-negro dentro del mapa. Este árbol tiene la función de clasificar automáticamente los datos, por lo que todos los datos dentro del mapa están en orden. Por ejemplo, en una clase, hay una relación de mapeo uno a uno entre el número de estudiante de cada estudiante y su nombre.

características

  • Cada elemento tiene una clave y solo puede aparecer una vez, no se permiten duplicados.
  • Busque rápidamente registros en función del valor de la clave. La complejidad de la búsqueda es básicamente O (logN). Si hay 1000 registros, la búsqueda binaria se puede buscar hasta 10 veces (1024). (ventaja)
  • Cada vez que se inserta un valor, el árbol rojo-negro debe ajustarse, lo que tiene un cierto impacto en la eficiencia. (defecto)
  • Agregar y eliminar nodos tiene poco efecto en el iterador, excepto por el nodo de operación, no tiene efecto en otros nodos. (ventaja)
  • Para los iteradores se pueden modificar los valores reales, pero no se pueden modificar las claves.
  • Resumen: los elementos son pares clave-valor, la clave y el valor pueden ser del tipo que necesite, cada elemento tiene una clave y solo puede aparecer una vez, no se permite la repetición y encuentra rápidamente registros de acuerdo con la clave.

escena aplicable

Es adecuado para escenarios en los que se debe almacenar un diccionario de datos y los valores se pueden encontrar fácilmente en función de las claves.

ejemplo

#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 es lo mismo que map, pero permite elementos repetidos, es decir, multimap puede contener múltiples elementos con el mismo valor clave (clave). No haré demasiada introducción aquí.

2.6 Adaptador de contenedor

Además de las siete categorías básicas de contenedores anteriores, para satisfacer necesidades especiales, STL también proporciona algunos adaptadores de contenedores especiales (y predefinidos), que se implementan en función de las categorías básicas de contenedores. incluir:

1, pila

El nombre lo dice todo, el contenedor de pilas adopta una estrategia de gestión LIFO (last-in-first-out) para los elementos.

2, cola

El contenedor de cola adopta la estrategia de gestión FIFO (primero en entrar, primero en salir) para los elementos. En otras palabras, es un búfer ordinario (búfer).

3, prioridad_cola

Los elementos en un contenedor de prioridad_cola pueden tener diferentes prioridades. La denominada prioridad se define en función de los criterios de clasificación proporcionados por el programador (los operadores se utilizan de forma predeterminada). El efecto de la cola de prioridad es equivalente a un búfer de este tipo: "El siguiente elemento es siempre el elemento con la prioridad más alta en la cola". Si varios elementos tienen la prioridad más alta al mismo tiempo, su orden es indefinido.

3. Resumen

Resumen de las características de cada contenedor

  • Vector admite acceso aleatorio, insertar o eliminar en la cabeza y en el medio es ineficiente, pero insertar o eliminar en la cola es eficiente.
  • Admite acceso aleatorio, pero la eficiencia no es tan alta como la del vector. Es eficiente insertar o eliminar en la cabeza y la cola, pero es ineficiente insertar o eliminar en el medio.
  • La lista no admite acceso aleatorio y la eficiencia de inserción y eliminación en cualquier posición es alta.
  • El conjunto está implementado por un árbol rojo-negro, y sus elementos internos se ordenan automáticamente de acuerdo con su valor. Cada valor de elemento solo puede aparecer una vez, no se permite la repetición, y la eficiencia de inserción y eliminación es mayor que la de otros contenedores de secuencia. .
  • Los elementos del mapa son pares clave-valor. La clave y el valor pueden ser del tipo que necesite. Cada elemento tiene una clave y solo puede aparecer una vez. No se permite la duplicación. Busque rápidamente registros basados ​​en la clave.

En el proceso de uso real, cuál de estos contenedores debe seleccionarse debe seguir los siguientes principios:

1. Si necesita un acceso aleatorio eficiente y no le importa la eficiencia de inserción y eliminación, use vector.
2. Si necesita acceso aleatorio y se preocupa por la eficiencia de inserción y eliminación de datos en ambos extremos, use deque.
3. Si necesita insertar y eliminar una gran cantidad de elementos y no le importa la eficiencia del acceso aleatorio, use la lista.
4. Si comprueba a menudo si un elemento está en un determinado conjunto y necesita ordenarse, use conjunto si solo existe y use multiconjunto si no existe de forma única.
5. Si planea almacenar el diccionario de datos y necesita encontrar el valor convenientemente en función de la clave, use map para situaciones de uno a uno y use multimapa para situaciones de uno a muchos.

Análisis de complejidad temporal de cada contenedor

  • La complejidad temporal de la inserción y eliminación de vectores en la cabeza y el centro es O(N), la complejidad temporal de la inserción y eliminación en la cola es O(1), la complejidad temporal del acceso aleatorio es O(1), y la búsqueda tiempo La complejidad es O(N);
  • La complejidad temporal de la inserción y eliminación de deque en el medio es O(N), la complejidad temporal de la inserción y eliminación en la cabeza y la cola es O(1), la complejidad temporal del acceso aleatorio es O(1), y la búsqueda tiempo La complejidad es O(N);
  • La complejidad temporal de la inserción y eliminación de la lista en cualquier posición es O(1), y la complejidad temporal de la búsqueda es O(N);
  • Tanto el conjunto como el mapa se implementan a través de árboles rojo-negro, por lo que la complejidad temporal de las operaciones de inserción, eliminación y búsqueda es O (log N).

Elementos comunes de cada contenedor

Cada contenedor generalmente tiene las siguientes funciones: constructor por defecto, constructor de copias, destructor, vacío(), tamaño máximo(), tamaño(), operador=, operador<, operador<=, operador>, operador>=, operador==, operador !=, intercambiar().

Tanto los contenedores secuenciales como los asociativos comparten las siguientes funciones:

  • begin() : devuelve el puntero del iterador del primer elemento del contenedor;
  • end(): Devuelve el puntero del iterador un bit por detrás del último elemento del contenedor;
  • rbegin(): devuelve un puntero de iterador inverso, que apunta al último elemento del contenedor;
  • rend(): devuelve un puntero de iterador inverso, que apunta a un bit antes del primer elemento del contenedor;
  • clear(): elimina todos los elementos del contenedor;
  • erase(it): elimina el elemento en el puntero del iterador.

Supongo que te gusta

Origin blog.csdn.net/QtCompany/article/details/131688999
Recomendado
Clasificación