栈和队列
栈:限定仅在栈顶进行插入和删除操作的线性表
队列:只允许在一端进行插入操作,而在另一端进行删除操作的线性表
一、栈
1.栈的定义
栈(stack):限定仅在栈顶进行插入和删除操作的线性表
允许插入和删除的一端称为栈顶(top),另一端称为栈底,不含任何数据元素的栈叫空栈
栈又被称为:后进先出的线性表,简称LIFO结构
栈的插入操作:进栈,压栈,入栈
栈的删除操作:出栈,弹栈
使用:如果栈的使用过程中元素变化不可预料,有时很大,有时很小,一般会用链式栈。
如果元素变化在可控范围内,一般使用顺序栈
2.栈的应用
要使用好栈先进后出的特性
递归
定义:把一个直接调用自己或者通过一系列的调用语句间接地调用自己的函数,叫做递归函数
注意:每个递归定义必须至少有一个,满足这个条件时,递归不在进行,即不在引用自身而是返回值出去
优点:能使程序的结构更清晰,简洁,更容易让人理解
缺点:大量的递归调用会建立函数的副本,会耗费大量的时间和内存
用栈来实现递归操作:
在递归前行阶段,对于每一层递归,函数的局部变量,参数值,以及返回地址都被压入栈中。在退回阶段,位于栈顶的局部变量,参数和返回地址被弹出,用于返回调用层次中执行代码的其余部分,也就是恢复了调用的状态。
四则运算表达式求值
后缀(逆波兰)表示法:一种不需要括号的后缀表示法
中缀表达式-》后缀表达式
转换规则:从左到右遍历表达式的每个数字和符号,遇到是数字就输出(成为后缀表达式的一部分),遇到是符号,需要判断其与栈顶符号的优先级,如果是右括号或者优先级不高于栈顶符号,则栈顶元素依次出栈并输出,并将当前符号进栈。
正常数学表达式:
20-2*(5+3)+6/3;
后缀表达式:
20 2 5 3 + * - 6 3 / +
后缀表达式计算方法:从左到右遍历表达式的每一个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶的两个数字出栈,进行运算,运算结果进栈。
3.顺序栈
#include<stdio.h>
#include<stdlib.h>
//栈的最大容量
#define maxSize 100
//顺序栈结构
typedef struct stack
{
int* data;//存储栈的元素
int top;//栈顶指针
}stack;
//初始化栈
stack initStack()
{
//定义一个结构体
stack s;
//申请内存空间
s.data = (int*)malloc(sizeof(int)*maxSize);
//判断是否申请成功
if (!s.data)
{
printf("malloc memory failed!\n");
//异常退出
exit(0);
}
//从0开始
s.top = 0;
return s;
}
//入栈
void push(stack *s,int key)
{
//判断是否栈满
if (s->top == maxSize)
{
printf("stack is full!\n");
return;
}
else
{
s->data[s->top] = key;
s->top++;
}
}
//出栈
void pop(stack *s)
{
//判断栈是否为空
if (s->top == 0)
{
printf("this is a empty stack!\n");
return;
}
else
{
s->top--;
}
}
//打印栈中的元素
void print(stack* s)
{
int i = 1;
int count = s->top;
while (count != 0)
{
printf("第 %d 个元素是:%d\n", i, s->data[--count]);
i++;
}
}
4.链式栈
#include<stdio.h>
#include<stdlib.h>
//链式栈结构
typedef struct stackNode
{
int data;//数据域
struct stackNode* next;//指针域
}stackNode;
typedef struct linkStack
{
stackNode* top;//栈顶指针-和链表的头指针合并了
int size;//栈里有多少个元素
}linkStack;
//初始化
linkStack init_LinkStack()
{
linkStack s;
s.size = 0;
s.top = NULL;
return s;
}
//入栈-使用头插法
void push(linkStack* s,int key)
{
//申请一个节点
stackNode* new_node = (stackNode*)malloc(sizeof(stackNode));
if (!new_node)
{
printf("malloc memory failed!\n");
exit(0);
}
//添加元素
new_node->data = key;
//让原来的首元结点变成第二个结点
new_node->next = s->top;
//移动栈顶指针
s->top = new_node;
//元素个数加1
s->size++;
}
//出栈
void pop(linkStack* s)
{
//临时结点,用来释放出栈节点
stackNode* free_node;
free_node = s->top;
//移动top指针
s->top = s->top->next;
//释放节点
free(free_node);
//元素个数-1
s->size--;
}
//打印
void print(linkStack* s)
{
printf("栈中有 %d 个元素!\n", s->size);
if (s->size)
{
//计数器
int count = 1;
//临时指针,用来遍历栈
stackNode* pcurrent;
pcurrent = s->top;
while (pcurrent)
{
printf("第 %d 个元素是:%d\n", count, pcurrent->data);
count++;
pcurrent = pcurrent->next;
}
}
}
二、队列
缓存淘汰机制-先进先出,后进后出
1.队列的定义
队列(queue):是一种只允许进行插入操作,而在另一端进行删除操作的线性表
允许插入的一端叫:队尾
允许删除的一端叫:队头
队列是一种先进先出的线性表,简称FIFO
2.顺序队列
一般顺序队列是使用的循环队列,可以防止假溢出,和有效利用内存空间
循环队列:头尾相接的循顺序存储结构
#include<stdio.h>
#include<stdlib.h>
//队列大小
#define maxSize 100
//队列结构
typedef struct queue
{
int* data;//存放数据
int front;//头指针
int rear;//尾指针
}queue;
//初始化队列
queue init_queue()
{
queue q;
q.data = (int*)malloc(sizeof(int)*maxSize);
if (!q.data)
{
printf("malloc memory failed!\n");
exit(0);
}
q.front = 0;
q.rear = 0;
return q;
}
//入队-队尾入队
void enQueue(queue* q,int key)
{
//判断队列是否已满
if ((q->rear + 1) % maxSize == q->front)
{
printf("queue is full!\n");
return;
}
//加入数据
q->data[q->rear] = key;
//尾指针后移一位
q->rear = (q->rear + 1) % maxSize;
}
//出队-队头出队
void deQueue(queue* q)
{
//判断队列是否为空
if (q->front == q->rear)
{
printf("queue is empty!\n");
return;
}
//移动队头指针
q->front = (q->front + 1) % maxSize;
}
//打印
void print(queue q)
{
//计数器
int count = 1;
int front = q.front;
int rear = q.rear;
while (front != rear)
{
printf("第 %d 个元素是 %d \n", count, q.data[front]);
count++;
front = (front + 1) % maxSize;
}
}
3.链式队列
#include<stdio.h>
#include<stdlib.h>
//队列结构
typedef struct queueNode
{
int data;//数据域
struct queueNode* next;//指针域
}queueNode;
typedef struct linkQueue
{
queueNode* front;//头指针
queueNode* rear;//尾指针
int size;//元素个数
}linkQueue;
//入队从链表尾部,出队从链表头部
//初始化
linkQueue init_linkQueue()
{
linkQueue q;
q.rear = (queueNode*)malloc(sizeof(queueNode));
if (q.rear == NULL)
{
printf("malloc memory failed!\n");
exit(0);
}
q.front = q.rear;
q.front->next = NULL;
q.size = 0;
return q;
}
//入队-尾插法
void enQueue(linkQueue* q, int key)
{
//申请队列节点
queueNode* new_node = (queueNode*)malloc(sizeof(queueNode));
if (!new_node)
{
printf("malloc memory failed!\n");
exit(1);
}
//加入元素
new_node->data = key;
new_node->next = NULL;
//将新节点设置为最后一个节点
q->rear->next = new_node;
//移动尾指针
q->rear = new_node;
//元素个数+1
q->size++;
}
//出队-链表头部出队
void deQueue(linkQueue* q)
{
//判断队列是否为空
if (q->front == q->rear)
{
printf("queue is empty!\n");
exit(1);
}
//待删除节点
queueNode* free_node;
free_node = q->front->next;
//移动头指针
q->front = q->front->next->next;
free(free_node);
//元素个数-1
q->size--;
}
//打印
void print(linkQueue q)
{
printf("一共有 %d 个元素\n", q.size);
//计数器
int count = 1;
//当队列不为空的时候
if(q.front != q.rear)
{
//临时指针,用来遍历链表
queueNode* temp = q.front->next;
while (temp)
{
printf("第 %d 个元素是:%d\n", count, temp->data);
count++;
temp = temp->next;
}
}
}