数据结构与算法(python版)之栈
一、什么是栈
抽象数据类型“栈”是一个有次序的数据集,每一个数据仅从“栈顶”一端加入到数据集中或从数据集中移除,栈具有后进先出LIFO特性。
二、栈的特性:反转特性
进栈和出栈的次序正好相反。计算机操作上的栈,如浏览器的“后退back”按钮,最先back的是最近访问的网页,Word的“Undo”按钮,最先撤销的是最近的操作。
三、栈的6个基本操作
Stack()#创建一个空栈,不包含任何数据项
push(item)#将item加入栈顶,无返回值
pop()#将栈顶数据项移除,并返回,栈被修改
peek()#“窥视”栈顶数据项,返回栈顶的数据项但不移除,栈不被修改
isEmpty()#返回栈是否为空栈
size()#返回栈中有多少个数据项
栈的操作样例
四、用python实现ADT Stack
1.思路:
(1)将ADT Stack实现为python的一个Class;
(2)将ADT Stack的操作实现为Class的方法;
由于Stack是一个数据集,所以可以采用python的原生数据集来实现,选用最常用的数据集List来实现。
2.实现细节:
可以将List的任意一端(index=0或者-1)设置为栈顶。
我们选用List的末端(index=-1)作为栈顶。这样栈的操作就可以通过对List的append和pop来实现。
3.代码实现:
(1)List末端作为栈顶
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
(2)List首端作为栈顶
class Stack:
def __init__(self):
self.items=[]
def push(self,item):
self.items.insert(0,item)
def pop(self):
return self.items.pop(0)
def peek(self):
return self.items[0]
def isEmpty(self):
return self.items==[]
def size(self):
return len(self.items)
(2)首端和末端的区别
①二者的功能完全相同;
②在实现上,push()、pop()、peek()三个函数不同;
③性能不同,栈顶是List首端的版本,其push/pop的复杂度为O(n),而栈顶是List末端的版本,其push/pop的复杂度为O(1)。其主要和List的操作有关。
五、栈的应用一:括号匹配
1.括号平衡规则
(1)每个开括号要恰好对应一个闭括号;
(2)每对开闭括号要正确嵌套;
2.思路
(1)从左到右扫描括号串;
(2)最新打开的左括号,应该最先遇到右括号;
这样,第一个左括号,应该匹配最后一个右括号,这种次序反转的识别,正好符合栈的特性。
3.程序流程图
4.实现源程序
def parChecker(symbolStr):
s=Stack()
index=0
match=True
while index<len(symbolStr) and match:
symbol=symbolStr[index]
if symbol=='(':
s.push(symbol)
else:
if s.isEmpty():
match=False
else:
s.pop()
index=index+1
if s.isEmpty() and match:
return True
else:
return False
5.多种括号的匹配问题
(1)需要修改的地方:
①各种左括号入栈;
②遇到右括号时,检查栈顶左括号和右括号是否属于同一类括号。
(2)实现程序:
def parCheckerMix(symbolStr):
s=Stack()
index=0
match=True
while index<len(symbolStr) and match:
symbol=symbolStr[index]
if symbol in '([{':
s.push(symbol)
else:
if s.isEmpty():
match=False
else:
top=s.pop()
if not isSameType(top,symbol):
match=False
index=index+1
if s.isEmpty() and match:
return True
else:
return False
def isSameType(left,right):
lefts='([{'
rights=')]}'
return lefts.index(left)==rights.index(right)
六、栈的应用二:十进制转换成二进制
1.方法
十进制转换为二进制,采用的是“除以2求余数”的算法,即将整数不断除以2,每次得到的余数就是由低到高的二进制位。
“除以2”的过程,得到的余数是从低到高的次序,而输出是从高到低,所以需要一个栈来反转次序。
2.实现程序
def divideBy2(decNum):
remStack=Stack()
while decNum>0:
#求余
rem=decNum%2
remStack.push(rem)
#整数除
decNum=decNum//2
binStr=''
while not remStack.isEmpty():
binStr=binStr+str(remStack.pop())
return binStr
3.扩展
十进制转换成二进制的算法可以很容易的扩展成十进制转换成N进制。只需要将“除以2求余数”改为“除以N求余数”。
十进制转换为十六进制以下任意进制的代码:
def baseConverter(decNum,base):
digits='0123456789ABCDEF'
remStack=Stack()
while decNum>0:
#求余
rem=decNum%base
remStack.push(rem)
#整数除
decNum=decNum//base
binStr=''
while not remStack.isEmpty():
binStr=binStr+digits[remStack.pop()]
return binStr
七、栈的应用三:表达式转换
1.中缀表达式
2.中缀表达式的优先级
3.全括号中缀表达式
4.前缀和后缀表达式
5.中缀表达式转换为前缀和后缀表达式
6.通用的中缀转后缀表达式
7.通用的中缀转后缀算法流程
示例:
8.通用的中缀转后缀实现程序
输入表达式字符串时,操作符和操作数用空格隔开。
def infixToPostfix(infixexpr):
#定义操作符优先级的字典
prec={}
prec["*"]=3
prec["/"]=3
prec["+"]=2
prec["-"]=2
prec["("]=1
opStack=Stack();
postfixList=[]
#用空格将字符串拆分
tokenList=infixexpr.split()
for token in tokenList:
if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":
postfixList.append(token)
elif token=='(':
opStack.push(token)
elif token==')':
topToken=opStack.pop()
while topToken!='(':
postfixList.append(topToken)
topToken = opStack.pop()
else:# +-*/
while (not opStack.isEmpty()) and (prec[opStack.peek()]>=prec[token]):
postfixList.append(opStack.pop())
opStack.push(token)
while not opStack.isEmpty():
postfixList.append(opStack.pop())
return "".join(postfixList)
print(infixToPostfix('( 4 + 3 ) * 5'))
八、栈的应用四:后缀表达式求值
1.思路
2.实例1:
3.实例2:
4.后缀表达式求值流程
5.实现程序
输入表达式字符串时,操作符和操作数用空格隔开。
def postfixEval(postfixExpr):
operandStack=Stack()
tokenList=postfixExpr.split()
for token in tokenList:
if token in "0123456789":
operandStack.push(int(token))
else:
operandRight=operandStack.pop()
operandLeft=operandStack.pop()
result=doMath(token,operandLeft,operandRight)
operandStack.push(result)
return operandStack.pop()
def doMath(op,opL,opR):
if op=='*':
return opL*opR
if op=='/':
return opL/opR
if op=='+':
return opL+opR
else:
return opL-opR
print(postfixEval('2 3 + 5 * 1 -'))