1 队列概念
队列是一种
先进先出
(
Fisrt In First Out, FIFO
) 的线性表
它只允许在表的一端进行插入数据,在表的另外一端删除数据
允许插入数据的那一端称为队尾
(rear / tail)
允许删除数据的那一端称为队头
(front / head)
2 队列的存储结构
2.1 顺序结构
队列的顺序结构也称之为顺序队列
它是一组地址连续的存储单元来存放队列中的元素
由于队列中的元素插入和删除限制在表的两端进行的,因此,设置队头
“
指针
”
和 队尾
“
指针
”
,分别指出
当前队列的队头和队尾的位置
![](https://img-blog.csdnimg.cn/direct/bc4d17efe1bb4b6f8b990044aea571dc.png)
基本操作:
初始化一个空的队列,
queue_init();
入队
push / enqueue
出队
pop / dequeue
获取队头元素的值
get_front()
判断队列是否为空
清空队列,销毁队列
2.2 循环队列
在顺序结构,元素入队时只修改队尾
"
指针
"
, 元素出队时,只修改队头
"
指针
"
由于顺序队列的存储空间是提前设定,所以队尾
“
指针
”
会有一个上限值, 当队尾
“
指针
”
到达这个上限
值,就只能进行出队操作,不能再通过队尾
"
指针
"
来实现新元素的入队操作。
一般将顺序队列设想成一个
环状结构
(
通过整数取余运算实现
)
, 则可维持入队,出队的简单性
,
我们称之
为
循环队列
。
![](https://img-blog.csdnimg.cn/direct/80151f88993942e9a088e7a0089b3789.png)
设循环队列
Q
的容量为
MAXSIZE,
初始时队列为空, 且
Q.rear
和
Q.front
都是
0
,如下图
a
元素入队时,修改队尾指针
Q.rear = (Q.rear + 1) % MAXSIZE
, 如下图
b
元素出队时,修改队头指针
Q.front = (Q.front + 1) % MAXSIZE
, 如下图
c
不断的出队,当出队操作导致队列为空的时候,有
Q.front == Q.rear ,
如图
d
如果不断的入队,导致队满了,则有
Q.front == Q.rear,
如下图
e
在队列空和队列满的情况下,循环队列的队头
“
指针
”
和队尾
"
指针
"
的位置是相同的,此时,我们无法根据
Q.rear
和
Q.front
来判断队列的状态
为了区分队空还是队满,常用以下方法进行处理
:
牺牲一个存储单元,约定好
:
队列的队尾
“
指针
”
所在位置的下一个位置是队头
"
指针
"
时,此时,认为队列已满。如下图
f
![](https://img-blog.csdnimg.cn/direct/10474e359bd3472290ff0a8b283cdd4a.png)
还可以通过记录队列中的元素个数来判断队列是否已满
typedef int ElemType;
#define MAXSIZE 10
typedef struct Seqqueue
{
ElemType data[MAXSIZE]; //数据域
int front; //队头"指针"
int rear; //队尾"指针"
int num; //队列中的元素个数
}Seqqueue;
if(num == 0) //队列为空
if(num == MAXSIZE) //队列已满
2.3 链式队列
相当于一端进行插入,另一端进行删除的链表,就是一个队列
(
链式队列
)
链式队列的创建
入队
(
尾插
)
出队
(
删除第一个元素
)
获取队头元素的数据
判断队列是否为空
销毁队列
..........
3. 后缀表达式
中缀表达式
:
如
: 1 + 2 * 3
100 + 200 * 3 / 2
后缀表达式
:
如
: 1 2 3 * +
100 200 3 * 2 / +
3.1 中缀转后缀的一般思路
遍历中缀表达式
1
、如果遇到数字,则直接入队
2
、如果遇到的是运算符,则运算符入栈
入栈规则:
a.
如果栈为空,则直接入栈
b.
如果栈不为空,则需要比较入栈的运算符与栈顶元素的优先级
如果待入栈的运算符的优先级比栈顶元素优先级高,则直接入栈
否则,需要把栈顶元素弹出,并进行入队,继续比较待入栈元素与新的栈顶元素的优先级
重复以上过程
3
、当遍历完中缀表达式之后,依次把栈中的运算符弹出,入队。
当栈空后,队列中保存的就是所谓的 后缀表达式。
![](https://img-blog.csdnimg.cn/direct/44f85f3462aa429d92c876704ab17a1b.png)
3.2 后缀表达式的计算
1
、遍历后缀表达式队列,获取并移除队头元素
2
、对队头元素做判断
a.
如果队头元素是数字,则直接入栈
b.
如果是运算符,则从栈中依次弹出两个元素,分别作为运算符的右操作数
(
先弹出的
)
和左操作数
(
后
弹出的
)
,用当前运算符进行计算,最后把计算的结果进行入栈
重复上面的过程,直到队列为空,最后的结果存储在栈中
![](https://img-blog.csdnimg.cn/direct/5caf4c48144846a7bc5c74639bcfc521.png)
计算过程:
![](https://img-blog.csdnimg.cn/direct/0be177ebf77343639b72b28ee3af33bf.png)
顺序队列代码:
seqqueue.c
#include "seqqueue.h"
/*
功能: 创建一个空的队列
返回值:
队列的地址
*/
Seqqueue* queue_init()
{
Seqqueue* queue = malloc(sizeof(Seqqueue));
queue->front = 0;
queue->rear = 0; //一开始,队头和队尾在同一位置,表示队列为空
return queue;
}
/*
功能: 入队
参数:
queue: 队列的地址
value: 需要入队的元素
*/
void push_queue(Seqqueue* queue, ElemType value)
{
//判断队列是否满了
if(queue->rear == MAXSIZE)
{
printf("队列已经满了,无法进行入队操作\n");
return ;
}
//data[rear]
queue->data[queue->rear] = value; //向队列中添加元素
queue->rear++; //队尾"指针"去到下一个入队的位置
}
/*
功能: 出队
参数:
queue: 队列的地址
返回值:
成功返回出队元素的数据
失败返回0
*/
ElemType pop_queue(Seqqueue* queue)
{
//判断队列是否为空
if(queue_empty(queue))
{
printf("队列已空,无法进行出队操作\n");
return 0;
}
ElemType temp = queue->data[queue->front]; //先保存出队元素的数据
queue->data[queue->front] = 0;
queue->front++; //原来队头后面的那个元素成为新的队头
}
/*
功能: 获取队头元素的数据
参数:
queue: 队列的地址
返回值:
成功返回队头元素的数据
失败返回0
*/
ElemType get_front(Seqqueue* queue)
{
//判断队列是否为空
if(queue_empty(queue))
{
printf("队列已空,无法进行出队操作\n");
return 0;
}
else
{
return queue->data[queue->front]; //data[front]
}
}
/*
功能: 判断队列是否为空
参数:
队列的地址
返回值:
为空返回 true
不为空返回 flase
*/
bool queue_empty(Seqqueue* queue)
{
//在此队列中,当队头和队尾的位置一致时,认为队列为空
if(queue->front == queue->rear)
return true;
return false;
}
Seqqueue* destory_queue(Seqqueue* queue)
{
while(1)
{
//如果队列为空,则跳出循环
if(queue_empty(queue))
break;
//不断的进行出队操作
pop_queue(queue);
}
free(queue);
queue = NULL;
return queue;
}
seqqueue.h
#ifndef __SEQQUEUE_H__
#define __SEQQUEUE_H__
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int ElemType;
#define MAXSIZE 10
typedef struct Seqqueue
{
ElemType data[MAXSIZE]; //数据域
int front; //队头"指针"
int rear; //队尾"指针"
}Seqqueue;
Seqqueue* queue_init();
void push_queue(Seqqueue* queue, ElemType value);
ElemType pop_queue(Seqqueue* queue);
ElemType get_front(Seqqueue* queue);
bool queue_empty(Seqqueue* queue);
Seqqueue* destory_queue(Seqqueue* queue);
#endif
main.c
#include "seqqueue.h"
int main()
{
//创建一个空的队列
Seqqueue* queue = queue_init();
//出队
pop_queue(queue);
//入队
push_queue(queue, 100);
push_queue(queue, 200);
push_queue(queue, 300);
//获取队头元素的数据
int front_data = get_front(queue);
if(front_data != 0)
printf("front_data = %d\n", front_data);
//出队
pop_queue(queue);
front_data = get_front(queue);
if(front_data != 0)
printf("front_data = %d\n", front_data);
//销毁队列
queue = destory_queue(queue);
push_queue(queue, 500);
return 0;
}
循环队列代码:
Circlequeue.c
#include "Circlequeue.h"
/*
功能: 创建一个空的队列
返回值:
队列的地址
*/
Seqqueue* queue_init()
{
Seqqueue* queue = malloc(sizeof(Seqqueue));
queue->front = 0;
queue->rear = 0; //一开始,队头和队尾在同一位置,表示队列为空
return queue;
}
/*
功能: 入队
参数:
queue: 队列的地址
value: 需要入队的元素
*/
void push_queue(Seqqueue* queue, ElemType value)
{
//判断队列是否满了
if((queue->rear+1)%MAXSIZE == queue->front)
{
printf("队列已经满了,无法进行入队操作\n");
return ;
}
//data[rear]
queue->data[queue->rear] = value; //向队列中添加元素
queue->rear = (queue->rear+1)%MAXSIZE; //队尾"指针"去到下一个入队的位置
}
/*
功能: 出队
参数:
queue: 队列的地址
返回值:
成功返回出队元素的数据
失败返回0
*/
ElemType pop_queue(Seqqueue* queue)
{
//判断队列是否为空
if(queue_empty(queue))
{
printf("队列已空,无法进行出队操作\n");
return 0;
}
ElemType temp = queue->data[queue->front]; //先保存出队元素的数据
queue->data[queue->front] = 0;
queue->front = (queue->front+1)%MAXSIZE;//原来队头后面的那个元素成为新的队头
}
/*
功能: 获取队头元素的数据
参数:
queue: 队列的地址
返回值:
成功返回队头元素的数据
失败返回0
*/
ElemType get_front(Seqqueue* queue)
{
//判断队列是否为空
if(queue_empty(queue))
{
printf("队列已空,无法进行出队操作\n");
return 0;
}
else
{
return queue->data[queue->front]; //data[front]
}
}
/*
功能: 判断队列是否为空
参数:
队列的地址
返回值:
为空返回 true
不为空返回 flase
*/
bool queue_empty(Seqqueue* queue)
{
//在此队列中,当队头和队尾的位置一致时,认为队列为空
if(queue->front == queue->rear)
return true;
return false;
}
Seqqueue* destory_queue(Seqqueue* queue)
{
while(1)
{
//如果队列为空,则跳出循环
if(queue_empty(queue))
break;
//不断的进行出队操作
pop_queue(queue);
}
free(queue);
queue = NULL;
return queue;
}
Circlequeue.h
#ifndef __CIRCLEQUEUE_H__
#define __CIRCLEQUEUE_H__
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int ElemType;
#define MAXSIZE 5
typedef struct Seqqueue
{
ElemType data[MAXSIZE]; //数据域
int front; //队头"指针"
int rear; //队尾"指针"
}Seqqueue;
Seqqueue* queue_init();
void push_queue(Seqqueue* queue, ElemType value);
ElemType pop_queue(Seqqueue* queue);
ElemType get_front(Seqqueue* queue);
bool queue_empty(Seqqueue* queue);
Seqqueue* destory_queue(Seqqueue* queue);
#endif
main.c
#include "Circlequeue.h"
int main()
{
//创建一个空的队列
Seqqueue* queue = queue_init();
//出队
pop_queue(queue);
//入队
push_queue(queue, 100);
push_queue(queue, 200);
push_queue(queue, 300);
push_queue(queue, 400);
push_queue(queue, 500);
//获取队头元素的数据
int front_data = get_front(queue);
if(front_data != 0)
printf("front_data = %d\n", front_data);
//出队
pop_queue(queue);
front_data = get_front(queue);
if(front_data != 0)
printf("front_data = %d\n", front_data);
//入队
push_queue(queue, 600);
//销毁队列
queue = destory_queue(queue);
push_queue(queue, 500);
return 0;
}
链式队列代码:
queue.c
#include "queue.h"
/*
功能: 初始化一个头结点(存储队列的一些属性信息)
返回值:
返回头结点的地址
*/
Queue* queue_init()
{
Queue* queue = malloc(sizeof(Queue));
queue->front = NULL;
queue->rear = NULL;
queue->num = 0;
return queue;
}
/*
功能: 初始化一个新的结点
参数:
value: 结点数据域的值
返回值:
返回这个新的结点的地址
*/
queue_node* node_init(ElemType value)
{
queue_node* new_node = malloc(sizeof(queue_node));
new_node->data = value;
new_node->next = NULL;
return new_node;
}
/*
功能: 入队(往链表最后插入新的结点)
参数:
queue: 队列头结点的地址
value: 需要入队的元素的数据
*/
void push_queue(Queue* queue, ElemType value)
{
//初始化一个新的结点
queue_node* new_node = node_init(value);
//从无到有
if(queue->front == NULL)
{
queue->front = new_node;
queue->rear = new_node;
}
//从少到多
else
{
//往链表最后插入新的结点 ==> 尾插
queue->rear->next = new_node;
queue->rear = new_node; //新入队的元素接在原来队列的最后一个元素后面
}
queue->num++; //队列中元素的个数加一
}
/*
功能: 出队(删除链表中的第一个结点)
参数:
队列的头结点的地址
返回值:
成功返回出队元素的数据
失败返回0
*/
ElemType pop_queue(Queue* queue)
{
if(queue->num == 0)
{
printf("队列已空,无法进行出队操作\n");
return 0;
}
ElemType value;//保存出队的元素的数据
//队列中只有一个元素
if(queue->front->next == NULL)
{
value = queue->front->data;
queue_node* p = queue->front; //先保存需要释放的结点的地址
queue->front = NULL;
queue->rear = NULL;
free(p);
}
else
//队列中有多个元素
{
value = queue->front->data;
queue_node* p = queue->front; //先保存需要释放的结点的地址
queue->front = queue->front->next; //原来队列中的第二个元素成为新的队头
p->next = NULL;
free(p);
}
queue->num--;
return value;
}
/*
功能: 获取队头元素的数据
参数:
queue: 队列头结点的地址
返回值:
成功返回队头元素的数据
失败返回0
*/
ElemType get_front(Queue* queue)
{
if(queue->num != 0)
return queue->front->data;
else
printf("队列已空,无法获取队头元素的数据\n");
return 0;
}
/*
功能: 销毁一个队列
参数:
queue: 队列的头结点的地址
返回值:
NULL
*/
Queue* destory_queue(Queue* queue)
{
queue_node* p = queue->front; //遍历整个队列
queue_node* p_next = NULL; //用来保存此时待删除结点的下一个结点的地址
while(p != NULL)
{
p_next = p->next; //保存此时待删除结点的下一个结点的地址
p->next = NULL;
free(p);
p = p_next; //继续往后遍历
}
queue->front = NULL;
queue->rear = NULL;
queue->num = 0;
free(queue);
queue = NULL;
return queue;
}
queue.h
#ifndef __QUEUE_H__
#define __QUEUE_H__
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int ElemType;
typedef struct queue_node
{
ElemType data; //数据域
struct queue_node* next; //指针域
}queue_node;
typedef struct Queue
{
queue_node* front; //队头指针,保存队头元素的地址
queue_node* rear; //队尾指针,保存队尾元素的地址
int num; //队列中元素的个数
}Queue;
Queue* queue_init();
queue_node* node_init(ElemType value);
void push_queue(Queue* queue, ElemType value);
ElemType pop_queue(Queue* queue);
ElemType get_front(Queue* queue);
Queue* destory_queue(Queue* queue);
#endif
main.c
#include "queue.h"
int main()
{
Queue* queue = queue_init();
//出队
pop_queue(queue);
//入队
push_queue(queue, 100);
push_queue(queue, 200);
push_queue(queue, 300);
ElemType front_data = get_front(queue);
if(front_data != 0)
printf("front_data = %d\n", front_data);
//出队
pop_queue(queue);
front_data = get_front(queue);
if(front_data != 0)
printf("front_data = %d\n", front_data);
//销毁队列
queue = destory_queue(queue);
push_queue(queue, 500);
return 0;
}
中缀表达式转后缀表达式代码:
houzhui.c
#include "houzhui.h"
/*
功能: 获取一个运算符的优先级
参数:
op: 运算符
返回值:
返回这个运算符的优先级
*/
int get_priority(char op)
{
if(op == '+' || op == '-')
return 1;
if(op == '*' || op == '/')
return 2;
}
/*
功能: 运算符的入栈
参数:
stack: 栈的头结点地址
queue: 队列头结点的地址
op : 运算符
*/
void push_operator_stack(Stack* stack, Queue* queue, char op)
{
char sop[2] = {op, '\0'}; //存储运算符
//如果栈不为空,则需要比较入栈的运算符与栈顶元素的优先级
while(stack_empty(stack) == false)
{
//如果待入栈的运算符的优先级比栈顶元素优先级高,则直接入栈
if(get_priority(op) > get_priority(get_top(stack)[0]))
{
push_stack(stack, sop);
return ;
}
//否则,需要把栈顶元素弹出,并进行入队,继续比较待入栈元素与新的栈顶元素的优先级
else
{
ElemType* top_data = pop_stack(stack); //获取栈顶元素的数据并进行出栈
push_queue(queue, top_data); //将出栈的元素进行入队
}
}
//如果栈为空,则直接入栈
push_stack(stack, sop);
}
/*
功能: 中缀表达式转化为后缀表达式
参数:
stack: 栈的头结点的地址
queue: 队列的头结点的地址
express: 需要进行转化的中缀表达式
*/
void convert_mid_express(Stack* stack, Queue* queue, ElemType* express)
{
//1、数据域为字符串
//2、先建立一个空的栈和一个空的队列
//3、定义一个字符数组,存储中缀表达式
char temp[10] = {0}; //用来保存中缀表达式中的数字字符
int pos = 0; //偏移量,用来协助数字字符的存储
//4、遍历中缀表达式,并判断遇到的字符是数字还是运算符
for(int i = 0; i < strlen(express); i++)
{
//5、如果是数字,则存入字符数组
if(express[i] >= '0' && express[i] <= '9')
{
temp[pos] = express[i];
pos++;
}
else
{
//6、如果是运算符,则将字符数组中的数据入队,再根据规则对运算符进行入栈操作
push_queue(queue, temp);
memset(temp, 0, sizeof(temp)); //将temp这个数组清空
pos = 0; //偏移量置零
//运算符入栈
push_operator_stack(stack, queue, express[i]);
}
}
//最后一个数字字符串存储在temp中,需要手动进行入队
push_queue(queue, temp);
//7、中缀表达式遍历完毕之后,将栈中的元素依次出栈并进行入队
while(stack_empty(stack) == false)
{
ElemType* top_data = pop_stack(stack);
push_queue(queue, top_data);
}
}
houzhui.h
#ifndef __HOUZHUI_H__
#define __HOUZHUI_H__
#include "queue.h"
#include "Stack.h"
int get_priority(char op);
void push_operator_stack(Stack* stack, Queue* queue, char op);
void convert_mid_express(Stack* stack, Queue* queue, ElemType* express);
#endif
main.c
#include "queue.h"
#include "stack.h"
#include "houzhui.h"
int main()
{
/*
Queue* queue = queue_init();
//出队
pop_queue(queue);
//入队
push_queue(queue, "hello");
push_queue(queue, "hahahaha");
push_queue(queue, "oooo");
print_queue(queue);
ElemType* front_data = get_front(queue);
if(front_data != NULL)
printf("front_data = %s\n", front_data);
//出队
pop_queue(queue);
front_data = get_front(queue);
if(front_data != NULL)
printf("front_data = %s\n", front_data);
//销毁队列
queue = destory_queue(queue);
push_queue(queue, "hhh");
*/
/*
//初始化一个空的栈
Stack* stack = stack_init();
//出栈
pop_stack(stack);
//入栈
push_stack(stack, "hello");
push_stack(stack, "world");
push_stack(stack, "hahahaha");
ElemType* top_data = get_top(stack);
if(top_data != NULL)
printf("top_data = %s\n", top_data);
//出栈
ElemType* pop_data = pop_stack(stack);
printf("pop_data = %s\n", pop_data);
free(pop_data);
top_data = get_top(stack);
if(top_data != NULL)
printf("top_data = %s\n", top_data);
*/
//对栈和队列进行初始化
Stack* stack = stack_init();
Queue* queue = queue_init();
//中缀表达式 100 + 200 * 3 / 2
//ElemType express[20] = "1+2*3"; //1 2 3 * +
ElemType express[20] = "100+200*3/2"; //100 200 3 * 2 / +
//中缀转后缀
convert_mid_express(stack, queue, express);
//打印队列
print_queue(queue);
return 0;
}