# python 数据结构三 之 栈与队列

2019.07.18 21:24 1956浏览

python数据结构教程第三课

1.栈的顺序表实现
2.栈的链表实现
3.栈的应用——括号匹配、中缀表达式、Ackerman函数

1.队列的循环线性表实现
2.队列的应用——舞伴配对问题

## 一.简介

``````ADT Stack：
Stack(self)             #创建空栈
is_empty(self)          #空栈判断
top(self)               #返回栈顶元素
pop(self)               #返回并删除栈顶元素
push(self,elem)         #元素入栈
len(self)               #返回栈深度
``````

``````ADT Queue:
Queue(self)             #创建空队列
is_empty(self)          #空队列判断
enqueue(self,elem)      #元素入队
dequeue(self)           #元素出队
peek(self)              #返回队首元素
len(self)               #返回队长
``````

## 三.栈的python实现

1.栈的顺序表实现

``````#避免空栈访问
class StackUnderflow(ValueError):
pass

class SStack():
def __init__(self):      #栈初始化
self._elems = []

def is_empty(self):      #空栈判断
return 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 peek(self):         #返回最新进入栈的元素
if self._elems == []:
return None
return self._elems[-1]

def deep(self):         #返回栈深度
return len(self._elems)
``````

``````st1 = SStack()
st1.push(3)
st1.push(5)
while not st1.is_empty():
print(st1.pop())
``````

``````5
3
``````

2.栈的链表实现

``````'''

'''
#LNode结点类
class LNode():
def __init__(self,elem = None,next_ = None):
self.elem = elem
self.next = next_

#基于链接表技术实现的栈类
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
``````

``````#将序列中的元素反序
list1 = list([1,2,3,4,5,6,7,8])
print（list1）
st1 = SStack()
for x in list1:
st1.push(x)
list2 = []
while not st1.is_empty():
list2.append(st1.pop())
print(list2)
``````

``````[1, 2, 3, 4, 5, 6, 7, 8]
[8, 7, 6, 5, 4, 3, 2, 1]
``````

3.栈的应用——括号匹配、中缀表达式、Ackerman函数
1）检测 python程序当中括号不匹配的情况：

``````'''检测python程序中的括号不匹配情况(考虑注释与字符串情况)，发现不匹配时输出不匹配的括号、行号和位置'''
def check_py_parens(py_name):
parens = '()[]{}'
open_parens = '([{'
opposite = {')':'(',']':'[','}':'{'}
def parentheses(py_name):   #括号生成器
row = 0                 #传出python文件中
st0 = SStack()          #不在注释与字符串
flag = 1                #里的括号
for  line in open(py_name):
row += 1
column = 0
for char in line:
if char == '#':
break
elif (char == '\'' or char == '\"') and st0.is_empty():
st0.push(char)
column += 1
flag = 0
elif (not st0.is_empty()) and (char == '\'' or char == '\"') and char == st0.pop():
flag = 1
column += 1
else:
column += 1
if flag == 1:
if char in parens:
yield char,row,column
st = SStack()       #存储括号的栈
for pr,row,column in parentheses(py_name):
if pr in open_parens:  #开括号入栈
st.push(pr)
elif st.is_empty() or st.pop() != opposite[pr]:           #空栈或者不匹配时返回失败
print(pr,row,column)
return False
return True
``````

``````#这里的hello.py可以是任意py程序文本，这里不附文本了
print(check_py_parens('hello.py'))
``````

2）中缀表达式的计算

@建立两个栈，一个用于存储运算符，一个用于存储数字
@从左到右逐个读取文本，遇到数字直接入栈，遇到操作符的情况较为复杂，在中缀表达式中遇到一个操作符时是无法直接计算的，因为程序此时只读取了二元运算符的一个运算对象，还需要读取另一个运算对象，所以，这时需要先将操作符入栈，接下来分情况讨论
@当读到一个操作符，操作符栈为空或该操作符是‘（’时，需要先将该操作符入栈
@当读到的操作符是‘）’时，需要不断弹出一个操作符与两个操作数进行运算，将运算结果重新入栈，直到遇到‘（’，将其弹出
@当遇到的操作符是四则运算时，需要比较该操作符与栈顶操作符的优先级，如果小于等于栈顶的优先级则应该先将栈顶的操作符和两个操作数弹出运算，结果压栈，再将读到的操作读入栈，当大于栈顶的优先级时，则简单的将其压栈，至此，则解决了所有括号与优先度的问题
@不断弹出一个操作符与两个数字，进行运算，将运算结果压栈，直至运算符栈为空

``````#协助函数，输入两个数与一个操作符，得到结果
def op_calculate(a,b,op):
a = float(a)
b = float(b)
if op == '+':
return a+b
elif op == '-':
return b-a
elif op == '*':
return a*b
elif op == '/':
return b/a

#直接的中缀表达式求值函数,只考虑四则运算和括号的情况,输入以空格分开
def infix_calculator(line):
st_num = SStack()       #数字栈
st_op = SStack()        #运算符栈
operator = '+-*/()'
priority = {'+':1,'-':1,'*':2,'/':2,'(':0}

exp = line.split()      #将输入字符串化为列表
for char in exp:        #开始处理表达式
if char not in operator:
st_num.push(char)
elif char in operator:
if st_op.is_empty() or char == '(':
st_op.push(char)
elif char == ')':
while st_op.peek() is not '(':
op = st_op.pop()
a = st_num.pop()
b = st_num.pop()
st_num.push(op_calculate(a,b,op))
st_op.pop()
elif char in '+-*/':
if priority[char] <= priority[st_op.peek()]:
op = st_op.pop()
a = st_num.pop()
b = st_num.pop()
st_num.push(op_calculate(a,b,op))
st_op.push(char)
elif priority[char] > priority[st_op.peek()]:
st_op.push(char)
while not st_op.is_empty(): #处理无括号
op = st_op.pop()        #的剩余运算
a = st_num.pop()
b = st_num.pop()
st_num.push(op_calculate(a,b,op))
return st_num.pop()         #返回结果
``````

``````print(infix_calculator(' ( 3 + 5 ) + 2 * ( 5 - ( 1 + 2 ) )'))
print(infix_calculator(' 3 + 5 * 2'))
``````

``````12.0
13.0
``````

3）Ackerman函数求解
Ackermen函数是栈运用问题中非常经典的一个，举这个例子的目的是为了说明，任一个递归定义的函数，都可以引入一个栈保存中间结果的方式，翻译为一个非递归过程，与此对应，任何一个包含循环的程序都可以翻译为一个不包含循环的递归函数
Ackerman函数如下：

``````Ack(m,n)
= n+1 , m = 0
= Ack(m-1,1) , m != 0 and n = 0
= Ack(m-1,Ack(n-1)) , m != 0 and n != 0
``````

``````def Ackerman(m,n):
if m == 0:
return n+1
elif n == 0:
return Ackerman(m-1,1)
elif n != 0:
return Ackerman(m-1,Ackerman(m,n-1))
``````

``````def Ackerman_stack(m,n):
st1 = SStack()
st2 = SStack()
st1.push(m)
st2.push(n)

while m != 0:
if n != 0:
st1.push(m)
n -= 1
st2.push(n)
elif n == 0:
m = st1.pop() - 1
n = 1
st2.pop()
st1.push(m)
st2.push(1)
while  m == 0 and st2.deep() > 1:
st1.pop()
n = st2.pop()
m = st1.pop()
st2.pop()
m -= 1
n += 1
st1.push(m)
st2.push(n)

return (st2.pop() + 1)
``````

``````print(Ackerman(2,3))
print(Ackerman_stack(2,3))
print(Ackerman(3,3))
print(Ackerman_stack(3,4))
``````

``````9
9
61
125
``````

## 四.队列的python实现

1.队列的循环线性表实现

``````'''基于循环顺序表的队列实现'''

#首先定义由于空而无法完成出队操作造成的异常
class QueueUnderflow(ValueError):
pass

class SQueue():
def __init__(self,init_len = 8): #初始化
self._len = init_len
self._elems = [0]*init_len   #初始长度
self._num = 0

def __len__(self):               #返回队列长度
return self._num

def is_empty(self):              #空队判断
return self._num == 0

def peek(self):                  #返回队首元素
if self._num == 0:
raise QueueUnderflow

def dequeue(self):          #返回并删除队首元素
if self._num == 0:      #并更新队首位置
raise QueueUnderflow
self._head = (self._head + 1) % self._len
self._num -= 1
return e

def dequeue_tail(self):     #返回并删除队尾元素
if self._num == 0:
raise QueueUnderflow
e = self._elems[(self._head+self._num-1) % self._len]
self._num -= 1
return e

def enqueue(self,e):        #元素入队
if self._num == self._len:
self.__extend()
self._elems[(self._head+self._num) % self._len] = e
self._num += 1

if self._num == self._len:
self.__extend()
self._head = (self._head + 1) % self._len

def __extend(self):         #扩展存储空间
old_len = self._len
self._len *= 2
new_elems = [0]*self._len
for i in range(old_len):

``````

2.队列的应用——舞伴配对问题

``````#舞伴配对问题
def party(men_num,women_num,times):
men = SQueue(men_num)
men_dance = SQueue()
women = SQueue(women_num)
women_dance = SQueue()
for i in range(men_num):
men.enqueue(i+1)
for i in range(women_num):
women.enqueue(i+1)
t = 0
while t < times:
print('第'+str(t+1)+'轮舞会配对：')
n = 0
m = 0
while n < men_num and m < women_num:
men_dance.enqueue(men.dequeue())
women_dance.enqueue(women.dequeue())
n += 1
m += 1
if women_num == men_num:
print('全部入场')
elif women_num == m:
print('下一场次第一个出场的本次未配对男性的序号: ' + str(men._head+1))
elif men_num == n:
print('下一场次第一个出场的本次未配对女性的序号: ' + str(women._head+1))
while not men_dance.is_empty():
men.enqueue(men_dance.dequeue())
women.enqueue(women_dance.dequeue())
t += 1
``````

``````party(10,12,3)
``````

``````第1轮舞会配对：

``````

0人点赞

• 推荐
• 评论
• 收藏
• 共同学习，写下你的评论

0/150