栈和队列也是数据结构中经常用到的一种容器.栈是先进后出FILO,队列是先进先出FIFO.在C语言中,栈可以用数组或者链表来实现,在python中,list也就是列表也可以当做栈使用.比如在尾部压入元素可以用append的方法,压出元素可以用pop的方法.访问栈定元素可以用list[-1]的方法.但是list也同时提供了一大批栈不应该支持的操作,比如栈中未弹出的元素是存在的,但是list可以删除任意元素.因此为了实现完全的栈功能,我们可以用一个类来实现栈并将list隐藏在这个对象的内部.代码如下
在内部定义了一个私有变量_elem.使得无法从外部实例进行访问,也就无从修改
class stack():
def __init__(self):
self._elem=[]
def is_empty(self):
return self._elem == []
def top(self):
if self._elem == []:
raise BaseException(‘top:stack empty‘)#当列表为空的时候,抛出异常
return self._elem[-1]
def push(self,elem):
self._elem.append(elem)
def pop(self):
if self._elem == []:
raise BaseException(‘pop:stack empyt‘)
return self._elem.pop()
if __name__=="__main__":
s=stack()
s.push(5)
s.push(4)
s.push(10)
print s.pop()
前面这个例子是通过内置的list来实现了栈队列,我们还可以采用列表的形式来实现栈
代码如下:
这里首先引用了第三章中的Lnode对象
class Lnode(object):
def __init__(self,elem,next_=None):
self.elem=elem
self.next=next_
class Stack_node():
def __init__(self):
self._top=None
def is_emepy(self):
return self._top == None
def top(self):
if self._top== None:
raise BaseException("top:stack is empty")
return self._top.elem
def push(self,elem):
self._top=Lnode(elem,self._top) #通过传入上一个Lnode对象进行链表链接
def pop(self):
if self._top == None:
raise BaseException(‘pop:stack is empty‘)
p=self._top
self._top=p.next
return p.elem
if __name__=="__main__":
s=Stack_node()
s.push(4)
s.push(10)
print s.pop()
前面2个列子介绍了栈的两种实现方式,下面来看下栈的具体应用.首先来看第一个例子.括号匹配.在数字运算中有(),[],{}三种符号来进行各种封闭的运算.对于一个正常的计算公式而言,当前比括号应该与前面最近的尚未匹配的开括号匹配,下一个闭括号应与前面次进的括号匹配.因此在这里我们就可以应用到栈来实现括号的匹配.来看下具体的实现
def check_parents(text):
#首先定义parents代表所有的括号形式,然后定义open_parents表示所有的开括号
然后定义oppsites一个字典,代表开括号和闭括号的对应关系.
parents=‘()[]{}‘
open_parents=‘([{‘
oppsites={")":"(","]":"[","}":"{"}
def parent_generate(text):
i,text_len=0,len(text)
while True:
while i< text_len and text[i] not in parents: #当不属于括号的,则继续往后
i+=1
if i >= text_len:
return
yield text[i],i #属于括号形式的则通过yield返回
i+=1
s=stack()
for pt,i in parent_generate(text): #调用parent_generate生成器
if pt in open_parents: #如果是开括号则进栈
s.push(pt)
elif s.pop()!=oppsites[pt]: #否则是闭括号,则与最近的一次进栈的括号进行比较,看是否匹配,如果不匹配则提示umatch
print ‘unmatch was found at %d‘ % i
return False
else: #如果匹配则不做任何操作
pass
print ‘all are matched‘
return True
if __name__=="__main__":
check_parents(‘{[(a+b)+c]/3+1}+2‘)
运行结果:
/usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py
all are matched
如果修改下传入的text为check_parents(‘{[(a+b)+c/3+1}+2‘),少了一个].则会提示umatch,并指示不匹配的位置.
/usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py
unmatch was found at 13
后缀表达式
算是表达式有三种表达方式 中缀表达式,前缀表达式,后缀表达式.来看下三种表达式的区别
中缀表达式: (3-5)*2+4
前缀表达式:+*-3524
后缀表达式:35-2*4+
可以看到中缀表达式就和我们平常书写的运算公式是一样的,中缀表达式就是将运算符号写在运算符的前面,而后缀表达式就是将运算符号写在运算符的后面
其实表达式之间的转换很简单,可以参考如下的方法:
给出一个中缀表达式:a+b*c-(d+e)
第一步:按照运算符的优先级对所有的运算单位加括号:式子变成拉:((a+(b*c))-(d+e))
第二步:转换前缀与后缀表达式
前缀:把运算符号移动到对应的括号前面
则变成拉:-( +(a *(bc)) +(de))
把括号去掉:-+a*bc+de 前缀式子出现
后缀:把运算符号移动到对应的括号后面
则变成拉:((a(bc)* )+ (de)+ )-
把括号去掉:abc*+de+- 后缀式子出现
那么这几种表达式有什么意义呢,中缀表达式对于我们正常手写运算来说很形象,但是对于计算机来说就很抽象了,而前缀表达式和后缀表达式来说则很形象.比如3+(2-5)*6/3这个表达式,对应的后缀表达式为3 2 5 - 6 3 / * +
那么在计算借助到栈的方法进行运算
1 如果是数字则直接进栈此时在栈的数据为523
2 遇到运算符则将栈顶的2个数字出栈进行计算,此时遇到-则5,2出栈,进行运算2-5=-3并将-3重新入栈.并继续遍历表达式,此时栈的数据为36-33
3 遇到/则继续出栈2个数字进行运算6/3=2并将2入栈,此时栈的数据为2-33
4 遇到*则继续出栈2个数字进行运算2*-3=-6并入栈,此时栈数据为-63
5 遇到+则出栈2个数字进行运算-6+3=-3并入栈,此时栈数据为-3
这样就得到了最终的数据.从上面的表示可以看出后缀表达式对于计算机的运算来说很方便,下面就来看下后缀表达式的实现:以:3+2*5-6/3为例.
有个数据结构一个是后缀表达式队列
一个是运算符栈队列
a 遇到数字则加入后缀表达式列表
b遇到运算符或者是(则进运算符栈
c当遇到准备进栈的运算符优先级小于或等于栈顶的运算符,则将栈顶运算符出栈加入到后缀表达式列表中
d如果准备进栈的运算符优先级大于栈顶的运算符则继续进栈
第一步:数字3进入后缀表达式,同时+进入运算符栈
后缀表达式队列 运算符栈
3 +
第二步:数字2进入后缀表达式,同时*进入运算符栈
后缀表达式队列 运算符栈
32 *+
第三步:数字5进入后缀表达式,同时-准备进入运算符栈,由于-的优先级小于当前栈顶的*,因此*出栈并加入到后缀表达式队列中,并且-和+的优先级相同,因此+继续出栈并加入到后缀表达式队列中.最后-进栈
后缀表达式队列 运算符栈
325*+ -
第四步:数字6进入后缀表达式,/进入运算符栈
后缀表达式队列 运算符栈
325*+6 /-
第五步:数字3进入后缀表达式.此时将运算符依次出栈链接到后缀表达式队列之后.
后缀表达式队列 运算符栈
325*+63 /-
最终生成的后缀表达式为325*+63/-
下面来看下具体的代码实现,首先是后缀表达式生成的代码:
def trans_infix_suffix(line):
s=stack() #运算符栈
exp=[] #后缀表达式队列
priority={"(":1,"+":3,"-":3,"*":5,"/":5} #生成运算符优先级大小的字典
infix_operator="+-/*()"
for x in line:
if x not in infix_operator: #如果不是运算符号,则进后缀表达式队列
exp.append(x)
elif s.is_empty() or x == ‘(‘: #如果是(则直接进栈
s.push(x)
elif x == ‘)‘: #如果是),则运算符出栈并加入到后缀表达式中,直到遇见'('
while not s.is_empty() and s.top() != ‘(‘:
exp.append(s.top)
if s.is_empty():
raise BaseException(‘stack is empty‘)
s.pop() # '(' 出栈
else: #比较栈顶的元素和当前运算符的优先级,如果大于则出栈加入到后缀表达式队列中.知道栈顶元算符优先级小于当前运算符的优先级
while not s.is_empty() and priority[s.top()] >= priority[x]:
exp.append(s.pop())
s.push(x) #当前运算符入栈
while not s.is_empty(): #栈运算符的运算符全部出栈加入到后缀表达式队列中.
if s.top() == ‘(‘:
raise BaseException(‘extra ( found‘)
exp.append(s.pop())
return exp
得到了后缀表达式,那么下一步就是要针对后缀表达式进行运算了,代码如下:
def exp_calculate(exp):
operators=‘+-/*‘
s=stack()
for x in exp:
if x not in operators: #如果是非运算符,则直接进栈
s.push(int(x))
continue
if s.depth() < 2:
raise BaseException(‘lack of stack element‘)
a=s.pop() #否则遇到运算符,则出栈2个数字进行运算
b=s.pop()
if x == ‘+‘:
c=b+a
elif x == ‘-‘:
c=b-a
elif x == ‘*‘:
c=b*a
elif x == ‘/‘:
c=b/a
else:
break
s.push(c) #将元算得到的数字进栈
if s.depth() == 1: # 运算完返回最终的结果
return s.pop()
对于3+2*5-6/3这个表达式,得到运算结果为11.运算正确
/usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py
[‘3‘, ‘2‘, ‘5‘, ‘*‘, ‘+‘, ‘6‘, ‘3‘, ‘/‘, ‘-‘]
11