Python中的链表之双向链表

< 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

总结

总结一下使用列表和使用链表实现栈队列的优缺点:

  • 列表可以使用整数索引,而链表只能使用我们上面讲到的嵌套类来表示,索引的查询效率明细地比链表要高;
  • 添加删除操作中,不在底层数组的边界操作,其效率要比链表高的。因为在链表中添加元素需要考虑节点的实例化、节点的合适链接和整数的增量,但是链表支持任意位置的常数复杂度的插入操作;
  • 在存储空间方面,由于列表底层数组都要比实际存储的要大,列表直觉上要比链表占更多的内存,其实不然,比如在单向链表中,单个节点不仅要存储自身元素,还要存储下个节点,那么一个长度为 n n n的单向链表至少需要 2 n 2n 2n的空间。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值