《数据结构与算法分析 Java语言描述》 栈

栈模型

  是限制插入和删除操作只能在一个位置上进行的表,该位置是表的末端,叫做栈的顶端。对栈的基本操作有push(进栈)和pop(出栈),前者相当于插入,后者则是删除最后的插入元素。
栈模型

栈的实现

  由于栈是一个表,故可以用任何实现表的算法来实现栈,当然,ArrayList和LinkedList都支持栈操作,下面我们给出两种非常流行的实现方法,一种方法使用链式结构,另一种方法使用数组。

栈的链表实现

  栈的第一种实现方法是使用单链表。通过在表的顶端插入来实现push,通过删除表顶端元素实现pop。top操作只是考查表顶端元素并返回它的值。有时pop操作和top操作合二为一。

栈的数组实现

  另一种实现方法避免了链表而且是最流行的解决方案。模仿ArrayList的add操作来实现栈的方法非常简单。每个栈相关的操作是theArray和topOfStack,对于空栈,topOfStack为-1。为了将某个元素推入栈中,我们使topOfStack增加1后,将theArray[topOfStack]赋予x值,为了弹出栈元素,我们置返回值为theArray[topOfStack]后,然后将topOfStack减1。

  注意,这些操作需要一定的边界操作,比如在pop操作时,检查栈是否为空;在push操作时,检查栈是否已满。

栈的应用

平衡符号

  在编译器检查程序语法时,比如缺少一个符号(如遗漏一个花括号或注释起始符)等。简单的算法过程如下:

  做一个空栈。读入字符直到文件结尾,当读入到的字符是一个开符号,比如[{("'等,则将其推入栈中。如果读入到的字符是一个闭符号,比如]})"',则当栈空时报错;否则将栈元素弹出,如果弹出的元素不是对应的开放符号,则报错。在文件结尾时,如栈非空则报错。
在这里插入图片描述

后缀表达式

  假设我们要实现一个简单的计算机程序,比如输入的自然表达式是:4.99+5.99+6.99*1.06=? 随着计算机的不同,这个结果的答案也不同,因为有些简单的计算机时逐个进行计算,而较为先进的计算机知道乘法的优先级别高于加法的优先级别。
  典型的计算过程可以是先将4.99与5.99相加并存入A1,然后将6.99与1.06相乘并把答案存入A2,最后将A1与A2相加并存入A1。我们可以将这种操作顺序书写为:4.99 5.99 + 6.99 1.06 * +
  这种记法叫做后缀或逆波兰记法。计算这种问题最好的方法是使用一个栈,当遇到一个数时就把它推入栈中;在遇到一个运算符时就将该算符作用到从栈顶弹出的两个数上,再将所得的结果推入栈中。比如对于后缀表达式:
6 5 2 3 + 8 * + 3 + *

下一个符号
6
65
6 52
6 5 23
6 5 2 3+
6 5 58
6 5 5 8*
6 5 40+
6 453
6 45 3+
6 48*
288

  计算一个后缀表达式所花费的时间时O(N),而且更为重要的是,当一个表达式以后缀号给出的时候,我们没必要知道任何优先级别的规则,这个一个明显的优点。

中缀到后缀的转换
  那么如何得到一个后缀表达式呢?这里同样可以使用栈将一个标准的中缀表达式转换为后缀表达式。这里我们只允许操作+,-,*,/,(,),并用普通的优先级法则将一般的问题浓缩为小规模的问题,此外还要假设表达式是合法的,假设中缀表达式为:a + b * c + (d * e + f)* g
将此转换为后缀表达式。
  当读到一个操作数的时候,立即把它放到输出中。操作符不立即输出,而是必须先存入到某个栈中,这里我们规定* / + -的优先级别依次降低,其中* 和 /的优先级别系统,而+和-的优先级别相同,最后:除非是在处理一个)的时候,否则我们绝不会从栈中移走(。读入的操纵符,从栈顶中pop出所有优先级别大于或等于该操作符的元素。
  最后,如果读入到输入的末尾,我们将栈元素弹出直到该栈变成为空栈,将符号写到输出中
  示例如下:

输出下一个元素
a
a+
+ab
+a b**优先级大于+,故不弹出
+ *a bc
+ *a b c+
+a b c * +(+优先级小于*,故*弹出;而栈底的+号与当前操纵符有相同优先级,故弹出;将当前操作符+推入栈中
+ (a b c * +d
+ (a b c * + d*
+ ( *a b c * +de
+ ( *a b c * + d e+
+ ( +a b c * + d e *f
+ ( +a b c * + d e * f)
+a b c * + d e * f +*读入直到一个),因此将栈元素直到(弹出
+ *a b c * + d e * f +g
+ *a b c * + d e * f + g
a b c * + d e * f + g * +

方法调用
  当存在方法调用时候,需要存储很多重要信息,比如寄存器的值(对应变量的名字)和返回地址等。然后控制转移到新方法,该方法自由地用它的一些值代替这些寄存器。如果它又进行其他的方法调用,那么它也遵循相同的过程。显然,所有全部工作均可由一个栈来完成,而这正是实现递归的每一种程序程序设计语言中实际发生的事实。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值