栈(Stack)的python实现
定义
- 栈是由一系列对象组成的一个集合,这些对象的插入和删除遵循后进先出的原则。
栈的抽象数据类型
- 栈是支持以下两类操作的一种抽象数据类型,用S表示这一抽象数据类型:
- S.push() : 将一个元素添加到栈S的栈顶;
- S.pop(e) : 将元素e从栈顶的位置取出并在栈中删除这个元素,如果此时栈S是空栈,那么这一步会报错,为了方便,再定义以下操作:
- S.top() : 在不移除栈顶元素的前提下返回栈顶元素,如果栈为空栈,提示出错;
- S.is_empty() : 判断栈S是否为空栈,如果为空栈,返回布尔值 True;
- len(S) : 返回栈S中元素的个数,在python中使用 __len()__方法来实现。
基于数组的栈的实现
- 栈的概念和python的列表有较高的相似性,但是不能直接使用列表类来表示栈类,因为两者的概念和术语不一致;
- 在这里,可以使用适配器模式,通过修改现有的列表类,来使列表类的方法和栈类的方法相匹配,即以一个列表类的实例作为栈类的隐藏域,用这个隐藏域实例的方法实现栈类的方法。
用python的list()类实现一个栈
-
""" 本文代码主要讲解python基于数组的栈Stack的实现 """ class Error(Exception): """ 自定义异常类的原因是避免空栈调用top()/pop()方法报错时的提示信息是列表类的异常信息 """ pass class Stack(object): # 初始化时,将一个列表类实例作为栈类的隐藏域 def __init__(self): self._data = [] def __len__(self): return len(self._data) def push(self,arg): self._data.append(arg) def is_empty(self): return len(self) == 0 def top(self): if self.is_empty(): raise Error("stack is empty") return self._data[-1] def pop(self): if self.is_empty(): raise Error("stack is empty") value = self._data.pop(-1) return value if __name__ =="__main__": s = Stack() s.push(5) s.push(3) print(len(s)) print(s.pop()) print(s.is_empty()) print(s.pop()) s.push(7) s.push(9) print(s.top())
分析基于数组的栈的实现
- 在最坏的情况下,is_empty()/len()/top()方法都是在常量时间内完成,push()和top()方法的时间复杂度是O(1),指的是均摊计算的边界。对上述方法的任何一个典型调用都只需要常量的时间,但是当一个操作导致列表重新调整其内部数组的大小时,偶尔在最坏的情况下,也需要O(n)的时间开销,其中n是当前栈的个数,对于栈的空间利用率是O(n).
避免由于预留空间导致的摊销
-
前面的代码描述了栈类的实现开始于一个空列表,在不断调用push()方法的情况下扩充列表的大小,由于python列表类的特性,实际中构造一个最初长度为n的列表要比从一个空列表逐渐添加n项更加有效(即使两种方法都能在O(n)时间内完成)
-
如果实现上述的提议,那么栈类在初始化实例时,需要接受一个用于指定栈最大容量的参数,并根据此参数初始化数据成员列表的长度,这个时候栈的长度不在等同于列表的长度,列表的长度就是此参数,栈的长度取决于栈内元素的个数。此时需要在初始化时维护一个整数作为实例变量表示当前栈内元素的个数。
-
""" 本文代码主要讲解python基于数组的栈Stack的实现 """ class Error(Exception): """ 自定义异常类的原因是避免空栈调用top()/pop()方法报错时的提示信息时列表类的异常信息 """ pass class Stack(object): # 初始化时,将一个列表类实例作为栈类的隐藏域 def __init__(self, value): self._value = value self._data = [None] * value self._length = 0 def __len__(self): return self._length def push(self, arg): if self.is_full(): raise Error("stack is full") self._data.insert(self._length, arg) self._length += 1 def is_empty(self): return self._length == 0 def is_full(self): return self._length == self._value def top(self): if self.is_empty(): raise Error("stack is empty") return self._data[self._length - 1] def pop(self): if self.is_empty(): raise Error("stack is empty") value = self._data.pop(self._length - 1) self._length -= 1 return value if __name__ == "__main__": s = Stack(3) s.push(5) s.push(3) print(len(s)) print(s.pop()) print(s.is_empty()) print(s.pop()) s.push(7) s.push(9) print(s.top())
使用栈实现数据的逆转
- 由于栈的后入先出特性,栈可用于实现一个数据序列的逆置。
- 实现的思路,例如逆序打印一个文件,可以先依次读取文件的每一行,去掉换行符后压入栈内,全部读写后,再根据栈的弹出顺序,写入文件,不过再写入的时候需要在每一行后面,加上换行符。这里对换行符的操作是为了避免原文件最后一行没有换行符,导致新写入的文件的第一行和第二行紧挨在一起。
栈的应用
括号和HTML标记匹配
-
括号匹配:
""" 本文代码主要讲解python基于数组的栈Stack的实现 """ class Error(Exception): """ 自定义异常类的原因是避免空栈调用top()/pop()方法报错时的提示信息是列表类的异常信息 """ pass class Stack(object): # 初始化时,将一个列表类实例作为栈类的隐藏域 def __init__(self): self._data = [] def __len__(self): return len(self._data) def push(self, arg): self._data.append(arg) def is_empty(self): return len(self) == 0 def top(self): if self.is_empty(): raise Error("stack is empty") return self._data[-1] def pop(self): if self.is_empty(): raise Error("stack is empty") value = self._data.pop(-1) return value def is_label_matched(label): s = Stack() left = "([{" right = ")]}" for i in label: if i in left: s.push(i) if i in right: if s.is_empty(): return False value = s.top() if left.index(value) != right.index(i): return False else: return True if __name__ == "__main__": result = is_label_matched("(5+1)") print(result) result = is_label_matched("{5+1]}") print(result)
-
HTML文本匹配:
""" 本文代码主要讲解python基于数组的栈Stack的实现 """ class Error(Exception): """ 自定义异常类的原因是避免空栈调用top()/pop()方法报错时的提示信息是列表类的异常信息 """ pass class Stack(object): # 初始化时,将一个列表类实例作为栈类的隐藏域 def __init__(self): self._data = [] def __len__(self): return len(self._data) def push(self, arg): self._data.append(arg) def is_empty(self): return len(self) == 0 def top(self): if self.is_empty(): raise Error("stack is empty") return self._data[-1] def pop(self): if self.is_empty(): raise Error("stack is empty") value = self._data.pop(-1) return value def is_label_matched(label): s = Stack() j = label.find("<") while j != -1: k = label.find(">", j + 1) if k == -1: return False key_label = label[j + 1:k] if key_label.startswith("/"): if s.is_empty(): return False else: value = s.top() if value == key_label[1:]: s.pop() else: return False else: s.push(key_label) j = label.find("<", k + 1) return s.is_empty() if __name__ == "__main__": result = is_label_matched("<a></a>") print(result) result = is_label_matched("<a>") print(result)