栈和队列主要用于在计算过程中保存临时数据,是一种缓冲存储结构。
- 栈是保证元素后进先出关系的结构
- 队列是保证元素先进先出的结构
栈的实现
栈抽象数据结构
ADT Stack:
Stack(self) #创建空栈
is_empty(self) #判断栈是否为空
push(self, elem) #将元素elem入栈
pop(self) #删除栈最后加入的元素并返回
top(self) #取得最后加入的元素,不删除
栈是后进先出的方式,在采用表来实现的时候要考虑操作效率问题,栈的操作只在表的一端进行,不涉及其他部分,所以要用表操作效率最高的一端来作为栈顶:
- 顺序表的后端插入和删除操作都为
O(1)
O
(
1
)
- 链接表的前端插入和删除操作都为
O(1)
O
(
1
)
栈的顺序表实现
自定义异常:
class StackUnderflow(ValueError):
pass
Python
P
y
t
h
o
n
的
list
l
i
s
t
为动态顺序表,在
list
l
i
s
t
的基础上实现栈,假设表
Lst
L
s
t
:
- 建立空栈对应于创建一个空表[],判断空栈对应于检查是否为空表。
-
list
l
i
s
t
为动态顺序表,作为栈不会满。
- 入栈操作为在表的尾端加入元素,对应于
Lst.append(x)
L
s
t
.
a
p
p
e
n
d
(
x
)
。
- 访问栈顶元素为
Lst[−1]
L
s
t
[
−
1
]
。
- 出栈操作对应于
Lst.pop()
L
s
t
.
p
o
p
(
)
。
class SStack():
def __init__(self):
self._elems = []
def is_empty(self):
return self._elems == []
def top(self):
if self._elems == []:
raise StackUnderflow("in SStack.top")
return self._elems[-1]
def push(self, elem):
self._elems.append(elem)
def pop(self):
if self._elems == []:
raise StackUnderflow("in SStack.top")
return self._elems.pop()
栈的链接表实现
链接栈比起顺序栈来有以下优点,不需要再表满的时候扩大而引起高代价操作,不需要完整的大面积存储区。
class LStack():
def __init__(self):
self._top = None
def is_empty(self):
return self._top is None
def top(self):
if self._top is None:
raise StackUnderflow("in LStack.top")
return self._top.elem
def push(self,elem):
self._top = LNode(elem, self._top)
def pop(self):
if self._top is None:
raise StackUnderflow("in LStack.pop")
p = self._top
self._top = p.next
return p.elem
栈的应用
栈可以方便的存取信息,常作为算法或程序里的辅助存储结构,临时保存信息;具有先进后出的性质,利用这种性质可以保证特定的存储顺序。
颠倒一组元素顺序:
st1 = SStack()
for x in list1:
st1.push(x)
list2 = []
while not st1.is_empty():
list2.append(st1.pop())
表达式的计算
- 中缀表达式:( 3 - 5 ) * ( 6 + 17 * 4 ) / 3
- 前缀表达式:/ * - 3 5 + 6 * 17 4 3
- 后缀表达式:3 5 - 6 17 4 * + * 3 /
在运算符的元数已知且唯一的情况下:对于前缀表示,每个运算符的运算对象就是它后面出现的几个完整表达式,表达式的个数由运算符的元数确定;对于后缀表示,每个运算符的运算对象就是它前面出现的几个完整表达式,表达式个数由运算符元数确定。
从这里可以看出,后缀表达式很适合用栈来描述:运算对象依次入栈,扫描到运算符时,根据运算符元数从栈中取出运算对象,计算出结果再入栈,作为后边运算符的运算对象,计算完成时栈中只剩一个对象。
后缀表达式的计算
假设只有四个基本运算符(都是二元运算符),表达对象都是浮点数,有一个函数nextItem(),调用它就会得到输入里的下一个项(可能是运算符,也可能是运算对象),基本结构如下:
while 还有输入:
x = nextItem()
if is_operand(x): #检查是否为运算对象
st.push(float(x))
else: #为运算符时
a = st.pop() #都是二元运算符,所以取出两个运算对象
b = st.pop()
计算结果m
st.push(m) #把计算结果在压入栈,作为下一个运算符的运算对象
定义一个函数来把表示表达式的字符串转换为项的表:
def suffix_exp_evaluator(line): #这里分割表达式用的split(),更复杂的输入需要相应的分割方法
return suf_exp_evaluator(line.split())
处理运算符时栈中的运算对象不能少于两个,处理完成时,栈中元素应为一个,所以栈应该有一个检查深度的方法:
class ESStack(SStack):
def depth(self):
return len(self._elems)
最终实现:
def suf_exp_evaluator(exp):
operators = "+-*/"
st = ESStack()
for x in exp:
if x not in operators:
st.push(float(x))
continue
if st.depsh() < 2:
raise SyntaxError("Short of operand(s).")
a = st.pop()
b = st.pop()
if x == "+":
c = b + a
elif x == "-":
c = b - a
elif x == "*":
c = b * a
elif x == "/":
c = b / a
else:
break
st.push(c)
if st.depth() == 1:
return st.pop()
raise SyntaxError("Extra operand(s).")
def suffix_exp_calculator():
while True:
try:
line = input("Suffix Expression:")
if line == "end":
return
res = suffix_exp_evaluator(line)
print res
except Exception as ex: #Exception是所有异常的基类,这是用来捕获所有异常,然后把异常约束于ex,后边在打印出异常信息
print "Error:", type(ex), ex.args
中缀表达式到后缀表达式的转换
对中缀表达式依次扫描,运算对象可以直接弹出,运算符就需要考虑一下优先级的问题,再有就是括号的问题。
priority = {"(":1, "+":3, "-":3, "*":5, "/":5} #设立优先级系数
infix_operators = "+-*/()"
def trans_infix_suffix(line):
st = SStack()
exp = []
for x in tokens(line):
if x not in infix_operators: #如果是运算对象,直接加入列表中
exp.append(x)
elif st.is_empty() or x == '(': #左括号进栈
st.push(x)
elif x == ')':
while not st.is_empty() and st.top() != '(': #栈不为空且栈顶不为(
exp.append(st.pop()) #把栈顶加入到exp
if st.is_empty(): #如果为空,错误
raise SyntaxError("Missing '('.")
st.pop()
else: #运算符分支
while (not st.is_empty() and priority[st.top()] >= priority[x]):
exp.append(st.pop())
st.push(x)
while not st.is_empty():
if st.top() == '(':
raise SyntaxError("Extra '('.")
exp.append(st.pop())
return exp
def tokens(line):
i, llen = 0, len(line)
while i < llen:
while line[i].isspace():
i += 1
if i >= llen:
break
if line[i] in infix_operators:
yield line[i]
i += 1
continue
j = i + 1
while (j < llen and not line[j].isspace() and line[j] not in infix_operators):
if ((line[j] == 'e' or line[j] == 'E') and j+1 < llen and line[j+1] == '-'):
j += 1
j += 1
yield line[i:j]
i = j
def test_trans_infix_suffix(s):
print s
print trans_infix_suffix(s)
print "Value:", suf_exp_ovaluator(trans_infix_suffix(s))