1. 什么是堆栈
·LIFO,后进先出,只在栈顶(Top)插入删除元素
·数据对象集:一个有0个或多个元素的有穷线性表
·操作集:
- 创建空栈 Stack
- 判断空 is_empty
- 长度,返回栈的元素数量 len
- 压入 push
- 弹出 pop
- 访问栈顶元素 top
2.栈的实现
2.1 栈的顺序表实现
后端插入和删除是O(1)操作,表尾作栈顶
PS:与C语言不同,python中的list类可以自动扩大存储区,所以不会出现栈满的情况
class StackUnderflow(ValueError):
pass
class SStack():
def __init__(self):
self._elems = []
def is_empty(self):
return self._elems == []
def __len__(self):
return len(self._elems)
def push(self,elem):
self._elems.append(elem)
def pop(self):
if self._elems == []:
raise StackUnderflow("in SStack.pop()")
return self._elems.pop()
def top(self):
if self._elems == []:
raise StackUnderflow("in SStack.pop()")
return self._elems[-1]
2.2栈的链表实现
前端插入和删除是O(1)操作,表头作栈顶
PS:虽然顺序表即可满足绝大部分的实际需要,但是扩大存储需要做一次高代价的复制操作,以及完整的大块存储区,而链接技术在这方面较有优势
class Node():
def __init__(self,elem,next_=None):
self._elems = elem
self._next = next_
def __str__(self):
return str(self._elems)
class LStack():
def __init__(self):
self._head = None
self._lenth = 0
def __len__(self):
return self._lenth
def is_empty(self):
return self._head is None
def push(self,elem):
self._head = Node(elem,self._head)
def pop(self):
if self._head is None:
raise StackUnderflow("in LStack.pop()")
p = self._head
self._head = p._next
return p.elem
def top(self):
if self._head is None:
raise StackUnderflow("in LStack.top()")
return self._head.elem
# 栈实现颠置
list1 = list(range(10))
st1 = SStack()
for x in list1:
st1.push(x)
list2 = []
while not st1.is_empty():
list2.append(st1.pop())
print(list2)
3栈的应用
3.1 表达式的变换和计算
# 后缀表达式的计算
def suf_exp_evaluator(line):
exp = line.split()
operators = "+-*/"
st = SStack()
for x in exp: # x为数字时
if x not in operators:
st.push(float(x))
continue
if len(st) < 2: # x 为运算符时,要求栈内元素数量至少为2
raise SyntaxError("Short of operands")
a = st.pop()
b = st.pop()
# 注意b与a的运算顺序
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 len(st) == 1:
return st.pop()
else:
raise SyntaxError("Extra operands")
#try:
line = input('Suffix Expression:')
# if line == "end":
# return
result = suf_exp_evaluator(line)
print(result)
# except Exception as ex:
# print("Error:",type(ex),ex.args)
def trans_infix_suffix(line):
"""将中缀表达式转换为后缀表达式"""
priority = {"(":1,"+":3,"-":3,"*":5,"/":5}
operators = "+-*/()"
st = SStack()
exp = [] # 储存后缀表达式
for x in token(line): # 生成器
if x not in operators:
exp.append(x)
elif st.is_empty() x == '(':
st.push(x)
elif x == ')':
while not st.is_empty() and st.top != '(':
exp.append(st.pop())
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 token(line):
"""逐一生成line中的一个个项"""
i, llen = 0, len(line)
operators = "+-*/()"
while i< llen:
while i < llen and line[i].isspace(): # 若i为空格,跳过
i += 1
if i >= llen:
break
if line[i] in operators:
yield line[i]
i += 1
continue
j = i+1
while (j<llen and not line[j].isspace() and line[j] not in operators):
if ((line[j]=='e' or line[j]=='E') and line[j+1] == '-'):
j += 1
j += 1
yield line[i:j]
i=j
3.2 栈与递归:背包问题
递归:在一个定义中引用了被定义的对象本身。
递归调用的过程 可以 视为向栈里压入元素的过程
递归返回的过程 可以 视为不断取出栈元素的过程
背包问题
一个背包里可放入重量为weight的物品,现有n件物品的集合S,其中物品的重量分别为w0,w1,…,wn-1。问能否选出若干件物品,重量之和正好等于weight。如果存在,该问题就有解。
# hint: n件物品的问题,可以归结为两个n-1件物品的问题。任意子问题有解,原问题就有解
def knap_rec(weight,wlist,n):
# 出口
if weight == 0:
return True
if weight<0 or (weight>0 and n<1):
return False
# 递归主题
if knap_rec(weight-wlist[n-1],wlist,n-1): # 如果选择最后一件物品
print("Item "+str(n)+":",wlist[n-1])
return True
if knap_rec(weight,wlist,n-1): # 如果不选择最后一件物品
return True
else:
return False
wlist = [3,9,2,31,13,28,11,6,40]
a = knap_rec(50,wlist,9)
a