栈和队列
栈和队列(理论+实践)
基本概念
- 栈和队列的逻辑结构是:线性表
栈
-
概念:限制存取点的后入先出的线性表
-
数学性质:n个不同的元素入栈,出栈元素的不同排列个数是1/(n+1)*Cn2n:卡特兰数
-
存储结构
-
顺序栈:数组+栈顶指针
-
//创建 int stk[N],tt; //插入操作 stk[++tt]=x; //弹出操作(删除栈顶元素) tt--; //判断栈是否为空 if(tt>0) not empty else empty //取出栈顶元素 stk[tt];
-
共享栈:两个顺序栈共享一个数组空间,两个栈的栈底分别置于共享空间的两端,栈顶向中间延伸
-
-
链栈:便于节点插入与删除,入栈和出栈操作都在表头进行
-
-
理论题
- 给出一段入栈序列,求各种可能的出栈序列:按照栈先入后出的特性,模拟一下入栈和出栈的过程即可
- 前中后缀表达式的转化:step1:按优先级把所有运算单位加上括号;step2:把运算符号移动到对应括号的前面或后面;step3:去掉括号
-
实践题
- 匹配问题:n对数据的序列,每一对数据有前后两元素,下一对的前元素不得在这一对的后元素之前出现
- 括号匹配问题:将{入栈。栈顶为{,入栈元素为}时弹出栈顶,若栈顶与入栈元素不匹配直接false
- 连线不交叉问题:将每一组数据较小的一个当做左括号,另一个当做右括号,解法同括号匹配一样
- 后缀表达式求值:数字直接入栈,算术符号则取栈顶两个数字,计算后将新数字入栈
- 中缀表达式转后缀:数字直接入后缀表达式,' ( '入栈,' ) ':以此把栈中运算符入后缀表达式,直到出现' ( ',其他运算符:优先级小于等于栈顶则不断弹出栈顶入后缀表达式,优先级大于栈顶则入栈
- 递归问题:递归函数的底层实现依赖于栈,理论上来说任一递归函数都可以改写为用栈来实现
- 两个栈实现一个队列:一个栈为instack,一个栈为outstack,插入数据时放入instac,输出数据时输出outstack的栈顶,若outstack为空则将instack的所有元素放入outstack中:两个后入先出即实现先入先出
- 火车调度问题
- 火车调度问题I:火车编号1~n,给出入栈序列,求最大字典序的出栈序列:把编号n前的火车全部入栈(n一定第一个出栈),然后用后缀数组判断n之后每一个即将入栈的元素post[i]是否大于栈顶,若大于栈顶则将元素入栈直至post[i]小于栈顶,开始弹出栈顶直到栈顶又小于post[i]
- 火车调度问题II:火车编号1~n,入栈序列为1,2,3,4……n,给出出栈序列,判断此段序列是否合法:判断栈顶元素是否是序列开头的数A,是的话弹出栈顶;不是的话看数A是否还没入栈,是的话把数A前的数全入栈,若栈顶不是A且数A已经入栈,说明出栈序列不合法
- 单调栈问题
- 接雨水问题:栈中存储柱子数组的下标,当入栈元素>栈顶元素,则进行一次面积计算,并弹出栈顶,实际上会形成从栈顶到栈底的一个递增单调栈
- 给出一段序列,返回每个数左边最近的比其小的数:循环(如果入栈元素<栈顶元素),则不断弹出栈顶 ,因为新入栈元素比栈顶元素小且更靠近下一个数,所以那些元素必不会作为答案输出,弹出结束后每次取出栈顶即为每次的答案,实际上会形成从栈顶到栈底的一个递增单调栈
- 匹配问题:n对数据的序列,每一对数据有前后两元素,下一对的前元素不得在这一对的后元素之前出现
队列
-
概念:操作受限的先入先出的线性表
-
存储结构
-
顺序队列:数组+队头指针+队尾指针
-
//在队尾插入元素,在队头弹出元素 int q[N],front,rear=-1; //插入元素 q[++rear]=x; //弹出队头 front++; //取出队头 q[front]; //判断队列是否为空 if(front<=rear) not empty else empty
-
循环队列:顺序队列+队头指针front==maxsize-1的时候,再进一个位置到0实现循环
-
//在队尾插入元素,在队头弹出元素 int q[N],front,rear; //插入元素 q[rear]=x; rear=(rear+1)%maxsize; //弹出队头 front=(front+1)%maxsize; //取出队头 q[front]; //判断队列是否为空 if(front!=rear) not empty else empty
-
-
-
链式队列:适用于数据元素变动比较大的情况
-
双端队列:允许两端都可以入队和出队的队列
-
-
理论题:依照概念和队列先入先出的特性模拟入队和出队即可
-
实践题
-
遍历问题:层序遍历与宽度优先搜索都依赖于队列实现,详见树篇与图论篇
-
滑窗算法:在给定的一段序列中,动态地维护一段区间,(在有限制的条件下动态地遍历各个区间)
- 单调队列问题
- 一段序列上有一个定长的滑动窗口,每次返回窗口内最大的值:while(如果入队元素>队尾元素) 则不断删除队尾(rear--),因为新入队元素比队尾的元素更大且更晚出窗口,所以那些元素永远不会作为答案输出,每次的队头即为答案,实际上会形成从队尾到队头的一个递增单调队列
- 求给定序列上,长度<=k且在和最大的情况下起始位置尽可能小的子序列,返回该序列起止坐标:将序列元素依次入队,限制条件1:要求最大和,所以当sum小于0时,要不断弹出队首,清空序列;限制条件2:当队列长度大于k时,要弹出队首;限制条件3:当队列有两个及以上元素时,如果队首小于0,就要弹出队首,每次弹出队首或新元素入队后更新sum并检查是否要更新maxsum和答案
- 单调队列问题
-