< Data Structures andAlgorithms in Python > Michael T.Goodrich,Roberto Tamassia,Michael H.Goldwasser 学习笔记
双向链表的实现
和单向链表不同,双向链表添加了头节点和尾节点,这两个节点不存储元素。它们占了少许内存空间,但是极大地简化了操作逻辑。其实现示意图如下:
我们可以先定义一个实现的基本类,以便后续的拓展延伸,代码如下:
class Empty(Exception):
def __init__(self, m):
super().__init__(self)
self.message = m
def __str__(self):
return self.message
class _DoubleLinkedBase:
class _Node:
__slots__ = '_element', '_prev', '_next'
def __init__(self, element, prev, next):
self._element = element
self._prev = prev
self._next = next
def __init__(self):
self._header = self._Node(None, None, None)
self._tailer = self._Node(None, None, None)
self._header._next = self._tailer
self._tailer._prev = self._header
self._size = 0
def __len__(self):
return self._size
def is_empty(self):
return self._size ==0
def _insert_between(self, e, predecessor, successor):
newest = self._Node(e, predecessor, successor)
predecessor._next = newest
successor._prev = newest
self._size += 1
return newest
def _delete_node(self, node):
predecessor = node._prev
successor = node._next
predecessor._next = successor
successor._prev = predecessor
self._size -= 1
element = node._element
node._prev = node._next = node._element = None
return element
_insert_between()和_delete_node()是两个保护方法,由于里面涉及到保护的嵌套类,不可以被直接引用,主要是给子类继承和类对象使用。
双向链表实现双端队列
将上面的代码命名为DoubleLinkedBase放入Linked的文件夹,引入LinkedDeque.py文件继承_DoubleLinkedBase类,其具体代码如下:
from Linked.DoubleLinkedBase import Empty, _DoubleLinkedBase
class LinkedDeque(_DoubleLinkedBase):
def first(self):
if self.is_empty():
raise Empty('Deque is empty')
return self._header._next._element
def last(self):
if self.is_empty():
raise Empty('Deque is empty')
return self._tailer._prev._element
def insert_first(self, e):
self._insert_between(e, self._header, self._header._next)
def insert_last(self, e):
self._insert_between(e, self._tailer._prev, self._tailer)
def delete_first(self):
if self.is_empty():
raise Empty('Deque is empty')
return self._delete_node(self._header._next)
def delete_last(self):
if self.is_empty():
raise Empty('Deque is empty')
return self._delete_node(self._tailer._prev)
具有位置信息的双向链表
栈和队列是常见的数据类型,它们一般只能在数据结构的两端进行操作,现在我们定义一个更普遍的数据类型:不仅可以在两端进行操作,也可以实现内部的插入和删除。实现插入和删除操作需要知道元素的具体位置,就比如说索引能够很好的描述元素在列表中的位置,但是数字的索引不适合描述一个链表内部的位置。链表和列表不同:列表存储是在一块连续的内存上的,而且每次插入删除操作都会调整列表的索引,链表是分散存储的,如果使用索引,当插入或删除操作时,其索引就失效了。所以我们使用另一种方式实现链表的位置信息:在数据结构类中定义一个位置信息的嵌套类Position,其具体实现方式如下:
from Linked.DoubleLinkedBase import _DoubleLinkedBase
class PositionalList(_DoubleLinkedBase):
class Position:
def __init__(self, container, node):
self._container = container
self._node = node
def __eq__(self, other):
return type(self) is type(other) and other._node is self._node
def __ne__(self, other):
return not (self == other)
def element(self):
return self._node._element
def _validate(self, p):
if not isinstance(p, self.Position):
raise TypeError('p must be proper Position type.')
if p._container is not self:
raise ValueError('p does not belong to this container')
if p._node._next is None:
raise ValueError('p is no longer valid')
return p._node
def _make_position(self, node):
if node is self._header or node is self._tailer:
return None
else:
return self.Position(self, node)
def first(self):
return self._make_position(self._header._next)
def last(self):
return self._make_position(self._tailer._prev)
def before(self, p):
node = self._validate(p)
return self._make_position(node._prev)
def after(self, p):
node = self._validate(p)
return self._make_position(node._next)
def __iter__(self):
cursor = self.first()
while cursor is not None:
yield cursor.element()
cursor = self.after(cursor)
def _insert_between(self, e, predecessor, successor):
node = super()._insert_between(e, predecessor, successor)
return self._make_position(node)
def add_first(self, e):
return self._insert_between(e, self._header, self._header._next)
def add_last(self, e):
return self._insert_between(e, self._tailer._prev, self._tailer)
def add_before(self, p, e):
original = self._validate(p)
return self._insert_between(e, original, original._next)
def delete(self, p):
original = self._validate(p)
return self._delete_node(original)
def replace(self, p, e):
original = self._validate(p)
old_value = original._element
original._element = e
return old_value
总结
总结一下使用列表和使用链表实现栈队列的优缺点:
- 列表可以使用整数索引,而链表只能使用我们上面讲到的嵌套类来表示,索引的查询效率明细地比链表要高;
- 添加删除操作中,不在底层数组的边界操作,其效率要比链表高的。因为在链表中添加元素需要考虑节点的实例化、节点的合适链接和整数的增量,但是链表支持任意位置的常数复杂度的插入操作;
- 在存储空间方面,由于列表底层数组都要比实际存储的要大,列表直觉上要比链表占更多的内存,其实不然,比如在单向链表中,单个节点不仅要存储自身元素,还要存储下个节点,那么一个长度为
的单向链表至少需要
的空间。