前面我们已经实现了Python 采用列表实现栈(Stack),那时我们将列表的末尾看作是栈的顶,列表方法append就是将元素压入栈中,而列表方法pop会删除并返回栈顶的元素,这种选择的主要缺点是,所有其它的列表操作也都可以操作这个栈,这包括了在任意位置插入、替换和删除元素。这些额外的操作违反了栈作为一种抽象数据类型的本意。
栈接口
栈方法 | 作用 |
---|---|
s.isEmpty() | 如果是空返回True,否则返回False |
__len__(s) | 等同于len(s),返回s中的项的数目 |
__str__(s) | 等同于str(s),返回s中的字符串表示 |
s.__iter__() | 等同于iter(s)或for item in s,从顶部向顶部,访问s中的每一项 |
s.__contains__(item) | 等同于item in s,如果item在s中,返回True |
s1__add__(s2) | s1 + s2,返回一个新的栈,其中包含了s1和s2的项 |
s.__eq__(anyObject) | 等同于s == anyObject |
s.clear() | 将s清空 |
s.peek() | 返回s顶部的项。先验条件:s必须不为空 |
s.push(item) | 在s的顶部添加一项 |
s.pop() | 在s的顶部删除一项并返回该项。先验条件:s必须不为空 |
这个接口的优点在于用户将知道使用哪一个方法,以及这些方法接收什么参数,而不必管选择哪一种栈实现!相同接口的不同实现很可能有不同的性能权衡。
栈的数组实现
AbstractCollection类
class AbstractCollection(object): """An abstract collection implementation.""" # Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" self._size = 0 if sourceCollection: for item in sourceCollection: self.add(item) # Accessor methods def isEmpty(self): """Returns True if len(self) == 0, or False otherwise.""" return len(self) == 0 def __len__(self): """Returns the number of items in self.""" return self._size def __str__(self): """Returns the string representation of self.""" return "[" + ", ".join(map(str, self)) + "]" def __add__(self, other): """Returns a new bag containing the contents of self and other.""" result = type(self)(self) for item in other: result.add(item) return result def __eq__(self, other): """Returns True if self equals other, or False otherwise.""" if self is other: return True if type(self) != type(other) or \ len(self) != len(other): return False otherIter = iter(other) for item in self: if item != next(otherIter): return False return True
AbstractStack类
from abstractcollection import AbstractCollection class AbstractStack(AbstractCollection): """An abstract stack implementation.""" # Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" AbstractCollection.__init__(self, sourceCollection) # Mutator methods def add(self, item): """Adds item to self.""" self.push(item)
ArrayStack类
这里将数组self._items的默认容量(物理大小)设置为10,并且self._size等于0。如果栈顶有项,它总是位于self._size-1的位置。要将一个项压入栈,需要将它存储到self._items[len(self)]的位置并且将self._size增加1。要弹出栈,就需要返回self._items[len(self)-1],并且将self._size减1。
注意:这里可能会有一个数组溢出的问题,我们需要当数组将要溢出的时候创建一个新的数组。按照之前的分析,当push填充数组的时候,我们将数组的容量加倍,当pop使得数组的四分之三为空的时候,我们将其容量减半。
from arrays import Array from abstractstack import AbstractStack class ArrayStack(AbstractStack): """An array-based stack implementation.""" # Class variable DEFAULT_CAPACITY = 10 # Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" self._items = Array(ArrayStack.DEFAULT_CAPACITY) AbstractStack.__init__(self, sourceCollection) # Accessor methods def __iter__(self): """Supports iteration over a view of self. Visits items from bottom to top of stack.""" cursor = 0 while cursor < len(self): yield self._items[cursor] cursor += 1 def peek(self): """Returns the item at the top of the stack. Precondition: the stack is not empty. Raises: KeyError if stack is empty.""" if self.isEmpty(): raise KeyError("The stack is empty") return self._items[len(self) - 1] # Mutator methods def clear(self): """Makes self become empty.""" self._size = 0 self._items = Array(ArrayStack.DEFAULT_CAPACITY) def push(self, item): """Inserts item at top of the stack.""" # Resize array here if necessary self._items[len(self)] = item self._size += 1 def pop(self): """Removes and returns the item at the top of the stack. Precondition: the stack is not empty. Raises: KeyError if stack is empty. Postcondition: the top item is removed from the stack.""" if self.isEmpty(): raise KeyError("The stack is empty") oldItem = self._items[len(self) - 1] self._size -= 1 # Resize the array here if necessary return oldItem
这里还需要注意一下peek、pop方法的先验条件,一个安全的实现是,当违反这些先验条件的时候,强制引发异常。
栈的链表实现
由于新添加的项和删除的项是链表结构的一段,所以pop和push方法很容易实现,但是__iter__方法的实现还是很复杂的,因为必须从链表结构的尾部到头部来访问各项。这时候我们可以采用递归,在初次调用这个函数的时候,参数节点是栈的链表结构的头(变量self._items)。如果这个节点不为None,我们使用该节点的next字段递归的调用该函数,从而一直向前直到到达结构的尾部。
from node import Node from abstractstack import AbstractStack class LinkedStack(AbstractStack): """A link-based stack implementation.""" # Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" self._items = None AbstractStack.__init__(self, sourceCollection) # Accessor methods def __iter__(self): """Supports iteration over a view of self. Visits items from bottom to top of stack.""" def visitNodes(node): """Adds items to tempList from tail to head.""" if not node is None: visitNodes(node.next) tempList.append(node.data) tempList = list() visitNodes(self._items) return iter(tempList) def peek(self): """ Returns the item at the top of the stack. Precondition: the stack is not empty. Raises: KeyError if the stack is empty.""" if self.isEmpty(): raise KeyError("The stack is empty.") return self._items.data # Mutator methods def clear(self): """Makes self become empty.""" self._size = 0 self._items = None def push(self, item): """Adds item to the top of the stack.""" self._items = Node(item, self._items) self._size += 1 def pop(self): """ Removes and returns the item at the top of the stack. Precondition: the stack is not empty. Raises: KeyError if the stack is empty. Postcondition: the top item is removed from the stack.""" if self.isEmpty(): raise KeyError("The stack is empty.") data = self._items.data self._items = self._items.next self._size -= 1 return data
两种实现的时间和空间分析
除了__iter__方法外,所有的栈方法都很简单,并且运行时间不大于O(1)。在数组的实现中,当数组容量翻倍的时候,push方法的运行时间也增加到了O(n),但是其它时候,它的运行时间保持在O(1),pop方法也类似。
n个对象的一个集合,需要足够的空间来保存n个对象的引用。n项的一个链表栈,需要n个节点,每个节点包含两个引用,一个引用项,另一个引用用于next节点,此外还有一个变量指向栈顶节点,还有一个变量用于大小,最终总的空间需求是2n+2.
对于一个数组的实现,当栈实例化的时候,其总的空间需求是固定的。这个空间包含了一个给定容量的数组,还有记录栈的大小和引用栈自身的变量,那么,总的空间需求就是数组的容量+2。那么,当数组的装载因子大于二分之一的时候,数组实现比链表实现具有更高的空间效率。
本文是数据结构(用Python实现)这本书的读书笔记!