堆栈是处理计算机科学中语言构造的非常重要的数据结构。 我们可以想到的任何符号都有某种嵌套符号,必须以平衡的顺序进行匹配。
堆栈定义
•是已添加项目的有序集合,从同一端添加和移除
•最后添加的项目是第一个删除,也称为后进先出
•最新添加的项目在顶部/顶部,最旧的在底部/后部
自助餐厅都有一叠托盘或盘子,人们可以将新盘子放在旧盘子顶部,为下一个排队的顾客发现新的托盘或盘子。堆叠的书唯一可见封面的书是最上面的书。要访问堆栈中的其他对象,我们首先要删除位于它们顶部的对象。删除的顺序与放置的顺序完全相反。堆栈很重要,因为它们可用于反转项目的顺序。图片来自于http://www.pas.rochester.edu/~rsarkis/csc162/_static/lectures/Stacks.pdf
图片来自于http://www.pas.rochester.edu/~rsarkis/csc162/_static/lectures/Stacks.pdf图片来自于http://www.pas.rochester.edu/~rsarkis/csc162/_static/lectures/Stacks.pdf
堆栈抽象数据类型由以下结构和操作定义。堆栈被构造为项目的有序集合,其中将项目添加到称为“顶部”的末端或从末端移除。堆栈式有序的LIFO。
•Stack()创建一个空的新堆栈。它不需要参数,并返回一个空堆栈。
•push(item)将新项目添加到堆栈顶部。它需要该项目,但不返回任何内容。
•pop()从堆栈中删除顶层项目。它不需要任何参数并返回项目。堆栈已修改。
•peek()返回堆栈顶部的项目,但不会将其删除。它不需要任何参数。堆栈未修改。
•is_empty()测试以查看堆栈是否为空。它不需要任何参数,并返回一个布尔值。
•size()返回堆栈中的项目数。它不需要参数,并返回整数。
与面向对象的编程语言一样,Python选择抽象数据类型(如堆栈)的选择实现是创建新类。 此外,要实现作为元素集合的堆栈,可以利用Python提供的原始集合的强大功能和简单性。 我们将使用列表。Python中的list提供了有序的收集机制和方法。 例如,如果有列表[2、5、3、6、7、4],则只需确定列表的哪一端将被视为堆栈的顶部,而哪一端将作为低端。 之后就可以使用列表方法(例如append和pop)来实现操作。
#stack 选择尾端作为先读取的地方
class Stack:
def __init__(self):
self.items = []
def is_empty(self):
return self.items == []
def push(self,item):
return self.items.append(item) #尾端添加
def peek(self):
return self.items[len(self.items)-1]
def pop(self):
return self.items.pop()
def size(self):
return len(self.items)
(注意item代码的单复数)
注意:我们可以选择列表来实现堆栈,其中顶部在开头而不是结尾。在这种情况下,先前的pop和append方法将不再起作用,我们将使用pop和insert显式索引位置0(列表中的第一项)
#stack 选择首端作为先读取的地方
class Stack:
def __init__(self):
self.items = []
def is_empty(self):
return self.items == []
def push(self,item):
return self.items.insert(0,item) #顶端添加
def peek(self):
return self.items[0]
def pop(self):
return self.items.pop(0)
def size(self):
return len(self.items)
这两种实现的性能存在差异。append和pop()操作都是 (1),意味着无论堆栈上有多少个项目,第一个实现都将在恒定时间内执行推入和弹出操作。第二种实现的性能,因为insert(0)和pop(0)操作都将需要 ( )来生成大小为的堆栈。即使在逻辑上是等效的,但执行基准测试时的计时有很大不同。
平衡的括号表示每个开始符号都有一个对应的结束符号,并且括号正确嵌套。 以下平衡的括号字符串:
(()()()())
(((())))
(()((())()))
不平衡的括号字符串:
((((((())
()))
(()()(()
参考图书 :Problem Solving with Algorithms and Data Structures, Release 3.0
其中有一段介绍stack来解决balanced parentheses的,放在这里大家看一下作者的陈述:
To solve this problem we need to make an important observation. As you process symbols from left to right, the most recent opening parenthesis must match the next closing symbol. Also, the first opening symbol processed may have to wait until the very last symbol for its match. Closing symbols match opening symbols in the reverse order of their appearance; they match from the inside out. This is a clue that stacks can be used to solve the problem.
Once you agree that a stack is the appropriate data structure for keeping the parentheses, the statement of the algorithm is straightforward. Starting with an empty stack, process the parenthesis strings from left to right. If a symbol is an opening parenthesis, push it on the stack as a signal that a corresponding closing symbol needs to appear later. If, on the other hand, a symbol is a closing parenthesis, pop the stack. As long as it is possible to pop the stack to match every closing symbol, the parentheses remain balanced. If at any time there is no opening symbol on the stack to match a closing symbol, the string is not balanced properly. At the end of the string, when all symbols have been processed, the stack should be empty.
1) the most recent opening parenthesis must match the next closing symbol
2)Closing symbols match opening symbols in the reverse order of their appearance
重点是左边符号与右边符号相匹配,而且要遵守前后顺序
Starting with an empty stack ----》
process the parenthesis strings from left to right----》
If a symbol is an opening parenthesis, push it on the stack as a signal that a corresponding closing symbol needs to appear later.
If, on the other hand, a symbol is a closing parenthesis, pop the stack. As long as it is possible to pop the stack to match every closing symbol, the parentheses remain balanced.
If at any time there is no opening symbol on the stack to match a closing symbol, the string is not balanced properly.
At the end of the string, when all symbols have been processed, the stack should be empty
解析:
def par_checker(symbol_string): #首先定义函数
s = Stack() #create a stack object 建立一个空的stack
is_balanced = True # 设定值为true,之后遇到false情况再设定为false
index = 0 # keep track where we are in the string that we are looping through 从左到右依次check
def is_match(p1,p2): # 设定一个新函数is_match,判断符号两边是否一致,这里考虑三种符号的情况
if p1 =='('and p2 ==')':
return True
elif p1 == '[' and p2 ==']':
return True
elif p1 == '{' and p2 =='}':
return True
else:
return False
while index < len(symbol_string) and is_balanced : # string不为空
paren = symbol_string[index]
if paren in '([{': # 开始的符号为左边符号
s.push(paren) #添加到stack里面
else:
if s.is_empty(): #如果刚开始为右边符号,则不平衡
is_balanced = False
else:
top = s.pop() #如果左边符号已经添加完毕,现在开始pop右边符号
if not is_match(top,paren): #如果考察的符号与stack里已有的符号不相符(注意这里的顺序),则符号不平衡
is_balanced = False
index += 1 #依次考察每一个index
if s.is_empty() and is_balanced: #考察完毕如果stack为空则为符号平衡
return True
else:
return False