Estruturas de Dados 1 - Min/Max Queue

Aula por Lúcio Cardoso

A estrutura de dados Min Queue é uma adaptação da queue do C++. Nela, podemos realizar as seguintes operações:

  • Adicionar um elemento de valor v atrás da fila
  • Remover o elemento da frente da fila
  • Responder qual o elemento de menor valor presente na fila

Descrição da Estrutura

Para armazenar as informações da estrutura, utilizaremos um deque. O deque é uma estrutura semelhante à queue, com a diferença de que podemos inserir/remover elementos dos dois lados da fila.

Em particular, cada posição do deque guardará um par de valores: O valor do elemento e o tempo em que foi adicionado (o primeiro elemento adicionado foi adicionado no tempo 1, e assim sucessivamente).

Função push(v)

A função push(v) insere um elemento de valor v na parte de trás da fila.

Seja v_0 o elemento atual no fundo da fila. Se v_0 \geq v, do momento em que v foi adicionado em diante, v_0 nunca será o valor mínimo na estrutura, pois v é menor e, por estar atrás de v_0, será removido em algum tempo depois de v_0. Logo, antes de adicionar v, iremos remover todos os elementos v_0 do fundo da fila enquanto v_0 \geq v. Após isso, vamos inserir o par (v, tempo) no fundo do deque.

Perceba que, dessa forma, a Min Queue estará sempre ordenada de maneira crescente, e, por tanto, o menor elemento na estrutura será sempre o elemento na frente da fila. Agora, como removemos elementos da estrutura, precisamos de um cuidado adicional na função pop().

Função pop()

A função pop() remove o elemento presente na frente da fila.

Perceba que, na função push(), removemos elementos do fundo da fila, e portanto, é possível que o elemento que deveria estar na frente da fila ao executarmos o comando pop() já tenha sido removido.

Para conferir isto, iremos guardar duas variáveis fora da estrutura: l e r. Estas variáveis indicam, respectivamente, o tempo em que o elemento na frente da fila foi inserido (mesmo se este já foi removido) e o tempo em que será adicionado o próximo elemento no fundo na fila. Assim, é muito simples remover um elemento: Basta checar se o tempo do elemento na frente do deque é igual a l. Se sim, removemos ele, senão, o elemento da frente já foi removido antes e não fazemos nada.

Para atualizar as variáveis l e r, basta somar um no valor de r cada vez que adicionarmos um elemento com a função push() e somar um em l a cada vez que removemos um elemento da frente da fila.

Função getmin()

A função getmin() retorna o elemento de menor valor presente na fila.

Como já dito anteriormente, a estrutura foi mantida de tal forma que o elemento mínimo será aquele na frente da fila. Logo, para retornar o menor elemento, basta retornamos o valor do elemento na frente do deque.

Complexidade

Inicialmente, sabemos que as funções getmin() e pop() possuem complexidade O(1). Além disso, cada elemento da estrutura foi removido/adicionado no máximo uma vez. Logo, a complexidade final após todas as operações terem sido realizadas será O(n), onde n é o tamanho da fila. Assim, dizemos que cada operação push() terá complexidade amortizada O(1).

Confira o código final da Min Queue, para melhor entendimento:


// Curso Noic de Informática - Estruturas de Dados 11
// Minqueue - Complexidade O(1) amortizada
// A Minqueue pode facilmente ser adaptada para Maxqueue, respondendo
// o valor máximo na estrutura
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
struct MinQueue
{
deque<pii> dq;
int l, r;
// inicialmente, dizemos que l = r = 1
void init(void)
{
l = r = 1;
dq.clear();
}
// adiciona v atrás da fila
void push(int v)
{
while (!dq.empty() && v < dq.back().ff) dq.pop_back();
dq.push({v, r}); // inserimos v com tempo r
r++; // aumentamos o tempo r
}
// remove o elemento da frente da fila
void pop(void)
{
if (!dq.empty() && dq.front().ss == l) dq.pop_front();
l++; //aumentamos o tempo l
}
int getmin(void) {return dq.front().ff;} // retorna o valor mínimo da estrutura
};

view raw

noic-ds11.cpp

hosted with ❤ by GitHub