一、定义
栈(Stack)又名先进后出表,是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算,这一端被称为栈顶,相对的,把另一端称为栈底。
二、栈的面试题
Java中的Stack是通过继承Vector来实现的,这种设计被认为是不良的设计,说说你的看法?
原因:其一,Stack继承自Vector可以复用Vector大量方法,Vector中包含了add(int index,T data)方法,可以在数据的任意位置插入数据,而Stack的定义限制了其只能在栈顶插入数据,这使得Stack在设计上不严谨。
其二,Vector是由数组实现的集合类,他包含了大量集合处理的方法。而Stack之所以继承Vector,是为了复用Vector中的方法,来实现进栈(push)、出栈(pop)等操作。这里就是质疑Stack的地方,既然只是为了实现栈,为什么不用链表来单独实现,只是为了复用简单的方法而迫使它继承Vector(Stack和Vector本来是毫无关系的)。这使得Stack在基于数组实现上效率受影响。
三、栈的经典应用-逆波兰表达式(后缀表达式)
中缀表达式转成后缀表达式步骤说明:
(1) 当读到数字直接送至输出队列中
(2) 当读到运算符t时:
a. 将栈中所有优先级高于或等于t的运算符弹出,送到输出队列中;
注:这句话不好理解,可以说成这样,从栈顶开始,依次弹出操作符,如果操作符优先级大于或者等于t,则送到输出队列,如果比它优先级低的或者遇到了一个左括号就停止。并同时放这个运算符回到栈中
b. t进栈;
(3) 读到左括号时总是将它压入栈中
(4) 读到右括号时,将靠近栈顶的第一个左括号上面的运算符全部依次弹出,送至输出队列后,再丢弃左括号和当前右括号
(5) 中缀表达式全部读完后,若栈中仍有运算符,将其送到输出队列中
例子:中缀表达式:9 + (3 - 1) * 3 + 10 / 2
步骤如下:
表达式: 9 + (3 - 1) * 3 + 10 / 2
1) 9 读到数字9直接输出到队列中
队列:9
运算符栈:
2) + 读到运算符+,因为运算符栈为空,所以直接入栈
队列:9
运算符栈:+
3) ( 读到左括号,直接入栈
队列:9
运算符栈:+ (
4)3 读到数字3直接输出到队列中
队列:9 3
运算符栈:+ (
5)- 读到运算符-,因为运算符栈顶为左括号所以直接入栈
队列:9 3
运算符栈:+ ( -
6)1 读到数字9直接输出到队列中
队列:9 3 1
运算符栈:+ ( -
7)) 读到右括号时,将靠近栈顶的第一个左括号上面的运算符全部依次弹出,送至输出队列后,再丢弃左括号和当前右括号
队列:9 3 1 -
运算符栈:+
8)* 读到运算符*,优先级大于栈顶运算符+,直接入栈
队列:9 3 1 -
运算符栈:+ *
9)3 读到数字3直接输出到队列中
队列:9 3 1 - 3
运算符栈:+ *
10)+ 读到运算符+,运算符栈栈顶开始,依次弹出优先级大于或者等于+,直接输出到队列,直到遇到优先级比它低或者左括号为止,并将+入栈
队列:9 3 1 - 3 * +
运算符栈:+
11)10 读到数字10直接输出到队列中
队列:9 3 1 - 3 * + 10
运算符栈:+
12) / 读到运算符/,优先级大于栈顶运算符+,直接入栈
队列:9 3 1 - 3 * + 10
运算符栈:+ /
13)2 读到数字2直接输出到队列中
队列:9 3 1 - 3 * + 10 2
运算符栈:+ /
14)读完 表达式已读完,将运算符栈中的运算符出栈到队列中,结束。
队列:9 3 1 - 3 * + 10 2 / +
运算符栈: