什么是栈和队列
栈和队列是两种重要的线性结构
数据结构:线性表
特殊性:基本操作是线性表的子集,也就是功能受限的线性表,被称为限定性的数据结构
数据类型:线性表不太相同,有时候被当做一种管理数据的规则
栈(stack)
限定仅在表尾进行插入或删除操作和线性表,对栈来说表尾和表头有特殊含义。
先进后出 LIFO(Last in first out)
栈顶:表尾
栈底:表头
空栈:没有元素的空表
满栈:元素达到栈的容量
压栈,入栈:数据添加到栈中
出栈,弹栈:数据从栈中删除
功能
- 创建栈
- 销毁栈
- 栈是否为空
- 栈是否为满
- 入栈
- 出栈
- 查找栈顶元素
- 求元素数量
顺序栈
#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);
}
return 0;
}
链式栈
#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);
}
return 0;
}
常考的笔试题
- 入栈顺序:1 2 3 4 5
- 出栈顺序:4 1 2 3 5
检测出栈入栈的代码
typedef struct Stack{
int* data;
int top;
int cap;
}Stack;
void push_stack(Stack* stack,int val)
{
stack->data[stack->top] = val;
stack->top++;
}
bool pop_stack(Stack* stack)
{
if(stack->top<=0)
return -1001;
stack->top--;
return stack->data[stack->top];
}
int top_stack(Stack* stack)
{
if(stack->top<=0)
{
return -1001;
}
return stack->data[stack->top-1];
}
bool IsPopOrder(int* pushV, int pushVLen, int* popV, int popVLen ) {
Stack* stack = malloc(sizeof(Stack));
stack->cap = 1000;
stack->top = 0;
stack->data = malloc(sizeof(int)*1000);
int index = -1;
for(int i=0,j=0;i<pushVLen;i++)
{
int val;
push_stack(stack,pushV[i]);
index++;
while(index>=0 && top_stack(stack) == popV[j])
{
index--;
j++;
pop_stack(stack);
}
}
return 0>index;
}
栈的应用
- 内存管理:比如栈内存,因为它遵循着栈的后进先出的规则,它支持着函数调用,函数在传参数就是把参数先压入到栈内存,等跳转过去后再把参数先压到栈内存,等跳转过去后再把参数从栈内存弹出
- 特殊的算法,例如:进制转换,表达式解析,迷宫求解
队列
与栈相反,先进先出的线性表,有且只有两个端口
队尾:只能添加的端口
队头:只能删除的端口
出队:删除数据
入队:添加数据
队列功能
- 创建队列
- 销毁队列
链式队列
#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);
}
return 0;
}
顺序队列
带计数器版本的循环队列
优点
- 解决了计算元素数量的问题
- 解决了判断队列空,满状态的问题
缺点
- 增加了一个成员,在入队和出队的时候都要判断
代码
#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;
}
不带计数器版本的循环队列
问题
-
如何判断队列空,满状态
前提:队头初始化front = 0,队尾初始化rear = 0;
队列空状态:front == rear;
队列满状态:front == rear;
解决办法:队列空一个位置不使用,所以队列满的状态front == rear+1
-
如何计算队列元素数量
(rear-front+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);
return 0;
}
队列的应用
一般用于业务处理
- 银行叫号系统
- 12306购票系统
- 电商的订单处理系统