python data structure -- location list class based on bidirectional linked list

Posted by Taro on Tue, 08 Feb 2022 13:41:22 +0100

Bidirectional linked list

Here is the code directly. If you have problems, see here.

class _DoublyLinkedBase():
    """a base class poviding a boubly linked list representation"""
    class _Node():
        __slots__ = '_element','_prev','_next'
        def __init__(self,element,prev,next):
            self._element = element
            self._prev = prev
            self._next = next
    def __init__(self):
        """create a empty list"""
        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 the number of elements in the list"""
        return self._size
    def is_empty(self):
        """return true if the list is empty"""
        return self._size==0
    def _insert_between(self,e,predecessor,successor):
        """add element e between pre and suc"""
        newest = self._Node(e,predecessor,successor)
        predecessor._next = newest
        successor._prev = newest
        self._size += 1
        return newest
    def _delete_node(self,node):
        """delete nonsentinel node from the list and return its element"""
        predecessor = node._prev
        successor = node._next
        predecessor._next = successor
        successor._prev - predecessor
        self._size -= 1
        element = node._element
        # be helpful for python to delete the node
        node._prev = node._next = node._element = None
        return element

Location list class

It can not only index the elements before and after, but also have a location attribute.
The position here is not the well-known list numerical subscript, but can insert, delete and judge the contents of the front and rear elements according to the current position. We can distinguish it into absolute position and relative position.

Support__ itr__ (), we can use the for loop to implement traversal;
Support__ en__ And__ eq__, Can compare two nodes.

Let's start with some details of the following code. First, we add an internal definition class once again.
Deficiencies of previous classes:
The head node may be used to operate the two-way linked list, which is not friendly to users;
Need to provide a more powerful data structure

Therefore, on the basis of the previous node, we are equivalent to setting a shell: Position, which shows the user this type, while the internal processing uses the previous node.

Two parameters of position: container and node. Node is the node of our two-way linked list, but what is container?
It sounds like a container. Let's take a look at our calling function**_ make_position.
Pass in the parameter self, because in a linked list, self is the same, in our judgment node function_ validate * * is also useful.

Then say it again with some private functions.
_ make_position: encapsulate a node node into a position instance
_ validate: judge whether a location is a position instance (using isinstance function), whether it is the same linked list, and whether it is the head and tail nodes of a two-way linked list.
__ iter__ To traverse_ insert_between encapsulates the previous insertion function and processes the return value into a position instance.

In this way, there should be nothing to look at the code:

class PositionalList(_DoublyLinkedBase):
    class Position():
        """an abstraction representing the location of a single element"""
        def __init__(self,container,node):
            """constructor should not be invoked by user"""
            self._container = container
            self._node = node
        def element(self):
            """return the element stored at this Position"""
            return self._node._element
        def __eq__(self,other):
            """return true if other is a Position representing the same location"""
            return type(other) is type(self) and other._node is self._node
        def __ne__(self,other):
            """return true if other is not representing the same lovation"""
            return not(self==other)
    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 proper Position type')
        if p._container is not self:
            raise ValueError('p does not belong to this container')
        if p._node._next is None or p._node._prev is None:
            raise ValueError('p is no longer valid')
        return p._node
    def _make_position(self,node):
        """return position instance for given node(or None if sentinel)"""
        if node is self._header or node is self._trailer:
            return None
        else:
            return self.Position(self,node)
    def first(self):
        """return the first position in the list"""
        return self._make_position(self._header._next)
    def last(self):
        """return the last position in the list"""
        return self._make_position(self._trailer._prev)
    def before(self,p):
        """return the position just before position p"""
        node = self._validate(p)
        return self._make_position(node._prev)
    def after(self,p):
        """return the position just after position p"""
        node = self._validate(p)
        return self._make_position(node._next)
    def __iter__(self):
        """generate a forward iteration of the elements of the list"""
        cursor = self.first()
        while cursor is not None:
            yield cursor.element()
            cursor = self.after(cursor)
    
    def _insert_between(self,e,predecessor,successor):
        """add element between existing nodes and return new position"""
        node = super()._insert_between(e,predecessor,successor)
        return self._make_position(node)
    def add_first(self,e):
        """insert element e at the front of the list and return new position"""
        return self._insert_between(e,self._header,self._header._next)
    def add_last(self,e):
        """insert element e at rhe back of the list and return new position"""
        return self._insert_between(e,self._trailer._prev,self._trailer)
    def add_before(self,p,e):
        """insert """
        original = self._validate(p)
        return self._insert_between(e,original._prev,original)
    def add_after(self,p,e):
        """insert element e into list after position p and return new position"""
        original = self._validate(p)
        return self._insert_between(e,original,original._next)
    def delete(self,p):
        """return and return the element oat pisition p"""
        original = self._validate(p)
        return self._delete_node(original)
    def replace(self,p,e):
        """replace the element at position p with e, and return the old element"""
        original = self._validate(p)
        old_value = original._element
        original._element = e
        return old_value