数据结构(Java)——Day11(栈的应用收尾,队列, 使用栈来模拟实现队列,使用队列来模拟实现栈)

一、栈的应用(书接上回)

(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. 代码实现:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值