栈和队列
1 栈
1.1 定义和逻辑结构
stack:只能在一端进入和删除的线性表,先进后出(FILO)
![image-20210623123619000](https://tva1.sinaimg.cn/large/008i3skNgy1grs3fr4ev8j30s20fdkd7.jpg)
1.2 存储结构
1.2.1 顺序栈
int stack[maxSize];
int top = -1; //初始化栈顶指针
x = stack[++top]; //入栈操作
x = stack[top--]; //出栈操作
top = -1; //栈空状态
top = maxSize - 1; //栈满状态
1.2.2 链式栈
![image-20210623124741558](https://tva1.sinaimg.cn/large/008i3skNgy1grs3rjmghoj30be02nabi.jpg)
//建立一个链式栈
LNode *head = (LNode*)malloc(sizeof(LNode));
head->next = NULL;
LNode *top = NULL;
//元素入栈,将栈顶元素指向头节点并且连在头节点之前
top = (LNode*)malloc(sizeof(LNode));
top->next = NULL;
top->data = 'A'; //同理,如果插入其他的节点,则将此处的A改为其他节点的值
top->next = head->next;
head->next = top;
//元素出栈,也就是删除链表中的元素
x = top->data;
head->next = top->next;
free(top);
top = head->next;
//栈空状态
head->next = NULL; //此为真,则栈空,只要内存够大,就不存在栈满状态
2 队列
2.1逻辑结构
queue,只可在一端插入,另一端删除的线性表。可插入元素的一端叫队尾(Rear),删除的一端叫队头(Front)。先进先出,FIFO。
2.2存储结构
2.2.1 线性队
![image-20210623125848597](https://tva1.sinaimg.cn/large/008i3skNgy1grs433w8s7j30lt07h7bj.jpg)
![image-20210623130454869](https://tva1.sinaimg.cn/large/008i3skNgy1grs49gge9nj30mi09tqcq.jpg)
![image-20210623130635038](https://tva1.sinaimg.cn/large/008i3skNgy1grs4b7ax5kj30cp0aewks.jpg)
int queue[maxSize];
int rear = 0, front = 0;
//根据具体情况推导出队空状态,不仅仅包括 rear = front
//入队操作
queue[++rear] = x;
//出队操作,注意front指针在出队操作完成之后不指向元素,而是指向一个空的内存
x = queue[++front];
//假溢出指的是在rear指针指向数组中最后一个元素时,没有办法进行进一步的插入操作,此时为假溢出
//为了避免假溢出的出现,形象上把整个数组掰弯形成一个环,这在具体的代码中就是取余操作
//“掰弯”之后的入队操作:
rear = (rear+1) % maxSize;
queue[rear] = x;
//“掰弯”之后的出队操作:
front = (front+1) % maxSize;
x = queue[front];
//此时的队空状态:
front == rear 为真
//此时的队满状态:
front == (rear+1) % maxSize 为真
2.2.2 链队
![image-20210623131434292](https://tva1.sinaimg.cn/large/008i3skNgy1grs4jihsfoj30g606m0xm.jpg)
![image-20210623131645232](https://tva1.sinaimg.cn/large/008i3skNgy1grs4lryekwj30h504wdju.jpg)
//考试时一般采取的结构体定义方式:
typedef struct{
LNode *front;
LNode *rear;
}queue;
//链队为空的情况:
head->next == NULL为真
3 考点
3.1 输出序列
3.1.1 由出栈序列判断容量
![image-20210623132014361](https://tva1.sinaimg.cn/large/008i3skNgy1grs4peo1xlj30b307cgpc.jpg)
举个例子:具体问答时一般不会直接给出出栈序列,需要根据一些条件进行推测,如下图所示:
1)给出入栈和出栈的顺序,进行判断最大的的存储容量:这种题目的方法就是一个一个地试。
![image-20210623132224286](https://tva1.sinaimg.cn/large/008i3skNgy1grs4rnfjssj30gr06k43n.jpg)
![image-20210623132338443](https://tva1.sinaimg.cn/large/008i3skNgy1grs4sxwbq4j30gh062aet.jpg)
2)给出入栈和出栈的顺序,要求判断哪种出栈是不可能的。这种问题有固定的套路,即如果123,则无312。如果最后一个入栈的第一个出栈,那么整个栈中的出栈顺序就是固定的。
![image-20210623132930477](https://tva1.sinaimg.cn/large/008i3skNgy1grs4z1zdk0j30iy01w75y.jpg)
![image-20210623133325403](https://tva1.sinaimg.cn/large/008i3skNgy1grs534njfyj30sb0fjtuz.jpg)
3)Catalan number-只需要记忆
3.2 表达式的转换问题
中缀表达式
前缀表达式(波兰式)
后缀表达式(逆波兰式)
1)中缀转换成前缀或者后缀的方法是给所有的运算参数上加上括号,如果转成前缀,则将括号之间的符号提到两个括号之前。后缀是放在括号之后。
![image-20210623134009422](https://tva1.sinaimg.cn/large/008i3skNgy1grs5a50eutj30h80avwns.jpg)
![image-20210623134104712](https://tva1.sinaimg.cn/large/008i3skNgy1grs5b2y1mlj30el06ewj0.jpg)
![image-20210623134144826](https://tva1.sinaimg.cn/large/008i3skNgy1grs5bs9zaej309s05xgoe.jpg)
![image-20210623134356656](https://tva1.sinaimg.cn/large/008i3skNgy1grs5e30jkcj30f109qjyi.jpg)
2)前缀后缀转换成中缀
看到两个数字后面紧跟着符号,则将这两个数字括在一起,中间/前面加符号。
![image-20210623134547540](https://tva1.sinaimg.cn/large/008i3skNgy1grs5fzlxpzj30eg0asn51.jpg)
![image-20210623134802396](https://tva1.sinaimg.cn/large/008i3skNgy1grs5ibztiej30b50avwkp.jpg)
3.3 用栈实现表达式的转换
![image-20210623135559031](https://tva1.sinaimg.cn/large/008i3skNgy1grs5qls6ctj30lt0d8wrb.jpg)
注意中缀转后缀时,新入栈的符号的优先级大于等于栈中的符号的优先级,则出栈。
void infixToPostFix(char infix[], char s2[], int &top2)
{
char s1[maxSize];
int top1 = -1;
int i = 0;
while(infix[i] != '\0')
{
//如果扫描到的是数字,则入结果栈
if('\0' <= infix[i] && infix[i] <= '9')
{
s2[++top2] = infix[i];
++i;
}
//如果扫描到的是左括号,则入辅助栈
else if (infix[i] == ')')
{
s1[++top1] = ')';
--i; //对应中缀转后缀的--i
}
//在输入符号的时候,判断符号的优先级
else if (infix[i] == '+' || infix[i] == '-' || infix[i] == '*' ||infix[i] == '/')
{
if (top == -1 || s1[top1] == '(' || getPriority(infix[i]) >= getPriority(s1[top1])) //注意等号
{
s1[++top1] = infi[i];
}
else
s2[++top2] = s1[top1--];
}
else if (infix[i] == '(')
{
while (s1[top1] != ')")
s2[++top2] = s1[top1--];
--top1;
--i; //对应中缀转后缀的--i
}
}
while (top1 != -1)
s2[++top2] = s1[top1--];
}
而在中缀转前缀时,新入栈的符号的优先级大于栈中的符号的优先级的时候才出栈,否则继续入栈。
![image-20210623140004159](https://tva1.sinaimg.cn/large/008i3skNgy1grs5uuyax8j30mm0dinan.jpg)
void infixToPostFix(char infix[], char s2[], int &top2)
{
char s1[maxSize];
int top1 = -1;
int i = 0;
while(infix[i] != '\0')
{
//如果扫描到的是数字,则入结果栈
if('\0' <= infix[i] && infix[i] <= '9')
{
s2[++top2] = infix[i];
++i;
}
//如果扫描到的是左括号,则入辅助栈
else if (infix[i] == '(')
{
s1[++top1] = '(';
++i;
}
//在输入符号的时候,判断符号的优先级
else if (infix[i] == '+' || infix[i] == '-' || infix[i] == '*' ||infix[i] == '/')
{
if (top == -1 || s1[top1] == '(' || getPriority(infix[i]) > getPriority(s1[top1]))
{
s1[++top1] = infi[i];
}
else
s2[++top2] = s1[top1--];
}
else if (infix[i] == ')')
{
while (s1[top1] != '(")
s2[++top2] = s1[top1--];
--top1;
++i;
}
}
while (top1 != -1)
s2[++top2] = s1[top1--];
}
后缀转前缀的方法是:将数字压入栈中,每两个算式遇到一个符号,则对两个算式进行重新处理,循环往复。
![image-20210623140313564](https://tva1.sinaimg.cn/large/008i3skNgy1grs5y4o0n4j30h90b0wmw.jpg)
3.4用栈实现表达式求值
3.4.1 中缀表达式
float calInfix(char exp[])
{
float s1[maxSize]; int top1 = -1;
char s2[maxSize]; int top2 = -1;
int i = 0;
while(exp[i] != '\0') //遇到串尾,则结束扫描
{
if('0' < exp[i] && exp[i] < '9')
{
//字符转换为数字
s1[++top1] = exp[i] - '0';
++i;
}
//左括号直接入字符栈
else if(exp[i] == '(')
{
s2[++top] = '(';
++i;
}
//判断扫描的值是符号
else if(exp[i] = '+'||exp[i] = '-'||exp[i] = '*'||exp[i] = '/')
{
if(top2 == -1 || s2[top2] =='('|| getPriority(exp[i]) > getPriority(s2[top2]) )
{
s2[top2] =exp[i];
++i;
}
else
{
int flag = calStackTopTow(s1, top1, s2, top2);
if(flag == 0)
return 0;
}
else if(exp[i] == ')')
{
while(s2[top2] != '(' )
{
int flag = calStackTopTow(s1, top1, s2, top2);
if(flag == 0)
return 0;
}
--top2;
++i;
}
}
//扫描结束如果栈中还有字符串剩余
while (top2 != -1)
{
int flag = calStackTopTow(s1, top1, s2, top2);
if(flag == 0)
return 0;
}
}
return s1[top1];
}
![image-20210623144923628](https://tva1.sinaimg.cn/large/008i3skNgy1grs7a661cnj30rq0g2h77.jpg)
3.4.2 后缀表达式
![image-20210623150310134](https://tva1.sinaimg.cn/large/008i3skNgy1grs7oi79t2j30no0fvqlo.jpg)
3.4.3 前缀表达式
从左到右扫描
3.5配置问题
队列的配置是考察的重点
3.5.1正常配置
//假溢出指的是在rear指针指向数组中最后一个元素时,没有办法进行进一步的插入操作,此时为假溢出
//为了避免假溢出的出现,形象上把整个数组掰弯形成一个环,这在具体的代码中就是取余操作
//“掰弯”之后的入队操作:
rear = (rear+1) % maxSize;
queue[rear] = x;
//“掰弯”之后的出队操作:
front = (front+1) % maxSize;
x = queue[front];
//此时的队空状态:
front == rear 为真
//此时的队满状态:
front == (rear+1) % maxSize 为真
计算元素个数的问题
rear>front 时
个数 = rear-front
![image-20210623151052495](https://tva1.sinaimg.cn/large/008i3skNgy1grs7wj2xxgj30v40ib4qp.jpg)
3.5.2 非正常配置
1)第一种情况
![image-20210623151214375](https://tva1.sinaimg.cn/large/008i3skNgy1grs7xy4grdj30v40cu1es.jpg)
![image-20210623151351957](https://tva1.sinaimg.cn/large/008i3skNgy1grs7zmoowlj30v40btttb.jpg)
![image-20210623151257116](https://tva1.sinaimg.cn/large/008i3skNgy1grs7yop7ozj30v40cynjt.jpg)
![image-20210623151329623](https://tva1.sinaimg.cn/large/008i3skNgy1grs7z8sdumj30v40bttt7.jpg)
2)第二种情况
![image-20210623151638986](https://tva1.sinaimg.cn/large/008i3skNgy1grs82jfilij30v40ev1hx.jpg)
![image-20210623151652903](https://tva1.sinaimg.cn/large/008i3skNgy1grs82rsmwoj30v40cie11.jpg)
![image-20210623151605439](https://tva1.sinaimg.cn/large/008i3skNgy1grs81ya396j30v40e3kgc.jpg)
3.6扩展问题-双端队列
![image-20210623152036663](https://tva1.sinaimg.cn/large/008i3skNgy1grs86nd5prj30v408o4cd.jpg)
![image-20210623152048896](https://tva1.sinaimg.cn/large/008i3skNgy1grs86v7n22j30v40eyty3.jpg)
![image-20210623152515686](https://tva1.sinaimg.cn/large/008i3skNgy1grs8bi0wj8j30lc0f9198.jpg)
3.7 栈的扩展内容
3.7.1 共享栈
![image-20210623152853812](https://tva1.sinaimg.cn/large/008i3skNgy1grs8f9oifaj30v408zaob.jpg)
![image-20210623153027880](https://tva1.sinaimg.cn/large/008i3skNgy1grs8gwyestj30v40d81er.jpg)
![image-20210623153103795](https://tva1.sinaimg.cn/large/008i3skNgy1grs8hj0qfwj30v406ak2f.jpg)
3.7.2 用栈模拟队列
![image-20210623153442779](https://tva1.sinaimg.cn/large/008i3skNgy1grs8lbm4djj30v40d9twp.jpg)
一定要注意在模拟过程中如果要保证顺序一致,则在移动过程中需要将元素全部移出,否则无法得到一致的顺序。
3.8括号匹配问题
括号匹配:
![image-20210623153918606](https://tva1.sinaimg.cn/large/008i3skNgy1grs8q3vzf4j30v40fi7vj.jpg)
![image-20210623153941384](https://tva1.sinaimg.cn/large/008i3skNgy1grs8qhtrukj30v40hz1kx.jpg)
3.9 利用栈进行计算
![image-20210623154117345](https://tva1.sinaimg.cn/large/008i3skNgy1grs8s6boqvj30v40kx7wh.jpg)
![image-20210623154150957](https://tva1.sinaimg.cn/large/008i3skNgy1grs8sqqovkj30v40ka7wh.jpg)
![image-20210623154212548](https://tva1.sinaimg.cn/large/008i3skNgy1grs8t4ejs4j30v40pghdt.jpg)