一、栈的应用(书接上回)
(1)逆波兰表达式求值:150. 逆波兰表达式求值 - 力扣(Leetcode)
A. 思路
a. 什么是逆波兰表达式:
<1>之前学过的数学表达式都是中缀表达式,即运算符在两个操作数之间的表达式,括号表示优先级最高,如:((1 + 2)* 3) + 4) / 5
<2>后缀表达式:运算符在两个操作数之后,不存在括号。上面的转换为后缀表达式为:12+3*4+5/
<3>中缀表达式比较符合人脑思维,但是计算机中很难使用中缀表达式进行计算。计算机更适合使用后缀表达式进行计算
b. 人脑思维

c. 实现思路:
<1>使用一个栈来存储操作数
<2>具体逻辑:
依次循环扫描tokens
当碰到是操作数的时候压入栈中;
当碰到的是操作符的时候,连续出栈两次,进行相对应的计算(switch分支),将计算后的结果压入栈中
循环外直接出栈的值即为表达式所求
<3>难点1:tokens给的是一个String类型数组,如何判断是操作数还是操作符?
解决思路:让String类型转换为数字类型,如果能转换成功,则不会抛出异常,说明是操作数;如果转换失败抛出异常NumberFormatException,则说明是运算符。
使用try-catch方法来捕捉异常处理
这里将其定义为一个isNum的方法,捕捉到则返回false,没捕捉到则返回true
<4>注意点2:在进行相应计算的时候,如果是减法/除法,要注意第一个弹出的是减数/除数,第二个弹出的是被除数和被减数
<5>补充知识点:增强switch语句,代码如下

B. 实现代码

(2)出栈入栈次序匹配:剑指 Offer 31. 栈的压入、弹出序列 - 力扣(Leetcode)
A. 代码思路:
a. 设置一个栈来模拟入栈出栈过程,定义一个变量j 来遍历popped数组
b. 通过循环遍历pushed数组
循环内部:
stack.push[pushed[i]]; //模拟入栈过程
//当栈不为空,且栈顶元素为popped[j]的时候就出栈,因为可能连续删除,因此使用while循环而不是if语句
while(!stack.isEmpty() && stack.peek() == popped[j]){
stack.pop();
j++;
c. 最终栈为空的时候,说明popped数组一定是合法的;栈不为空的时候则一定是不合法的
return stack.isEmpty();
B. 人脑思路:从popped入手,当popped[j]元素出栈的时候,则说明入栈顺序中它包括它之前的元素都已经入栈了,即栈顶元素 == popped[j]元素的时候出栈。不相等时候,说明还没到删除的时候,还得入栈。当popped数组遍历完成后,若栈为空,则说明popped序列合法;若不空,则说明出栈顺序不合法导致有元素未出正确出栈,说明popped序列不合法
C. 实现代码:

(3)补充知识:栈,虚拟机栈,栈帧的概念区分
A. 这里的栈:此处的栈是一种数据结构,线性表的一种。满足后进先出的特点,保存在栈中的元素都是相同的数据类型
B. 虚拟机栈、操作系统栈:是一种概念性的栈,保存在栈中的元素不一定是相同的数据类型,底层的实现到底是不是用的栈,用的是链式栈还是顺序栈都不是关键
C. 栈帧:函数调用过程到结束的一个体现。一个函数从调用开始到销毁其中占用的空间,内部的局部变量都统一放到该函数的栈帧中(一种类型,专门描述函数在操作系统内部的调用过程)
二、队列
1. 队列:操作受限的线性表
A. 只能从队列的一端添加元素(队尾),从队列的另一端删除元素(队首)。
B. 基本操作:
入队(offer):向队列中添加元素
出队(poll):从队列中移除元素
C. FIFO:队列满足先进先出的特点
2. 广义的队列分类:
A. 普通队列:一般采用链式队列(单链表,尾插头删)。(原因:使用顺序表,涉及头部的插入或者删除效率为O(n),效率比单链表低一点)
B. 循环队列:可以从队尾走到队首,使用定长数组实现。
C. 优先级队列(非线性结构):一般都是先进先出,但是优先级较高的元素可以优先出队,内部使用堆这个数据结构来实现(JDK默认使用的是小根堆——完全二叉树)
3. 普通队列(单链表实现)的代码实现
A. 接口的定义

B. 普通队列类的定义和基本方法的覆写

C. 使用测试

三、使用栈来模拟实现队列,使用队列来模拟实现栈(重中之重)
1. 两个栈来模拟实现队列:232. 用栈实现队列 - 力扣(Leetcode)
A. 思路:
a. s1用来存储队列,栈顶为队首; s2作为辅助,用来缓存s1之前的元素,保证之前元素的相对顺序正确
b. 基本操作逻辑
<1>入队操作
首先检测s1中是否有元素,如果有元素则将s1中元素依次出栈,入栈s2中
然后添加新元素
最后再将s2中依次出栈,入栈s1
<2>其他操作:由于s1是存储的队列,因此使用s1即可
B. 关于思路的一个补充:
为什么不能用类似于两个队列模拟栈的方式交换引用:因为队列不会改变元素的相对顺序,但是栈会
a. 比如,下列图示是队列模拟栈:队列元素顺序为1,2,3,4,5.我不论是先添加5再出队入队size次,还是先全部出队再添加5,再全部入队。元素的相对顺序不会改变,为了实现方便,我们选择前者。

b. 对于栈模拟队列来说,先添加再出栈入栈,则顺序会发生改变,正因为如此,没办法用一个栈模拟队列,为了保证元素的相对顺序不变必须有一个辅助
例如下图:
c. 因为对于栈模拟队列来说:我们先出栈,然后添加元素,再入栈,则相对顺序不会发生改变

C. 代码:

2. 两个队列来模拟实现栈:225. 用队列实现栈 - 力扣(Leetcode)
A. 思路:
a. 一个队列q1存储栈,另一个队列q2作为辅助
b. 具体模拟思路:
<1>插入操作:首先将待插入元素入q2队,然后q1中的所有元素依次出队,然后入q2队,此时发现q2队存储即为栈,因此调换q2和q1的引用即可。
<2>删除:q1.poll()
<3>查看栈顶元素:q1.peek()
<4>判断栈是否为空:q1.isEmpty
B. 代码实现

3. 一个队列模拟栈
A. 思路:
a. 思想:首先添加元素,然后每次除了新添加的元素,其他元素依次出队再入队
b. 一个难点:如何确定出队次数

B. 代码实现:

805

被折叠的 条评论
为什么被折叠?



