栈(stack)是一种容器,可存入数据元素,访问元素,删除元素等。存入栈中的元素之间相互没有任何具体联系,只有到来的先后顺序。
栈可以实现为在一端进行插入和删除的线性表,因此也称为后进先出表(LIFO)
在表实现中,执行插入和删除一端的操作称为栈顶,另一端称为栈底。访问和弹出的都应该显是栈顶元素。
- 对于顺序表,后端插入和删除是O(1)时间操作,应该用后端作为栈顶
- 对于链接表,前端插入和删除都是O(1)时间操作,应该用前端作为栈顶
1 栈的顺序表实现
- 建立空栈对应于创建一个空表 [] ,判断空栈对应于检查是否为空表
- 由于list 采用动态顺序表技术(分离式实现),作为栈的表不会满
- 压入元素在表单尾端进行,对应于lst.append(x)
- 访问栈顶元素应该用 list[-1]
- 弹出元素操作也应该在表单尾端进行,无参数的lst.pop()默认弹出表尾元素
这里把list 当做栈使用,完全可以满足应用的需要。但是这样的对象还是list,提供了list 类型的所有操作,
特别是提供了一大批栈结构原来不应该支持的操作,威胁安全性
例如:栈要求未经弹出的元素应该存在,但是表允许删除。
现在考虑基于顺序表定义一个栈类,使之成为一个独立的类型,把Python 的list隐藏在这个类的内部,作为其实现基础。
下面是一个栈类的定义,其中用一个list类型的数据属性_elems作为栈元素存储区,用_elems的首端作为栈底,尾端作为栈顶
class SStack(): # 基于顺序表实现的栈类
def __init__(self): # 用list对象_elems存储栈中元素
self._elems = [ ] # 所有栈操作都映射到栈操作
def is_empty(self):
return self._elems == [ ]
def top(self):
if self._elems == [ ]:
raise StackUnderflow( 'in stack.top()' )
else:
return self._elems[-1]
def push(self, elem):
self._elems.append(elem)
def pop(self):
if self._elems == []:
rasie StacakUnderflow(' in stack.pop()')
else:
return self._elems.pop()
以上代码都是基于Python 的list实现,所有理解起来不是很难
2 栈的链接表实现
使用顺序表实现栈,还是有些缺点,比如当list的元素存储区满了以后,要扩大元素存储区是一次高代价操作,而且,顺序表还需要完整的大的存储区。链接表的实现在这两个问题上都有优势。
当然,链接表的缺点是更多的依赖是解释器的存储管理,每个节点的链接开销,以及链接结点在实际计算机内存中任意散步可能带来的操作开销。
采用链接表技术,自然用表头一端作为栈顶,表尾作为栈底
class LStack(): # 基于链接表技术实现的栈类,用LNode作为结点
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()')
else:
return self._top.elem
def push(self, elem):
# 入栈->链接表->插入首端->新元素成为首元素
# 此时self._top 等于新的元素
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