Tabla de contenido
1. Stack_required para preguntas sobre cepillado
1. ¿Qué es un adaptador de contenedor?
2. La estructura subyacente de pila y cola en la biblioteca estándar STL
Suplemento de comprensión: Contenedor - deque
2. ¿Por qué elegir deque como contenedor predeterminado subyacente de pila y cola?
<2> Implementación de simulación
Comprensión del uso de funtores en la función de clasificación
4. Iterador inverso (tome la lista como ejemplo)
2. Un pequeño detalle sobre la documentación del iterador.
1. Stack_required para preguntas sobre cepillado
Interfaz común:
stack() crea una pila vacíavacío() detecta si la pila está vacíasize() devuelve el número de elementos en la pilatop() devuelve una referencia al elemento superior de la pilapush() empuja el elemento val a la pilapop() muestra el elemento al final de la pila
Aplicación sencilla, cepille algunas preguntas:
Apilar secuencias de empujar y hacer estallar Desarrollar papel
150. Evaluación de expresión polaca inversa
Dos, implementación de pila
Idea: durante el período del lenguaje C, podemos implementar la pila a través de una lista vinculada y una forma de matriz , y la forma de matriz es más eficiente, por lo que la implementación de la pila puede reutilizar directamente la interfaz vectorial y empaquetarla en la primera. Característica de entrada y salida de la pila .
#pragma once
#include <iostream>
#include <vector>
#include <list>
using namespace std;
namespace my_s_qu
{
template <class T >
class stack
{
public:
void push_back(const T& x)
{
Data.push_back(x);
}
void Pop()
{
Data.pop_back();
}
T& top()
{
return Data.back();
}
const T& top() const
{
return Data.back();
}
bool empty() const
{
return Data.empty();
}
size_t size() const
{
return Data.size();
}
private:
vector<T> Data;
};
}
Sencillo ¿no? Esto no termina aquí, descubrimos que nuestra pila solo se puede implementar a través de vectores. Según la biblioteca estándar de C ++, todavía nos falta una implementación de adaptador, es decir, la pila que implementamos no admite adaptadores de contenedor .
1. ¿Qué es un adaptador de contenedor?
2. La estructura subyacente de pila y cola en la biblioteca estándar STL
Volviendo a la implementación de nuestra pila, solo admitimos el diseño en modo vectorial y lo optimizamos en un adaptador de contenedor que admite todos los contenedores y puede admitir la característica de primero en entrar y último en salir de la pila. (en mi opinión esta idea es más importante)
namespace my_s_qu
{
template <class T, class container = deque<T> > // 添加容器模板
{
public:
void push_back(const T& x)
{
Data.push_back(x);
}
......
private:
container Data; // 容器换成模板
};
}
Entre ellos, ¿qué es deque?
Suplemento de comprensión: Contenedor - deque
¿Cómo mantiene deque su hipotética estructura continua con la ayuda de su iterador?
1. El defecto del deque
Ventaja:
Comparado con vector , la ventaja de deque es: la eficiencia de la inserción y eliminación de cabezales es alta. No es necesario mover elementos y la eficiencia es particularmente alta . Al expandirse, no es necesario mover una gran cantidad de elementos , por lo que su eficiencia debe ser alta.
En comparación con la lista , ventajas : admite acceso aleatorio . La capa inferior es un espacio continuo, la tasa de utilización del espacio es relativamente alta y no es necesario almacenar campos adicionales.
defecto:
No apto para travesías.Debido a que al atravesar, los iteradores deque necesitan calcular con frecuencia la posición de cada dato , lo que resulta en baja eficiencia . En escenarios secuenciales, es posible que se requiera un recorrido frecuente, por lo que en la práctica, cuando se requiere una estructura lineal, se prefiere en la mayoría de los casos. vector y lista, no hay muchas aplicaciones de deque , y una aplicación que se puede ver hasta ahora es que STL lo usa como estructura de datos subyacente de pila y cola .
2. ¿Por qué elegir deque como contenedor predeterminado subyacente de pila y cola?
1. No es necesario atravesar la pila y la cola (por lo que la pila y la cola no tienen iteradores) y solo necesitan 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 muchos datos al expandirse); cuando los elementos en la cola crecen, deque no solo es eficiente, sino que también tiene un alto uso de memoria.
Tres, implementación de cola
1. Cola ordinaria
Implementación de cola, esta vez usamos el adaptador de contenedor.
template <class T, class container = deque<T>>
class queue
{
public:
void push_back(const T& x)
{
Data.push_back(x);
}
void Pop()
{
Data.pop_front();
}
T& back()
{
return Data.back();
}
const T& back() const
{
return Data.back();
}
T& front()
{
return Data.front();
}
const T& front() const
{
return Data.front();
}
bool empty() const
{
return Data.empty();
}
size_t size() const
{
return Data.size();
}
private:
container Data;
};
2, cola prioritaria (difícil)
Introducción:
<1>.Función
Priority_queue()/priority_queue(fifirst, last) Construye una cola de prioridad vacíavacío () Comprueba si la cola de prioridad está vacía, devuelve verdadero si lo está; de lo contrario, devuelve falsotop() devuelve el elemento más grande (el más pequeño) en la cola de prioridad, es decir, el elemento superior del montónpush(x) inserta el elemento x en la cola de prioridadpop() elimina el elemento más grande (más pequeño) en la cola de prioridad, es decir, el elemento superior del montónpop_back() elimina elementos al final del contenedor
<2> Implementación de simulación
Para completar la cola de prioridad, debe utilizar el conocimiento del montón. Si no está familiarizado con el montón, se recomienda revisar el contenido de implementación del montón:
Utilice el conocimiento del montón para completar el marco más básico:
namespace my_priority_queue
{
template <class T, class container = vector<T>>
class priority_queue
{
public:
// 自定义类型,不需要给他初始化
void ajust_up(size_t child)
{
int parent;
while (child > 0)
{
parent = child / 2;
if (_pri_queue[child] > _pri_queue[parent]) // 写大堆
{
std::swap(_pri_queue[child], _pri_queue[parent]);
child = parent;
parent = child / 2;
}
else
{
break;
}
}
}
T& top()
{
return _pri_queue[0];
}
bool empty()
{
return _pri_queue.empty();
}
void push(const T& x)
{
_pri_queue.push_back(x);
// 向上调整
ajust_up(_pri_queue.size() - 1);
}
size_t size()
{
return _pri_queue.size();
}
void ajust_down()
{
size_t parent = 0;
size_t child = 2 * parent + 1;
while (child < _pri_queue.size())
{
if (child + 1 < _pri_queue.size() && _pri_queue[child + 1] > _pri_queue[child])
{
child++;
}
if (_pri_queue[parent] < _pri_queue[child])
{
std::swap(_pri_queue[parent], _pri_queue[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void pop()
{
std::swap(_pri_queue[0], _pri_queue[size() - 1]);
// 向下调整
_pri_queue.pop_back();
ajust_down();
}
private:
container _pri_queue;
};
}
Aquí tenemos una pregunta, ¿qué debemos hacer si queremos construir un orden ascendente? El funtor interpretará
1) Usando iterator_construct
// 自定义类型,不需要给他初始化
priority_queue()
{}
template <class newiterator>
priority_queue(newiterator begin, newiterator end)
: _pri_queue()
{
while (begin != end)
{
push(*begin);
begin++;
}
}
2) Functor
En este escenario, nuestra cola de prioridad ya ha implementado la función de orden descendente, entonces, ¿cómo implementamos el orden ascendente? ¿Qué dices de escribir otro párrafo y cambiar los símbolos? ?
Aquí hay que introducir el funtor, el funtor, entonces no es una función, ¿qué es? (Funtores como: menor, mayor)
El funtor es esencialmente una clase . Según la plantilla de comparación en la figura anterior, el funtor aquí se usa para cambiar de manera flexible el tamaño del símbolo del montón .
Implementamos el resultado directamente:
template <class T>
struct less
{
bool operator()(const T& left, const T& right)const
{
return left < right;
}
};
template <class T>
struct greater
{
bool operator()(const T& left, const T& right)const
{
return left > right;
}
};
Estas dos clases están sobrecargadas con operator(), por lo que después de crear una instancia del objeto, use:
plantilla <clase T, comparación de clases = menos<T>>
comparar t;
cout << t(1, 2) << endl;
Desde fuera parece una llamada a una función, pero en realidad es una llamada a una función miembro de una clase.
t.operador(1,2)
De esta forma, podemos cambiar directamente la clase del functor para controlar el tamaño del montón.
Comprensión del uso de funtores en la función de clasificación
En la plantilla de función :
La función de clasificación se utiliza como parámetro de línea :
sort(s.begin, s.end, major<int> ()); // La función debe ser un parámetro, agregamos paréntesis para convertirla en un objeto anónimo
4. Iterador inverso (tome la lista como ejemplo)
Si tiene amigos que todavía están aprendiendo iteradores ordinarios, consulte la implementación de iteradores ordinarios en este artículo.
[STL] lista de uso y prueba_implementación subyacente_Huaguoshan ~~ Blog del programador-Blog CSDN
Refiriéndose al código fuente de la lista, aquí está el resultado directamente y descubrí que el código fuente construye un iterador inverso tomando prestado un iterador común .
Vaya directamente al código:
namespace my_list
{
template <class T>
struct list_node
{
list_node(const T& data = T())
: _data(data)
, _next(nullptr)
, _prv(nullptr)
{}
T _data;
list_node* _next;
list_node* _prv;
};
template <class T, class Ref, class Ptr>
struct list_iterator
{
typedef list_node<T> Node;
typedef list_iterator< T, Ref, Ptr> iterator;
Node* _node;
list_iterator(Node* node)
: _node(node)
{}
bool operator!= (const iterator& it)
{
return _node != it._node;
}
bool operator==(const iterator& it)
{
return _node == it._node;
}
iterator& operator++()
{
_node = _node->_next;
return *this;
}
iterator& operator--()
{
_node = _node->_prv;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
_node = _node->_next;
return *tmp;
}
Ptr operator*()
{
return _node->_data;
}
Ref operator->()
{
return &(operator*());
}
};
template <class Iterator, class Ref, class Ptr>
struct _reverse_iterator
{
typedef _reverse_iterator<Iterator, Ref, Ptr> reverse_iterator;
Iterator _cur;
_reverse_iterator(const Iterator& cur)
: _cur(cur)
{}
reverse_iterator& operator++()
{
--_cur;
return *this;
}
reverse_iterator operator++(int)
{
reverse_iterator temp(*this);
--_cur;
return temp;
}
reverse_iterator& operator--()
{
++_cur;
return _cur;
}
reverse_iterator operator--(int)
{
reverse_iterator temp(*this);
++_cur;
return temp;
}
// !=
bool operator!=(const reverse_iterator& end)
{
return _cur != end._cur;
}
bool operator==(const reverse_iterator& end)
{
return _cur == end._cur;
}
// *
Ptr operator*()
{
auto tmp = _cur;
--tmp;
return *tmp;
}
// ->
Ref operator->()
{
return &(operator*());
}
};
template <class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T, T*, T&> iterator;
typedef list_iterator<T, const T*, const T&> const_iterator;
typedef _reverse_iterator<iterator, T*, T&> reverse_iterator;
typedef _reverse_iterator<const_iterator, const T*, const T&> const_reverse_iterator;
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
..... //list其他成员函数这里就不再赘述了
La idea de diseño es relativamente simple: es esencialmente una función que reutiliza iteradores ordinarios, y la idea de otras funciones sobrecargadas es similar a la de las funciones ordinarias. Pero aquí también hay un diseño más artístico:
Entonces analicemos aquí: ¿se puede usar este iterador inverso para vectores? ? la respuesta es sí
Mira la foto:
Conclusión: Iteradores inversos: adaptadores para iteradores.
2. Un pequeño detalle sobre la documentación del iterador.
¿Todos los contenedores son adecuados?
No necesariamente, porque el iterador ordinario del contenedor debe al menos admitir la interfaz ++, -- (por ejemplo: forward_list no admite --, por lo que no tiene un iterador inverso)
Aquí hay algunos suplementos sobre el uso de documentos [STL], que se dividen en tres categorías desde la perspectiva de las funciones de iterador :
1. Compatibilidad con forward_iterator (iterador unidireccional) ——> ++ Por ejemplo: forward_list, etc.
2. bidireccional_iterator (iterador bidireccional) --> ++ - como: lista, etc.
3. radom_access_iterator (iterador aleatorio) --> ++ -- + - Por ejemplo: vector, deque, etc., el tercer tipo de herencia de iterador 1, 2
¿Cuál es el significado de eso? ?
Importancia: es un recordatorio de que cuando utilice un iterador, la interfaz le indicará el tipo de iterador apropiado.
epílogo
Esta sección terminó aquí, gracias amigos por navegar, si tienen alguna sugerencia, bienvenidos a comentar en el área de comentarios, si aportan algunas ganancias a sus amigos, dejen sus me gusta, sus me gusta y sus inquietudes se convertirán en bloggers. La fuerza impulsora de la creación del maestro .