1. Introdução às pilhas e filas
Pilhas e filas são duas estruturas lineares importantes. Do ponto de vista da estrutura de dados, elas também são tabelas lineares. Sua particularidade é que suas operações básicas são um subconjunto de tabelas lineares, ou seja, tabelas lineares com funções limitadas. Elas também são chamadas É chamada de estrutura de dados restrita.
Mas, do ponto de vista dos tipos de dados, eles não são exatamente iguais às tabelas lineares e, às vezes, são usados como regra para gerenciar dados.
2. Estrutura da pilha
1. Introdução à estrutura de pilha
A pilha (stack) é limitada a operações de inserção ou exclusão e tabelas lineares apenas no final da tabela (apenas uma porta pode entrar e sair de dados). Para a pilha, o final e a cabeça da tabela têm significados especiais. A cauda da tabela é chamada de topo da pilha, e a tabela A cabeça é chamada de base da pilha , uma lista vazia sem elementos é chamada de pilha vazia , e o número de elementos atingindo a capacidade da pilha é chamado de full stack . Adicionar dados à pilha é chamado de empurrar e empurrar , e excluir dados da pilha é chamado de popping e popping. Stack , devido às regras especiais de adição e exclusão de elementos da pilha, o fenômeno de que os elementos da pilha serão primeiro a entrar, último a sair, conhecido como LIFO (Último a entrar, primeiro a sair).
2. Funções da estrutura da pilha
1. Crie uma pilha
2. Destrua a pilha
3. A pilha está vazia?
4. A pilha está cheia?
5. Empurre para a pilha
6. Saia
8. Visualize o elemento superior da pilha
9. Número de elementos empilhados
Nota: Somente a estrutura de pilha sequencial precisa julgar se a pilha está cheia.
3. Representação de sequência e implementação de estrutura de pilha
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
typedef struct StackArray
{
TYPE* base;
int top;
size_t cap;
}StackArray;
// 创建栈
StackArray* create_stack(size_t cap)
{
// 之所以使用成员指针是为了兼顾笔试题
StackArray* stack = malloc(sizeof(StackArray));
stack->base = malloc(sizeof(TYPE)*cap);
stack->cap = cap;
// 初始值值决定的栈空、栈满、入栈、查看栈顶时的top的操作,
stack->top = -1;
return stack;
}
// 销毁栈
void destroy_stack(StackArray* stack)
{
free(stack->base);
free(stack);
}
// 判断栈是否为空
bool empty_stack(StackArray* stack)
{
// 因为top的初始值是-1
return 0 > stack->top;
}
// 判断栈是否为满
bool full_stack(StackArray* stack)
{
// 同上
return stack->cap == stack->top+1;
}
// 入栈
bool push_stack(StackArray* stack,TYPE val)
{
if(full_stack(stack))
return false;
// 同上
stack->base[++stack->top] = val;
return true;
}
// 出栈
bool pop_stack(StackArray* stack)
{
if(empty_stack(stack))
return false;
stack->top--;
return true;
}
// 计算栈元素数量
size_t size_stack(StackArray* stack)
{
return stack->top+1;
}
// 查看栈顶
bool top_stack(StackArray* stack,TYPE* ptr)
{
if(empty_stack(stack))
return false;
*ptr = stack->base[stack->top];
return true;
}
int main(int argc,const char* argv[])
{
int num;
StackArray* stack = create_stack(10);
for(int i=0; i<10; i++)
{
push_stack(stack,i);
if(top_stack(stack,&num))
printf("top %d\n",num);
}
printf("----------------------\n");
while(!empty_stack(stack))
{
top_stack(stack,&num);
printf("top %d\n",num);
pop_stack(stack);
}
}
Perguntas comuns de testes escritos :
Julgamento da sequência push e pop da pilha, sequência push: 1 2 3 4 5 se a sequência pop é: 1 2 3 5 4
bool push_pop_order(int* arr1,int* arr2,size_t len)
{
StackArray* stack = create_stack(len);
int num;
for(int i=0,j=0; i<len; i++)
{
push_stack(stack,arr1[i]);
while(top_stack(stack,&num) && num == arr2[j] && pop_stack(stack))
j++;
}
return empty_stack(stack);
}
bool IsPopOrder(int* pushV, int pushVLen, int* popV, int popVLen )
{
if(pushVLen != popVLen)
return false;
int stack[popVLen] , top = -1;
for(int i=0,j=0; i<pushVLen; i++)
{
stack[++top] = pushV[i];
while(top>=0 && stack[top] == popV[j])
{
top--;
j++;
}
}
return 0 > top;
}
bool IsPopOrder(vector<int> pushV,vector<int> popV)
{
stack<int> s;
for(int i=0,j=0; i<pushV.size(); i++)
{
s.push(pushV[i]);
while(!s.empty() && s.top()==popV[j])
{
s.pop();
j++;
}
}
return s.empty();
}
4. Representação em cadeia e implementação da estrutura de pilha
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
typedef struct Node
{
TYPE data;
struct Node* next;
}Node;
Node* create_node(TYPE data)
{
Node* node = malloc(sizeof(Node));
node->data = data;
node->next = NULL;
return node;
}
typedef struct StackList
{
Node* top;
size_t cnt;
}StackList;
// 创建栈
StackList* create_stack(void)
{
StackList* stack = malloc(sizeof(StackList));
stack->top = NULL;
stack->cnt = 0;
return stack;
}
// 销毁栈
void destroy_stack(StackList* stack)
{
while(NULL!=stack->top)
{
Node* node = stack->top;
stack->top = node->next;
free(node);
}
free(stack);
}
// 栈空
bool empty_stack(StackList* stack)
{
return NULL == stack->top;
}
// 入栈
void push_stack(StackList* stack,TYPE data)
{
Node* node = create_node(data);
node->next = stack->top;
stack->top = node;
stack->cnt++;
}
// 出栈
bool pop_stack(StackList* stack)
{
if(empty_stack(stack))
return false;
Node* node = stack->top;
stack->top = node->next;
free(node);
stack->cnt--;
return true;
}
// 查看栈顶
TYPE top_stack(StackList* stack)
{
return stack->top->data;
}
// 元素数量
size_t size_stack(StackList* stack)
{
return stack->cnt;
}
int main(int argc,const char* argv[])
{
StackList* stack = create_stack();
for(int i=0; i<10; i++)
{
push_stack(stack,i);
if(!empty_stack(stack))
printf("top %d\n",top_stack(stack));
}
printf("-------------------\n");
while(!empty_stack(stack))
{
printf("top %d\n",top_stack(stack));
pop_stack(stack);
}
}
5. Aplicação de pilha
1. O gerenciamento de memória, como a memória de pilha, é chamado de memória de pilha porque segue a regra do último a entrar, primeiro a sair da pilha. Ele suporta chamadas de função. Quando uma função passa parâmetros, ela envia os parâmetros para a memória de pilha primeiro , e espera por um salto.Após virar, os parâmetros são retirados da memória da pilha.
2. Algoritmos especiais, tais como: conversão de base, análise de expressão, resolução de labirinto.
3. Fila
1. Introdução à Fila
É exatamente o oposto da pilha. É uma tabela linear primeiro a entrar, primeiro a sair. Possui duas portas para adicionar e excluir elementos. Uma porta só pode adicionar elementos, o que é chamado de entrada na fila. Essa porta é chamada o fim da fila, e a outra porta só pode ser excluída, que é chamada de desenfileiramento, e a porta é chamada de chefe da equipe.
2. As funções da estrutura da fila
1. Crie uma fila
2. Destrua a fila
3. A fila está vazia?
4. A fila está cheia?
5. Junte-se à equipe
6. Saia
8. Verifique o elemento principal da fila
9. Visualize os elementos no final da fila
10. Número de elementos da fila
Nota: Somente a estrutura de pilha sequencial precisa julgar se a fila está cheia.
3. Representação da cadeia e implementação da estrutura da fila
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
typedef struct Node
{
TYPE data;
struct Node* next;
}Node;
Node* create_node(TYPE data)
{
Node* node = malloc(sizeof(Node));
node->data = data;
node->next = NULL;
return node;
}
typedef struct QueueList
{
Node* front;
Node* rear;
}QueueList;
// 创建队列
QueueList* create_queue(void)
{
QueueList* queue = malloc(sizeof(QueueList));
queue->front = NULL;
queue->rear = NULL;
return queue;
}
// 销毁队列
void destroy_queue(QueueList* queue)
{
while(NULL != queue->front)
{
Node* node = queue->front;
queue->front = node->next;
free(node);
}
free(queue);
}
// 判断队列是否为空
bool empty_queue(QueueList* queue)
{
return NULL == queue->front;
}
// 入队
void push_queue(QueueList* queue,TYPE data)
{
Node* node = create_node(data);
if(empty_queue(queue))
{
queue->front = node;
queue->rear = node;
return;
}
queue->rear->next = node;
queue->rear = node;
}
// 出队
bool pop_queue(QueueList* queue)
{
if(empty_queue(queue))
return false;
Node* node = queue->front;
queue->front = node->next;
free(node);
}
// 查看队头元素,调用该函数前要先判断队列是否为空,否则就会使用到空指针
TYPE front_queue(QueueList* queue)
{
return queue->front->data;
}
// 查看队尾元素,调用该函数前要先判断队列是否为空,否则就会使用到野指针
TYPE rear_queue(QueueList* queue)
{
return queue->rear->data;
}
// 队列元素数量
size_t size_queue(QueueList* queue)
{
size_t size = 0;
for(Node* n=queue->front; NULL!=n; n=n->next)
{
size++;
}
return size;
}
int main(int argc,const char* argv[])
{
QueueList* queue = create_queue();
for(int i=0; i<10; i++)
{
push_queue(queue,i);
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
}
printf("---------------------\n");
while(!empty_queue(queue))
{
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
pop_queue(queue);
}
}
4. Representação e implementação de fila sequencial
O subscrito superior da pilha sequencial aumentará à medida que os elementos forem colocados na pilha e os elementos diminuirão quando forem retirados da pilha, de modo que o espaço possa ser reutilizado, enquanto o ponteiro do início da fila e o ponteiro do final da fila da fila sequencial vai saindo da fila conforme os elementos entram na fila Tem aumentado e não pode ser reutilizado, formando uma estrutura de dados one-time.
Para evitar essa situação, quando o subscrito do cabeçalho da fila e o subscrito do final da fila atingirem o final do espaço de armazenamento, encontre uma maneira de "voltar" o subscrito do cabeçalho da fila e o subscrito do final da fila, ou seja, trate o armazenamento espaço da fila sequencial como um "Anel", uso circular, portanto as filas sequenciais também são chamadas de filas circulares.
Fila circular com versão contadora:
1. Resolvido o problema de cálculo do número de elementos
2. Resolvido o problema de julgar se a fila está vazia ou cheia
3. A desvantagem é que um membro é adicionado e deve ser operado ao criar, entrar e sair da equipe.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
typedef struct QueueArray
{
TYPE* base;
int front;
int rear;
size_t cnt;
size_t cap;
}QueueArray;
// 创建队列
QueueArray* create_queue(size_t cap)
{
QueueArray* queue = malloc(sizeof(QueueArray));
queue->base = malloc(sizeof(TYPE)*cap);
queue->cap = cap;
queue->front = 0;
queue->rear = -1;
queue->cnt = 0;
return queue;
}
// 销毁队列
void destroy_queue(QueueArray* queue)
{
free(queue->base);
free(queue);
}
// 判断队列是否为空
bool empty_queue(QueueArray* queue)
{
return 0 == queue->cnt;
}
// 判断队列是否为满
bool full_queue(QueueArray* queue)
{
return queue->cnt == queue->cap;
}
// 入队
bool push_queue(QueueArray* queue,TYPE data)
{
if(full_queue(queue))
return false;
queue->rear = (queue->rear+1)%queue->cap;
queue->base[queue->rear] = data;
queue->cnt++;
return true;
}
// 出队
bool pop_queue(QueueArray* queue)
{
if(empty_queue(queue))
return false;
queue->front = (queue->front+1)%queue->cap;
queue->cnt--;
return true;
}
// 查看队头元素,判断队列是否为空
TYPE front_queue(QueueArray* queue)
{
return queue->base[queue->front];
}
// 查看队尾元素
TYPE rear_queue(QueueArray* queue)
{
return queue->base[queue->rear];
}
// 队列元素数量
size_t size_queue(QueueArray* queue)
{
return queue->cnt;
}
int main(int argc,const char* argv[])
{
QueueArray* queue = create_queue(10);
for(int i=0; i<10; i++)
{
push_queue(queue,i);
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
}
printf("---------------------\n");
while(!empty_queue(queue))
{
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
pop_queue(queue);
}
printf("---------------------\n");
for(int i=0; i<10; i++)
{
push_queue(queue,i);
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
}
printf("---------------------\n");
while(!empty_queue(queue))
{
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
pop_queue(queue);
}
destroy_queue(queue);
return 0;
}
Fila circular sem versão do contador:
1. Como julgar o status vazio da fila?
Inicialize o subscrito do início da fila para front = 0, e o subscrito da cauda da fila para rear = 0. Quando a fila está vazia, a condição de julgamento é front == rear, mas quando a fila está cheia , a condição de julgamento também é front == rear .
A solução é deixar a última posição da fila sem uso, de forma que o estado da fila seja full front == rear+1;
2. Como calcular o número de elementos da fila?
(traseira-frente+tampa)%cap
3. Visualize os elementos no final da fila
base[(ler-1+cap)%cap]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
typedef struct QueueArray
{
TYPE* base;
int front;
int rear;
size_t cap;
}QueueArray;
// 创建队列
QueueArray* create_queue(size_t cap)
{
QueueArray* queue = malloc(sizeof(QueueArray));
queue->base = malloc(sizeof(TYPE)*cap+1);
queue->cap = cap+1;
queue->front = 0;
queue->rear = 0;
return queue;
}
// 销毁队列
void destroy_queue(QueueArray* queue)
{
free(queue->base);
free(queue);
}
// 判断队列是否为空
bool empty_queue(QueueArray* queue)
{
return queue->front == queue->rear;
}
// 判断队列是否为满
bool full_queue(QueueArray* queue)
{
return queue->front == (queue->rear+1)%queue->cap;
}
// 入队
bool push_queue(QueueArray* queue,TYPE data)
{
if(full_queue(queue))
return false;
queue->base[queue->rear] = data;
queue->rear = (queue->rear+1)%queue->cap;
return true;
}
// 出队
bool pop_queue(QueueArray* queue)
{
if(empty_queue(queue))
return false;
queue->front = (queue->front+1)%queue->cap;
return true;
}
// 查看队头元素,判断队列是否为空
TYPE front_queue(QueueArray* queue)
{
return queue->base[queue->front];
}
// 查看队尾元素
TYPE rear_queue(QueueArray* queue)
{
return queue->base[(queue->rear - 1 + queue->cap) % queue->cap];
}
// 队列元素数量
size_t size_queue(QueueArray* queue)
{
return (queue->rear - queue->front + queue->cap) % queue->cap;
}
int main(int argc,const char* argv[])
{
QueueArray* queue = create_queue(10);
for(int i=0; i<10; i++)
{
push_queue(queue,i);
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
}
printf("---------------------\n");
while(!empty_queue(queue))
{
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
pop_queue(queue);
}
printf("---------------------\n");
for(int i=0; i<10; i++)
{
push_queue(queue,i);
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
}
printf("---------------------\n");
while(!empty_queue(queue))
{
printf("front:%d rear:%d\n",
front_queue(queue),
rear_queue(queue));
pop_queue(queue);
}
destroy_queue(queue);
}
5. Aplicação da Fila
A estrutura de fila é geralmente usada no processamento de negócios, como: sistema de chamada de número de banco, sistema de emissão de bilhetes 12306, processamento de pedidos de comércio eletrônico, etc.