Endereço do evento: Desafio de aprendizado de 21 dias da CSDN
Diretório de artigos
Leetcode622 - Desenhar Fila Circular
descrição do tópico
Projete sua implementação de fila circular. Uma fila circular é uma estrutura de dados linear cujo funcionamento é baseado no princípio FIFO (first in first out) e o final da fila é conectado após o início da fila para formar um loop. Também é conhecido como "buffer de anel".
Um benefício das filas circulares é que podemos utilizar o espaço usado anteriormente nessa fila. Em uma fila normal, uma vez que a fila está cheia, não podemos inserir o próximo elemento, mesmo que ainda haja espaço na frente da fila. Mas com uma fila circular, podemos usar esse espaço para armazenar novos valores.
Link : https://leetcode.cn/problems/design-circular-queue
Sua implementação deve suportar as seguintes operações:
MyCircularQueue(k): Construtor, defina o comprimento da fila para k.
Front: Obtém elementos da frente da fila. Retorna -1 se a fila estiver vazia.
Rear: Pegue o elemento no final da fila. Retorna -1 se a fila estiver vazia.
enQueue(value): Insere um elemento na fila circular. Retorna verdadeiro se a inserção foi bem-sucedida.
deQueue(): Remove um elemento da fila circular. Retorna verdadeiro se a exclusão foi bem-sucedida.
isEmpty(): Verifica se a fila circular está vazia.
isFull(): Verifica se a fila circular está cheia.
Exemplo :
MyCircularQueue circularQueue = new MyCircularQueue(3); // Define o comprimento para 3
circularQueue.enQueue(1); // Retorna true
circularQueue.enQueue(2); // Retorna true
circularQueue.enQueue(3); // Retorna true
circularQueue enQueue(4); // retorna false, a fila está cheia
circularQueue.Rear(); // retorna 3
circularQueue.isFull(); // retorna true
circularQueue.deQueue(); // retorna true
circularQueue.enQueue(4 ); // retorna true
circularQueue.Rear(); // retorna 4
Dicas :
- Todos os valores estão no intervalo de 0 a 1000;
- Os operandos estarão no intervalo de 1 a 1000;
- Não use a biblioteca de filas integrada.
padrão de código principal
typedef struct {
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
}
int myCircularQueueFront(MyCircularQueue* obj) {
}
int myCircularQueueRear(MyCircularQueue* obj) {
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
}
void myCircularQueueFree(MyCircularQueue* obj) {
}
/**
* Your MyCircularQueue struct will be instantiated and called as such:
* MyCircularQueue* obj = myCircularQueueCreate(k);
* bool param_1 = myCircularQueueEnQueue(obj, value);
* bool param_2 = myCircularQueueDeQueue(obj);
* int param_3 = myCircularQueueFront(obj);
* int param_4 = myCircularQueueRear(obj);
* bool param_5 = myCircularQueueIsEmpty(obj);
* bool param_6 = myCircularQueueIsFull(obj);
* myCircularQueueFree(obj);
*/
Análise de ideias e implementação de código (linguagem C)
Não é muito bom usar uma lista de encadeamento único. Por exemplo, se você deseja obter os elementos no final da fila, deve percorrê-la novamente, mas pode usar diretamente o subscrito -1 no final de a fila com a tabela de sequência. Além disso, a criação de uma lista de encadeamento único é mais problemática do que a tabela de sequência e você precisa usá-los um por um. Os nós são criados e todos estão vinculados. É melhor usar a tabela de sequência dinâmica.
A inserção ou exclusão de elementos na fila circular não altera o espaço, adotando-se a estratégia de sobrescrever. Na verdade, a chave para esta questão é determinar com precisão se está vazio ou cheio. Aqui deixamos o ponto traseiro apontar para a próxima posição do elemento no final da fila, e então surge o problema. Não podemos distinguir os dois estados de vazio e cheio. Por que?
Conforme mostrado na figura, tanto o ponteiro do início da fila quanto o ponteiro do final da fila apontam para a mesma posição nos estados vazio e cheio.
Solução :
- Incrementar um tamanho para contar
- Adicione um espaço, e sempre deixe um lugar quando estiver cheio, por exemplo, se a fila circular tiver 4 elementos, serão abertos 5 espaços.
A próxima posição da posição traseira é a frente, que é o estado cheio, e a frente e a traseira apontam para a mesma posição, que é o estado vazio. Porém, vale ressaltar que essa é a conclusão da estrutura lógica. Como mostra a figura, a fila da estrutura lógica está cheia, mas utilizamos uma tabela de sequência dinâmica para realizar a fila circular, mas a estrutura física é outra forma. Nossas operações devem ser baseadas na estrutura física.
Como mostra a figura, devemos prestar atenção em como lidar com isso, deixar o subscrito traseiro + 1 %N, N representa o número total de espaços (incluindo um espaço extra) e julgar se o resultado é igual ao da frente subscrito.
Declaração da estrutura e criação da fila
A lista circular é implementada usando uma tabela de sequência dinâmica, definindo o subscrito da cabeça e da cauda da fila e um N representando o número total de espaços abertos pela tabela de sequência dinâmica (mais um).
Crie uma fila para solicitar espaço no heap, assim como a tabela de sequência dinâmica, abra um espaço maior que o k fornecido e retorne o ponteiro da fila após a conclusão da inicialização.
typedef struct {
int* arr;
int front;
int rear;
int N;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->arr = (int*)malloc((k+1)*sizeof(int));
obj->front = obj->rear = 0;
obj->N = k + 1;
return obj;
}
Se a frente
e a retaguarda forem iguais, significa que a fila está vazia, e se o juízo estiver cheio, há duas situações que devem ser observadas, que foram citadas acima.
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->rear == obj->front;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return ((obj->rear+1) % obj->N) == obj->front;
}
Se a fila do elemento
estiver cheia, não será permitido colocá-la e false será retornado. Se não estiver cheia, o elemento será colocado diretamente na posição traseira. Observe que você deve se lembrar de aguardar obj- >N. Isso é para controlar que, se a parte traseira atingir o final do espaço, ela poderá retornar à posição do subscrito 0, de modo a realizar o loop.
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->arr[obj->rear] = value;
++obj->rear;
obj->rear %= obj->N;
return true;
}
Se o elemento estiver fora da fila
, ele não estará fora da fila e retornará falso. Se não estiver vazio, a frente será movida um bit para frente. Observe que você deve se lembrar de esperar por obj->N. Isso é para controlar que, se a parte traseira atingir o final do espaço, ela possa retornar à posição do subscrito 0, para realizar o loop.
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
++obj->front;
obj->front %= obj->N;
return true;
}
Obtenha primeiro o elemento de cabeçalho da fila
para ver se a fila está vazia, se estiver vazia, retorne -1, se não estiver vazia, apenas retorne o elemento de cabeçalho da fila diretamente.
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->arr[obj->front];
}
Para obter os elementos no final da fila,
primeiro verifique se a fila está vazia. Se estiver vazia, retorne -1. Se não estiver vazia, há duas situações a serem observadas: traseira é 0 e traseira não é 0.
Existem duas formas de escrever:
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else if(obj->rear == 0)
return obj->arr[obj->N - 1];
else
return obj->arr[obj->rear - 1];
}
Existem duas estruturas que são criadas dinamicamente destruindo e liberando a fila , uma é uma estrutura de fila e a outra é uma matriz dinâmica.A matriz deve ser liberada primeiro e depois a estrutura.
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->arr);
obj->arr = NULL;
obj->front = obj->rear = obj->N = 0;
free(obj);
}