Pila y cola de adaptadores de contenedores de C++ (incluidas deque, priority_queue)

Tabla de contenido

1. Adaptador de contenedor

  1.1 ¿Qué es un adaptador?

  1.2 La estructura subyacente de pila y cola en la biblioteca estándar STL

  1.3 y

        1.3.1 Introducción al principio deque (comprensión)

        1.3.2 Ventajas y desventajas de deque

        1.3.3 ¿Por qué elegir deque como el contenedor predeterminado subyacente de pila y cola?

2. Introducción y uso de la pila

  Introducción a la pila 2.1

  2.2 uso de pila

  2.3 implementación de simulación de pila

3. Introducción y uso de la cola

  3.1 Introducción a la cola

  3.2 Uso de colas

  3.3 implementación de la simulación de colas

4. prioridad_cola

  4.1 Introducción a la cola de prioridad

  4.2 Uso de la cola de prioridad

  4.3 implementación de simulación de cola de prioridad


1. Adaptador de contenedor

  1.1 ¿Qué es un adaptador?

Adapter es un patrón de diseño ( un patrón de diseño es un conjunto de uso repetido, conocido por la mayoría de las personas, clasificado y catalogado, y un resumen de la experiencia de diseño de código) , que convierte la interfaz de una clase en otra interfaz que los clientes desean .

  1.2 La estructura subyacente de pila y cola en la biblioteca estándar STL

Aunque los elementos también se pueden almacenar en stack y queue , no se dividen en los rangos de contenedores en STL , sino que se denominan adaptadores de contenedor , porque las pilas y las colas solo envuelven las interfaces de otros contenedores, STL En stack y queue, deque es utilizado por defecto .

  

  1.3 y

        1.3.1 Introducción al principio deque (comprensión)

Deque ( cola de doble extremo ) : es una estructura de datos de espacio " continuo " de doble apertura. El significado de doble apertura es: las operaciones de inserción y eliminación se pueden realizar en ambos extremos de la cabeza y la cola, y la complejidad del tiempo es O(1) En comparación con el vector , la eficiencia del complemento es alta y no es necesario mover elementos, en comparación con la lista , la tasa de utilización del espacio es relativamente alta.

Deque no es un espacio continuo real, pero está empalmado por pequeños espacios continuos. El deque real es similar a una matriz bidimensional dinámica . Su estructura subyacente se muestra en la siguiente figura:
La capa inferior de la cola de dos extremos es una ilusión de espacio continuo, que en realidad es segmentado y continuo. Para mantener su " continuidad general " y la ilusión de acceso aleatorio, recae el iterador de deque , por lo que el El diseño del iterador del deque

¿Cómo mantiene deque su estructura continua hipotética con la ayuda de su iterador? 

        1.3.2 Ventajas y desventajas de deque

 

En comparación con el vector , la ventaja de deque es que cuando se inserta y elimina la cabeza, no hay necesidad de mover elementos, y la eficiencia es particularmente alta , y cuando se expande, no hay necesidad de mover una gran cantidad de elementos , por lo que su eficiencia debe ser alta .
En comparación con list , su capa inferior es un espacio continuo, la tasa de utilización del espacio es relativamente alta y no es necesario almacenar campos adicionales.
Sin embargo, deque tiene un defecto fatal : no es adecuado para el recorrido, porque durante el recorrido, el iterador de deque necesita verificar con frecuencia si se ha movido al límite de un espacio pequeño, lo que resulta en una baja eficiencia . ser necesario atravesar con frecuencia, por lo que en la práctica, cuando se necesita una estructura lineal , el vector y la lista tienen prioridad en la mayoría de los casos . No hay muchas aplicaciones de deque , y una aplicación que se puede ver hasta ahora es que STL lo usa como los datos subyacentes de la estructura la colayde la pila .

deque: 1. El cálculo del operador [ ] es un poco complicado, se usa en grandes cantidades y el rendimiento cae (en comparación con el vector)

              2. La eficiencia de inserción y eliminación en el medio no es alta

              3. Los iteradores angulares subyacentes pueden ser complejos

        1.3.3 ¿Por qué elegir deque como el contenedor predeterminado subyacente de pila y cola?

Stack es una estructura de datos lineal especial de último en entrar, primero en salir, por lo que siempre que tenga una estructura lineal con operaciones push_back() y pop_back() , se puede usar como el contenedor subyacente de la pila, como vector y list ; queue es un primero en entrar, primero en salir especial Una estructura de datos lineal, siempre que tenga una estructura lineal con operaciones push_back y pop_front , puede usarse como el contenedor subyacente de queue , como list .
Sin embargo, en STL, deque se selecciona como contenedor subyacente de forma predeterminada para stack y queue , principalmente porque:
1. No es necesario atravesar la pila y la cola ( por lo que la pila y la cola no tienen iteradores ) , y solo deben operar en uno o ambos extremos del fijo.
2. Cuando los elementos en la pila crecen, deque es más eficiente que el vector ( no es necesario mover una gran cantidad de datos al expandirse ) ; cuando los elementos en la cola crecen, deque no solo tiene una alta eficiencia, sino que también tiene un alto uso de memoria .
Combinando las ventajas de deque , evita perfectamente sus defectos.
en conclusión:

1. La inserción y la eliminación de la cabeza a la cola son muy adecuadas. En comparación con el vector y la lista, es muy adecuado para el contenedor de adaptación predeterminado de pila y cola.

2. Inserte y elimine la lista de usos múltiples en el medio

3. Vector multipropósito de acceso aleatorio

2. Introducción y uso de la pila

  Introducción a la pila 2.1

  • stack es un adaptador de contenedor, que se usa especialmente en el entorno de contexto con la operación de último en entrar, primero en salir, y su eliminación solo puede insertar y extraer elementos de un extremo del contenedor .
  • La pila se implementa como un adaptador de contenedor, que encapsula una clase específica como su contenedor subyacente y proporciona un conjunto de funciones de miembro específicas para acceder a sus elementos, utilizando una clase específica como su cola de contenedor específica de elemento subyacente (es decir, la parte superior de la pila) se empuja y se abre.
  • El contenedor subyacente de la pila puede ser cualquier plantilla de clase de contenedor estándar o alguna otra clase de contenedor específica, y estas clases de contenedor deben admitir las siguientes operaciones:

        vacío: juicio vacío operación

        tamaño: devuelve el número de elementos válidos en la pila

        back: Obtener la operación del elemento de cola

        push_back: operación de elemento de inserción de cola

        pop_back: operación de elemento de eliminación de cola

  • Los contenedores estándar vector, deque y list cumplen todos estos requisitos. De manera predeterminada, si no se especifica ningún contenedor subyacente específico para la pila, se usa deque de manera predeterminada.

  2.2 uso de pila

función descriptiva
Descripción de la interfaz
pila() construir una pila vacía
vacío() Comprobar si la pila está vacía
tamaño() Devuelve el número de elementos en la pila.
arriba() Devuelve una referencia al elemento superior de la pila.
empujar() Empuje el elemento val en la pila
estallido()

Pop el elemento al final de la pila

 

 

  2.3 implementación de simulación de pila

	template<class T,class Container=deque<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		T& top()
		{
			return _con.back();
		}
		const T& top() const
		{
			return _con.back();
		}
		bool empty() const
		{
			return _con.empty();
		}
		size_t size() const
		{
			return _con.size();
		}

	private:
		Container _con;
	};

3. Introducción y uso de la cola

  3.1 Introducción a la cola

  • Una cola es un adaptador de contenedor diseñado para operar en un contexto FIFO (primero en entrar, primero en salir), donde los elementos se insertan desde un extremo del contenedor y se extraen desde el otro.
  • Una cola se implementa como un adaptador de contenedor, que encapsula una clase de contenedor específica como su clase de contenedor subyacente, y la cola proporciona un conjunto específico de funciones miembro para acceder a sus elementos. Los elementos ingresan a la cola desde la cola y salen de la cola desde la cabeza.
  • El contenedor subyacente puede ser una de las plantillas de clase de contenedor estándar u otra clase de contenedor especialmente diseñada. El contenedor subyacente soportará al menos las siguientes operaciones:
        vacío: comprobar si la cola está vacía
        tamaño: devuelve el número de elementos válidos en la cola
        front: Devuelve una referencia al elemento frontal de la cola
        atrás: devuelve una referencia al elemento al final de la cola
        push_back: ingrese a la cola al final de la cola
        pop_front: fuera de la cola al principio de la cola
  • Las clases de contenedor estándar deque y list cumplen estos requisitos. De forma predeterminada, si no se especifica ninguna clase de contenedor para la creación de instancias de cola, se utiliza el deque de contenedor estándar.

  3.2 Uso de colas

declaración de función Descripción de la interfaz
cola() construir una cola vacía
vacío() Compruebe si la cola está vacía, devuelva verdadero, de lo contrario, devuelva falso
tamaño() Devuelve el número de elementos válidos en la cola
frente() Devuelve una referencia al elemento al principio de la cola.
atrás() Devuelve una referencia al elemento al final de la cola.
empujar() Poner en cola el elemento val al final de la cola
estallido() Desencolar el elemento al principio de la cola

 

 

 

 

 

 

 

 

 

  3.3 implementación de la simulación de colas

	template<class T,class Container=deque<int>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();
		}
		T& back()
		{
			return _con.back();
		}
		T& front()
		{
			return _con.front();
		}
		const T& back() const
		{
			return _con.back();
		}
		const T& front() const
		{
			return _con.front();
		}
		bool empty() const
		{
			return _con.empty();
		}
		size_t size() const
		{
			return _con.size();
		}
	private:
		Container _con;
	};

4. prioridad_cola

  4.1 Introducción a la cola de prioridad

Un funtor , también conocido como objeto de función (Objeto de función), es una clase que puede realizar funciones de función.

La sintaxis del funtor es casi la misma que nuestra llamada de función ordinaria, pero como una clase del funtor, el operador operator() debe estar sobrecargado . Porque llamar al funtor es en realidad llamar al operador operator() sobrecargado a través del objeto de clase.

  • Una cola de prioridad es un adaptador de contenedor cuyo primer elemento es siempre el más grande de los elementos contenidos de acuerdo con estrictos criterios de ordenamiento débil.
  • Este contexto es similar a un montón, donde los elementos se pueden insertar en cualquier momento y solo se puede recuperar el elemento de montón más grande (el que está en la parte superior de la cola de prioridad).
  • La cola de prioridad se implementa como un adaptador de contenedor, que encapsula una clase de contenedor específica como su clase de contenedor subyacente, y la cola proporciona un conjunto de funciones miembro específicas para acceder a sus elementos. Los elementos se extraen de la "cola" de un contenedor en particular, que se denomina la parte superior de la cola de prioridad.
  • El contenedor subyacente puede ser cualquier plantilla de clase de contenedor estándar o cualquier otra clase de contenedor de un diseño específico. Los contenedores deben ser accesibles a través de iteradores de acceso aleatorio y admitir las siguientes operaciones:
        vacío (): comprueba si el contenedor está vacío
        size(): Devuelve el número de elementos válidos en el contenedor
        front(): devuelve una referencia al primer elemento del contenedor
        push_back(): inserta elementos al final del contenedor
        pop_back(): elimina el elemento de cola del contenedor
  • Las clases de contenedor estándar vector y deque satisfacen estas necesidades. De forma predeterminada, se utiliza vector si no se especifica ninguna clase de contenedor para una instancia de clase de prioridad_cola en particular.
  • Los iteradores de acceso aleatorio deben admitirse para que la estructura del montón siempre se mantenga internamente. El adaptador de contenedor hace esto automáticamente llamando automáticamente a las funciones algorítmicas make_heap, push_heap y pop_heap cuando es necesario.

  4.2 Uso de la cola de prioridad

La cola de prioridad usa el vector como su contenedor de almacenamiento de datos subyacente de forma predeterminada, y el algoritmo de almacenamiento dinámico se usa en el vector para construir los elementos en el vector en una estructura de almacenamiento dinámico, por lo que la cola de prioridad es un almacenamiento dinámico , y puede considerar usar la cola de prioridad donde lo necesite para usar el montón. Nota: Priority_queue es un gran montón de forma predeterminada.
declaración de función Descripción de la interfaz

prioridad_cola()

cola_prioridad(primero,último)

Construir una cola de prioridad vacía
vacío() Detecta si la cola de prioridad está vacía, devuelve verdadero, de lo contrario, devuelve falso
arriba() Devuelve el elemento más grande (más pequeño) en la cola de prioridad, es decir, el elemento superior
empujar() Insertar elemento val en la cola de prioridad
estallido() Elimine el elemento más grande (más pequeño) en la cola de prioridad, es decir, el elemento superior del montón
Montón superior grande (orden descendente)
//构造一个空的优先队列(此优先队列默认为大顶堆)
priority_queue<int> big_heap;   

//另一种构建大顶堆的方法
priority_queue<int,vector<int>,less<int> > big_heap2;  

pequeño montón superior (orden ascendente)

//构造一个空的优先队列,此优先队列是一个小顶堆
priority_queue<int,vector<int>,greater<int> > small_heap;  

Aviso:

Si usa menos y más, se requieren archivos de encabezado:

#include <functional>

Si coloca datos de tipo personalizado en la cola de prioridad, los usuarios deben proporcionar > o < sobrecarga en el tipo personalizado

  4.3 implementación de simulación de cola de prioridad

template<class T, class Container = vector<T>,class Compare=std::less<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}
			//建堆,(_con.size()-1-1)/2为末尾节点的父节点所在位置
			for (int i = ((_con.size() - 1 - 1) / 2); i >= 0; i--)
			{
				//向下建堆logN
				adjust_down(i);
			}
		}
		//向上建堆
		void adjust_up(size_t child)
		{
			/*size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				//建大堆
				if (_con[parent] < _con[child])
				{
					std::swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}*/
			Compare com;//仿函数,类对象像函数一样使用,重载operator()
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				//建大堆
				if (com(_con[parent],_con[child]))
				{
					std::swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
		void push(const T& x)
		{
            //先vector插入,再向上调整
			_con.push_back(x);

			adjust_up(_con.size() - 1);
		}
		//向下建堆
		void adjust_down(size_t parent)
		{
			/*size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				//选出左右孩子大的那一个
				if (child + 1 < _con.size() && _con[child] < _con[child + 1])
				{
					++child;
				}
				//建大堆
				if (_con[parent] < _con[child])
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}*/
			Compare com;//仿函数,类对象像函数一样使用,重载operator()
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				//选出左右孩子大的那一个
				if (child + 1 < _con.size() && com(_con[child] , _con[child + 1]))
				{
					++child;
				}
				//建大堆
				if (com(_con[parent] , _con[child]))
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		void pop()
		{
            //根与最右下边的孩子交换,vector尾删,再下调整
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			adjust_down(0);
		}
		const T& top() const
		{
			return _con[0];
		}
		bool empty() const
		{
			return _con.empty();
		}
		size_t size() const
		{
			return _con.size();
		}
	private:
		Container _con;
	};

No entraré en detalles sobre el proceso de creación de montones aquí, y puede buscar el contenido de la estructura de datos para revisarlo usted mismo.

Supongo que te gusta

Origin blog.csdn.net/bang___bang_/article/details/130552826
Recomendado
Clasificación