(2022.05.04 Wed)
Doubly Linked List双向链表
采用基于array形式的链表,如Python list,为找到某特定元素只需要知道该元素的index即可,查找方便,但在非array链表实现过程中查找某特定元素需要从表头开始遍历链表直到找到,效率低下。在这里给链表实现引入了定位(position),相当于对某特定元素a加标签,保留该标签可随时找到a,不管链表如何变化,方便了定位。
为了理解定位信息,可设想这样一种情况:多人排队做活宣,Jack身在其中,Jack前面的人会减少,后面的人会增加,前面也有可能有人插队,但不管队伍怎么变化,Jack总能在队伍中被轻易认出。好像Jack头上有个标志牌,只要看到该牌子就能找到Jack。链表中加入的定位信息,相当于这个标志牌。
这里使用双向链表Doubly Linked List,即表头表尾加占位符号header和trailer,这样保证除header和trailer外的所有节点都有prev
和next
属性,便于执行对链表的修改。header和trailer不保存链表信息。
实现
这里我们给出带位置的双向链表PositionalList
的实现过程。
首先设置双向链表基类_DoubleLinkedBase
。
在基类中定义基本方法,即插入和删除。设置嵌入类nested class,即_Node
用于封装节点信息,封装内容包括,1.节点所代表的元素, 2.节点前置点, 3.节点后置点。基类的header和trailer都初始化为空值None,且互为后置前置节点。加入__slots__
可在实例化时减少内存的使用,特别是大量实例化的时候。
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._trailer = self._Node(None, None, None)
self._header._next = self._trailer
self._trailer._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
接下来创建带位置信息的类PositionalList
,其继承自_DoubleLinkedBase
。在该类中,创建nested class Position
,该类包含了节点的信息和所在的容器(即链表)。该链表的内置方法包括
-
_make_position
: 将输入节点封装为Position类,对header和trailer不执行该操作 -
_validate
: 验证输入的对象p
是否,1. 是Position类,2. 在同一个容器(链表)中,3. 其下一个节点为None,即是否可用。验证通过,返回该输入对象的节点信息,即p._node
-
_insert_between
: 在两个节点间插入新节点,继承了_DoubleLinkedBase
的中方法,返回封装后的Position类对象 - 查询
first
last
before
after
: 如名字所示,查询链表的首、尾元素,和指定元素的前、后元素。返回结果是经过封装的Position类对象。注意,链表的首元素非header
元素,而是header
的下一个元素,尾元素同理。 - 更新
add_first
add_last
add_before
add_after
: 更新,返回都是经过封装的Position类对象 - 删除替换
delete
replace
: 略
class PositionalList(_DoubleLinkedBase):
#------------------------ nested Position class --------------------------
class Position:
def __init__(self, container, node):
self._container = container
self._node = node
def element(self):
return self._node._element
def __eq__(self, other):
return type(self) is type(other) and other._node is self._node
def __ne__(self, node):
return not (self == node)
#---------------------------- utility method -------------------------------
def _validate(self, p):
'''Return position s node, or raise appropriate error if invalid.'''
if not isinstance(p, self.Position):
raise TypeError('p must be of 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._trailer:
return None
else:
return self.Position(self, node)
def first(self):
# first node is NOT header, but the first after header
return self._make_position(self._header._next)
def last(self):
return self._make_position(self._trailer._prev)
def before(self, p):
n = self._validata(p)
return self._make_position(n._prev)
def after(self, p):
n = self._validate(p)
return self._make_position(n._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._trailer._prev, self._trailer)
def add_before(self, p, e):
original = self._validate(p)
return self._insert_between(e, original._prev, original)
def add_after(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_old = original._element
original._elemenet = e
return old_value
应用如下
>>> pl = PositionalList()
>>> pl._header._element
>>> pl._trailer._element
>>> pl._header._next._element
>>> a1 = pl.add_first(365) # 首元素365
>>> a2 = pl.add_after(a1, 10086) # 在365后加入10086
>>> a3 = pl.add_before(a2, 1314) # 在10086前计入1314
# 此时该链表中元素顺序是365->1314->10086,位置顺序为a1->a3->a2
>>> a3.element()
1314
>>> a3._node._next._element
10086
>>> a3._node._prev._element
365
>>> print(a1._node._prev._element)
None
复杂度分析
O(1)
Reference
1 Goodrich and etc., Data Structures and Algorithms in Python
网友评论