Índice
1. Stack_required para perguntas de escovação
1. O que é um adaptador de contêiner
2. A estrutura subjacente de pilha e fila na biblioteca padrão STL
Suplemento de compreensão: Container - deque
2. Por que escolher deque como o contêiner padrão subjacente de pilha e fila
<2>. Implementação de simulação
Compreensão do uso do funtor na função de classificação
4. Iterador reverso (tome a lista como exemplo)
2. Um pequeno detalhe sobre a documentação do iterador
1. Stack_required para perguntas de escovação
Interface comum:
stack() cria uma pilha vaziavazio() detecta se a pilha está vaziasize() retorna o número de elementos na pilhatop() retorna uma referência ao elemento superior da pilhapush() empurra o elemento val para a pilhapop() exibe o elemento no final da pilha
Aplicação simples, tire algumas dúvidas:
Empilhar sequências push e pop Revelar papel
150. Avaliação reversa da expressão polonesa
Dois, implementação de pilha
Ideia: Durante o período da linguagem C, podemos realizar a pilha por meio da lista vinculada e do formato de array , e o formato de array é mais eficiente, de modo que a implementação da pilha pode reutilizar diretamente a interface vetorial e empacotá-la no primeiro - recurso in-last-out da pilha .
#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;
};
}
Simples, não é? Não termina aqui, descobrimos que nossa pilha só pode ser realizada através de vetor. De acordo com a biblioteca padrão C++, ainda falta uma implementação de adaptador, ou seja, a pilha que implementamos não suporta adaptadores de contêiner .
1. O que é um adaptador de contêiner
2. A estrutura subjacente de pilha e fila na biblioteca padrão STL
![](https://img-blog.csdnimg.cn/4abcd57714854769984879c4110a81d8.png)
Voltando à implementação de nossa pilha, oferecemos suporte apenas ao design do modo vetorial e o otimizamos em um adaptador de contêiner que suporta todos os contêineres e pode suportar o recurso primeiro a entrar e último a sair da pilha. (na minha opinião esta ideia é mais 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 eles, o que é deque?
Suplemento de compreensão: Container - deque
![](https://img-blog.csdnimg.cn/980122a7ffde4e21b77625dc333c8578.png)
![](https://img-blog.csdnimg.cn/dc120c32e2aa4ee9a9db9256666be200.png)
Como o deque mantém sua hipotética estrutura contínua com a ajuda de seu iterador?
1. O defeito de deque
Vantagem:
Comparado com vector , a vantagem do deque é: a eficiência de inserção e exclusão de cabeçalho é alta. Não há necessidade de mover elementos e a eficiência é particularmente elevada e, ao expandir, não há necessidade de mover um grande número de elementos , pelo que a sua eficiência deve ser elevada.
Comparado com a lista , vantagens : suporte ao acesso aleatório . A camada inferior é um espaço contínuo e a taxa de utilização do espaço é relativamente alta e não há necessidade de armazenar campos adicionais.
defeito:
Não é adequado para travessia.Porque ao percorrer, os iteradores deque precisam calcular frequentemente a posição de cada dado , resultando em baixa eficiência , e em cenários sequenciais, a travessia frequente pode ser necessária, portanto, na prática, quando uma estrutura linear é necessária, é preferível na maioria dos casos. vetor e lista, não há muitas aplicações de deque , e uma aplicação que pode ser vista até agora é que o STL o usa como estrutura de dados subjacente de pilha e fila .
2. Por que escolher deque como o contêiner padrão subjacente de pilha e fila
1. A pilha e a fila não precisam ser percorridas (portanto, a pilha e a fila não têm iteradores) e só precisam operar em uma ou ambas as extremidades do fixo.2. Quando os elementos da pilha crescem, o deque é mais eficiente que o vetor (não há necessidade de mover muitos dados ao expandir), quando os elementos da fila crescem, o deque não é apenas eficiente, mas também tem alto uso de memória.
Três, implementação de fila
1. Fila normal
Implementação da fila, desta vez usamos o adaptador de contêiner.
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, fila prioritária (difícil)
Introdução:
<1>.Função
Priority_queue()/priority_queue(fifirst, last) Construa uma fila de prioridade vaziavazio() Verifica se a fila de prioridade está vazia, retorna verdadeiro se estiver, caso contrário retorna falsotop() retorna o maior (menor elemento) na fila de prioridade, ou seja, o elemento superior do heappush(x) insere o elemento x na fila de prioridadepop() exclui o maior (menor) elemento da fila de prioridade, ou seja, o elemento superior do heappop_back() remove elementos no final do contêiner
<2>. Implementação de simulação
Para completar a fila de prioridade, você precisa usar o conhecimento do heap.Se você não estiver familiarizado com o heap, é recomendável revisar o conteúdo da implementação do heap:
Use o conhecimento do heap para completar a estrutura mais básica:
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;
};
}
Aqui temos uma pergunta: o que devemos fazer se quisermos construir uma ordem crescente? O functor irá 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).Funtor
Neste cenário, nossa fila de prioridade já implementou a função de ordem decrescente, então como implementamos a ordem crescente? O que você diria para escrever outro parágrafo e mudar os símbolos? ?
Aqui tem que introduzir o functor, o functor, então não é uma função, o que é? (Funtores como: menos, maior)
O functor é essencialmente uma classe . De acordo com o modelo de comparação na figura acima, o functor aqui é usado para alterar de forma flexível o tamanho do símbolo de heap .
Implementamos o resultado diretamente:
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;
}
};
Essas duas classes estão sobrecarregadas com operador(), então após instanciar o objeto, use:
modelo <class T,class compare = less<T>>
compare t;
cout << t(1, 2) << endl;
Visto de fora, parece uma chamada de função, mas na verdade é uma chamada para uma função membro de uma classe
t.operador(1,2)
Desta forma, podemos alterar diretamente a classe do functor para controlar o tamanho do heap.
Compreensão do uso do funtor na função de classificação
No modelo de função :
A função sort é usada como parâmetro de linha :
sort(s.begin, s.end, maior<int>() ); // A função é para ser um parâmetro, adicionamos parênteses para se tornar um objeto anônimo
4. Iterador reverso (tome a lista como exemplo)
Se você tem amigos que ainda estão aprendendo iteradores comuns, consulte a implementação de iteradores comuns neste artigo.
[STL] lista de uso e implementação try-out_underlying_Huaguoshan ~~ Blog do programador-Blog CSDN
Referindo-se ao código-fonte da lista, aqui está o resultado direto e descobriu que o código-fonte constrói um iterador reverso pegando emprestado um iterador comum .
Vá diretamente para o 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其他成员函数这里就不再赘述了
A ideia do design é relativamente simples. É essencialmente uma função que reutiliza iteradores comuns. A ideia de outras funções sobrecarregadas é semelhante à das funções comuns. Mas aqui está também um design mais artístico:
Então vamos discutir aqui, esse iterador reverso pode ser usado para vetores? ? a resposta é sim
Olha a foto:
Conclusão: Iteradores reversos: adaptadores para iteradores.
2. Um pequeno detalhe sobre a documentação do iterador
Todos os recipientes são adequados?
Não necessariamente, porque o iterador comum do contêiner deve pelo menos suportar a interface ++, -- (por exemplo: forward_list não suporta --, portanto não possui um iterador reverso)
Aqui estão alguns suplementos sobre o uso de documentos [STL], que são divididos em três categorias do ponto de vista das funções do iterador :
1. Suporte a forward_iterator (iterador unidirecional) ——> ++ Por exemplo: forward_list, etc.
2. bidirecional_iterador (iterador bidirecional) --> ++ -- como: lista, etc.
3. radom_access_iterator (iterador aleatório) --> ++ -- + - Por exemplo: vetor, deque, etc., o terceiro tipo de herança de iterador 1, 2
Qual é o significado disso? ?
Significado: É um lembrete de que, ao usar um iterador, a interface solicitará o tipo de iterador apropriado.
epílogo
Esta seção está aqui, obrigado amigos por navegarem, se vocês tiverem alguma sugestão, sejam bem vindos a comentar na área de comentários, se vocês trouxerem algum ganho para seus amigos, por favor deixem seus likes, seus gostos e preocupações se tornarão blogueiros A força motriz da criação do mestre .