cola en la biblioteca C++ STL


Directorio de artículos

  • Introducción a la cola
  • Interfaces comunes para cola
  • Implementación de simulación de cola.
  • Introducción a la cola_prioritaria
  • Interfaces comunes de prioridad_queue
  • Implementación de simulación de prioridad_queue
  • adaptador de contenedor
  • Introducción a deque
  • Funtor


1. Introducción a la cola

  • 1. 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 los elementos se extraen del otro extremo.
  • 2. La cola se implementa como un adaptador de contenedor. El adaptador de contenedor encapsula una clase de contenedor específica como su clase de contenedor subyacente. La cola proporciona un conjunto específico de funciones miembro para acceder a sus elementos. Los elementos se colocan en la cola desde el final de la cola y se retiran de la cola desde el principio.
  • 3. El contenedor subyacente puede ser una de las plantillas de clase de contenedor estándar u otras clases de contenedor especialmente diseñadas. El contenedor subyacente debe admitir al menos las siguientes operaciones:
    • vacío: comprueba 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 principal de la cola
    • back: Devuelve una referencia al último elemento de la cola
    • push_back: cola al final de la cola
    • pop_front: quitar la cola al principio de la cola
  • 4. Las clases de contenedores estándar deque y list cumplen con estos requisitos. De forma predeterminada, si no se especifica ninguna clase de contenedor para la creación de instancias de cola, se utiliza la deque de contenedor estándar.

2. Interfaces comunes de cola.

Descripción de la interfaz de declaración de funciones
queue()                                                                                  construye una cola vacía
vacío ()                                                  detecta si la cola está vacía y devuelve verdadero; de lo contrario, devuelve falso
size()                                                                          devuelve el número de elementos válidos en la cola
front()                                                                              devuelve una referencia al elemento principal de la cola
back()                                                                              devuelve una referencia al último elemento de la cola
push()                                                                           coloca el elemento val en la cola al final de la cola
pop()                                                                                   quita de la cola el elemento principal de la cola
swap()                                                                                intercambia el contenido de dos contenedores 

 Demostración de interfaces relacionadas:

#include <iostream>
#include <queue>

int main()
{
	std::queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);

	while (!q.empty()) {
		std::cout << q.front() << " ";
		q.pop();
	}
	return 0;
}

3. Implementación de simulación de cola.

#pragma once
namespace Queue
{
	//这里默认是采用deque这种适配器来模拟栈
	template<class T, class Contain = std::deque<T>>
	class queue
	{
	public:
		/*
		queue()//这里不需要显示写构造函数,因为是自定义类型,直接调用默认构造函数就行
		{}
		*/

		bool empty()
		{
			return _con.empty();
		}

		size_t size()const
		{
			return _con.size();
		}

		const T& front()const
		{
			return _con.front();
		}

		const T& back()const
		{
			return _con.back();
		}

		void push(const T& val)
		{
			_con.push_back(val);
		}

		void pop()
		{
			_con.pop_front();
		}

		void swap(queue<T, Contain>& q)
		{
			std::swap(_con, q._con);
		}

	private:
		Contain _con;

	};
}

4. Introducción a prioridad_queue

  • 1. Una cola de prioridad es un adaptador de contenedor cuyo primer elemento es siempre el mayor de los elementos que contiene según estrictos criterios de ordenamiento débiles.
  • 2. Este contexto es similar a un montón, donde los elementos se pueden insertar en cualquier momento y solo se puede recuperar el elemento del montón más grande (el elemento superior en la cola de prioridad).
  • 3. La cola de prioridad se implementa como un adaptador de contenedor. El adaptador de contenedor encapsula una clase de contenedor específica como su clase de contenedor subyacente. La cola proporciona un conjunto específico de funciones miembro para acceder a sus elementos. Los elementos se extraen de la "cola" de un contenedor específico, que se denomina parte superior de la cola de prioridad.
  • 4. El contenedor subyacente puede ser cualquier plantilla de clase de contenedor estándar o pueden ser otras clases de contenedor diseñadas específicamente. Se debe poder acceder al contenedor mediante 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(): Insertar elementos al final del contenedor
    • pop_back(): elimina el elemento al final del contenedor
  • 5. Las clases de contenedores 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 cola prioritaria particular.
  • 6. Necesidad de admitir iteradores de acceso aleatorio 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 sea necesario.

5. Interfaces comunes de prioridad_queue

De forma predeterminada, la cola de prioridad utiliza un vector como contenedor subyacente para almacenar datos. El algoritmo de montón se utiliza en el vector para construir los elementos del vector en una estructura de montón. Por lo tanto, prioridad_queue es un montón. Puede considerar usar prioridad_queue dondequiera que se necesita montón.. Nota: De forma predeterminada, Priority_queue es un montón grande .

Descripción de la interfaz de declaración de funciones
Priority_queue()/priority_queue(first, last)                  construye una cola de prioridad vacía
vacío ()             detecta si la cola de prioridad está vacía y devuelve verdadero; de lo contrario, devuelve falso                              
top()                                                          devuelve el elemento más grande (el elemento más pequeño) en la cola de prioridad, es decir, el elemento superior del montón
push(x)                                                                          inserta el elemento x en la cola de prioridad
pop ()                                                  elimina el elemento más grande (más pequeño) en la cola de prioridad, es decir, el elemento superior del montón

1. De forma predeterminada, prioridad_queue es una pila grande

#include <iostream>
#include <deque>
#include <queue>
#include "queue.h"
#include <vector>
#include <functional>
#include <functional> // greater算法的头文件

void TestPriorityQueue()
{
	// 默认情况下,创建的是大堆,其底层按照小于号比较
	std::vector<int> v{3,2,7,6,0,4,1,9,8,5};
	std::priority_queue<int> q1;
	for (auto& e : v)
		q1.push(e);
	std::cout << q1.top() <<std:: endl;
	// 如果要创建小堆,将第三个模板参数换成greater比较方式
	std::priority_queue<int, std::vector<int>, std::greater<int>> q2(v.begin(), v.end());
	std::cout << q2.top() << std::endl;
}
int main()
{
	TestPriorityQueue();
	return 0;
}

2. Si coloca datos de tipo personalizado en Priority_queue, el usuario debe proporcionar una sobrecarga de > o < en el tipo personalizado.

#include <iostream>
#include <deque>
#include <queue>
#include "queue.h"
#include <vector>
#include <functional>
#include <functional> // greater算法的头文件

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend std::ostream& operator<<(std::ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestPriorityQueue()
{
	// 大堆,需要用户在自定义类型中提供<的重载
	std::priority_queue<Date> q1;
	q1.push(Date(2018, 10, 29));
	q1.push(Date(2018, 10, 28));
	q1.push(Date(2018, 10, 30));
	std::cout << q1.top() << std::endl;
	// 如果要创建小堆,需要用户提供>的重载
	std::priority_queue<Date, std::vector<Date>, std::greater<Date>> q2;
	q2.push(Date(2018, 10, 29));
	q2.push(Date(2018, 10, 28));
	q2.push(Date(2018, 10, 30));
	std::cout << q2.top() << std::endl;
}

int main()
{
	TestPriorityQueue();
	return 0;
}

6. Implementación de simulación de prioridad_queue

#pragma once
namespace Priority_queue
{
	template<class T>
	struct Less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	struct Greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
	template<class T,class Container=std::vector<T>,class Compare=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++;

				int child = _con.size() - 1;
				int parent = (child - 1) / 2;
				for (int i = parent; i >= 0; i--) AdjustDown(i);
			}
		}

		void AdjustUp(size_t child)
		{
			Compare com;

			size_t parent = (child - 1) / 2;
			while (child) {
				if (com(_con[parent], _con[child])) {
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else break;
			}
		}

		void AdjustDown(size_t parent)
		{
			Compare com;

			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 - 1) / 2;
					child = parent * 2 + 1;
				}
				else break;
			}
		}

		void push(const T& val)
		{
			_con.push_back(val);
			AdjustUp(_con.size() - 1);
		}

		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}

		bool empty()
		{
			return _con.empty(); 
		}

		size_t size()
		{
			return _con.size();
		}

		const T& top()
		{
			return _con[0];
		}

	private:
		Container _con;
	};

}

7. Adaptador de contenedor

1. El concepto de adaptador

Adaptador es un patrón de diseño (un patrón de diseño es un conjunto de experiencias de diseño de código que se usan repetidamente, son conocidas por la mayoría de las personas, clasificadas y catalogadas). Este patrón convierte la interfaz de una clase en otra que el cliente desea.

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

Aunque los elementos también se pueden almacenar en pilas y colas, en STL no se clasifican en contenedores, sino que se denominan adaptadores de contenedores. Esto se debe a que las pilas y las colas simplemente envuelven las interfaces de otros contenedores. STL En pila y cola, deque es utilizado por defecto, por ejemplo:

 

 

 8. Introducción al deque

1. Introducción al principio de deque

deque (cola de doble extremo): es una estructura de datos espaciales "continua" de doble apertura. El significado de doble apertura es que las operaciones de inserción y eliminación se pueden realizar en ambos extremos y la complejidad del tiempo es O (1), que es lo mismo que en comparación con el vector, la inserción de encabezados es más eficiente y no requiere elementos móviles; en comparación con la lista, la utilización del espacio es relativamente alta.

 

Deque no es un espacio verdaderamente continuo, sino que se compone de pequeños espacios continuos. El deque real es similar a una matriz dinámica bidimensional. Su estructura subyacente se muestra en la siguiente figura:

La capa inferior de la cola de dos extremos es un espacio continuo imaginario, que en realidad está segmentado. Para mantener su "continuidad general" y la ilusión de acceso aleatorio, cae sobre el iterador de deque, por lo que el diseño del iterador de deque es más complicado Como se muestra a continuación :

 

 

 

2. Defectos de deque

En comparación con el vector, las ventajas de deque son :

  • Al insertar y quitar el cabezal no es necesario mover elementos, lo cual es muy eficiente, y al expandirlo no es necesario mover una gran cantidad de elementos, por lo que su eficiencia debe ser alta.
  • En comparación con la lista, la capa inferior es un espacio continuo, la 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 al atravesar, el iterador de deque necesita detectar con frecuencia si se mueve hasta el límite de un determinado espacio pequeño, lo que resulta en una baja eficiencia. En escenarios secuenciales, es posible que se requiera un recorrido frecuente, por lo que en la práctica, es necesario Cuando se trata de estructuras lineales, el vector y la lista tienen prioridad en la mayoría de los casos. No hay muchas aplicaciones de deque. Una aplicación que se puede ver hasta ahora es que STL lo usa como la estructura de datos subyacente de la pila y la cola. .

3. ¿Por qué elegir deque como contenedor predeterminado subyacente para la pila y la cola ?

La pila es una estructura de datos lineal especial con función de último en entrar, primero en salir. Por lo tanto, cualquier estructura lineal con operaciones push_back() y pop_back() se puede utilizar como contenedor subyacente de la pila, como vector y lista; la cola es una función especial primero en entrar, primero en salir. Las estructuras de datos lineales, siempre que tengan operaciones push_back y pop_front, se pueden utilizar como contenedor subyacente de la cola, como la lista. Sin embargo, deque se selecciona como contenedor subyacente de forma predeterminada para la pila y la cola en STL, principalmente porque:
1. No es necesario atravesar la pila y la cola (por lo que la pila y la cola no tienen iteradores), solo necesitan operar en uno o ambos extremos fijos.
2. Cuando los elementos en la cola 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 es eficiente, sino que también tiene una alta uso de memoria. Combina las ventajas del deque y evita perfectamente sus deficiencias.

9. Funtor

1. El concepto de functor

Functor, también llamado objeto de función, es en realidad una estructura o clase que sobrecarga el operador (). Dado que el operador () está sobrecargado, usarlo es como llamar a una función, por lo que se denomina función "falsa".

2. Ejemplos de cómo escribir functores

    template<class T>
	struct Less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	struct Greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

3. Categorías de funtores

En el archivo de encabezado funcional de C++, se nos han proporcionado algunos functores que se pueden usar directamente.

1. Functor aritmético

1.plus calcula la suma de dos números

    transformar(comienzo(a), final(a), comienzo(b), comienzo(a), más<int>());

2.menos resta dos números

transformar(comienzo(a), final(a), comienzo(b), comienzo(a), menos<int>());

3.multiplica dos números multiplicados entre sí

transformar(comienzo(a), final(a), comienzo(b), comienzo(a), multiplica<int>());

4.divides divide dos números

transformar(comienzo(a), final(a), comienzo(b), comienzo(a), divide<int>());

Operación del módulo de cinco módulos.

transformar(comienzo(a), final(a), comienzo(b), comienzo(a), módulo<int>());

6.negar el número opuesto

transformar(comenzar(a), finalizar(a), comenzar(a), negar<int>());

2. Funtor de relación

  • 1.equal_to es igual
  • 2.not_equal_to no es igual
  • 3.mayor mayor que
  • 4.menos menos que
  • 5.greater_equal es mayor o igual a
  • 6.less_equal es menor o igual a

 3. Funtor lógico

  • 1.lógico_y binario, busque &
  • 2.lógico_o binario, buscar |
  • 3.lógico_¡ni un yuan, por favor!

Supongo que te gusta

Origin blog.csdn.net/qq_67458830/article/details/132029337
Recomendado
Clasificación