< Data Structures andAlgorithms in Python > Michael T.Goodrich,Roberto Tamassia,Michael H.Goldwasser 学习笔记
Python中除了列表,还有链表这样的基础数据结构,之前我们都是介绍用列表实现栈、队列这样的数据结构的,接下来我们看一下用链表实现的栈和队列。
既然列表已经可以实现栈和队列了,那么链表又有什么存在的意义呢?当然是因为列表有一些明细的缺点:
- 内存的分配:底层数组大小要比实际列表的大;
- 摊销的边界:底层数组已满时,继续向列表中添加元素,由于要换底层数组并转移数据,这时的时间复杂度明显要比之前添加元素的要高。
- 插入的代价:在尾部插入数据,时间复杂度摊销下来只要 ,但是在中间甚至在头部插入,后面的数据都需要后移来给新添加的数据腾出空间。(删除操作同理)
链表就很好的避免了以上列表的三个缺点,但是链表也有自己的缺点:无法通过索引来寻找链表中的元素。
单向链表
单向链表的结构图如上图所示。在单项链表中实现插入删除元素时,唯一需要注意的是在单向链表中间或者尾部删除元素:当我们确定要删除的节点的时候,我们可以查询到当前节点下一个节点,却无法查询到上一个节点,要想知道上一个节点,则需要遍历整个链表,这明显不可取。
单向链表实现栈
单向链表实现栈,规定栈的顶部使用链表的头部,因为只有在头部才可以有效的插入和删除。
为了表示列表中的单个节点,定义一个嵌套类_Node类。具体实现如下代码:
class Empty(Exception):
def __init__(self, m):
super().__init__(self)
self.message = m
def __str__(self):
return self.message
class LinkedStack:
class _Node:
__slots__ = '_element', '_next'
def __init__(self, e, n):
self._element = e
self._next = n
def __init__(self):
self._head = None
self._size = 0
def __len__(self):
return self._size
def is_empty(self):
return self._size == 0
def top(self):
if self.is_empty():
raise Empty('Stack is empty.')
return self._head._element
def push(self, e):
self._head = self._Node(e, self._head)
self._size += 1
def pop(self):
if self.is_empty():
raise Empty('Stack is empty.')
data = self._head._element
self._head = self._head._next
self._size -= 1
return data
显然,用链表实现的栈无论出栈入栈,其时间复杂度都是 ,并不需要摊销计算。
单向链表实现队列
根据前面提到的单向链表的注意点,对于队列,只能是链表的头部出队列,链表的尾部入队列,代码如下:
class Empty(Exception):
def __init__(self, m):
super().__init__(self)
self.message = m
def __str__(self):
return self.message
class LinkedQueue:
class _Node:
__slots__ = '_element', '_next'
def __init__(self, e, n):
self._element = e
self._next = n
def __init__(self):
self._head = None
self._tail = None
self._size = 0
def __len__(self):
return self._size
def is_empty(self):
return self._size == 0
def first(self):
if self.is_empty():
raise Empty('Queue is empty')
return self._head._element
def dequeue(self):
if self.is_empty():
raise Empty('Queue is empty')
data = self._head._element
self._head = self._head._next
self._size -= 1
if self.is_empty():
self._tail = None
return data
def enqueue(self, e):
new = self._Node(e, None)
if self.is_empty():
self._head = new
else:
self._tail._next = new
self._tail = new
self._size += 1
队列与栈实现不同的地方就是:队列需要多考虑一个尾部。性能方面,用链表实现的队列和栈类似,都是常数时间复杂度。