链表Linked List, since 2022-05-04

(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外的所有节点都有prevnext属性,便于执行对链表的修改。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

你可能感兴趣的:(链表Linked List, since 2022-05-04)