Search code examples
pythondoubly-linked-list

Doubly Linked list with iterator


I'm trying to implement the doubly-linked list in these singly-linked list operations, but how do I properly implement the previous Node (antNode) in the functions addNode and elimNode?

Almost everything that is done in insNode is applicable in addNode function.

class ListNode:
    def __init__(self, data, nextNode=None):
        self.data = data
        self.nextNode = nextNode
        self.antNode = None

class DoublyLinkedListIterator:
    def __init__(self, firstNode=None):
       self.firstNode = firstNode
       self.lastNode  = firstNode
       self.iterator  = firstNode
       if firstNode:
           self.size = 1
       else:
           self.size = 0

    def addNode(self, data): # Add a Node after the iterator.
        """Pre condition: Iterator defined
           Pos condition: The data is inserted into a node after the
                          iterator. The iterator stands over this node.
        """
        newNode = ListNode(data,None) # treats the empty list
        newNode.nextNode = None
        newNode.antNode = None
        if(self.size == 0):           # treats the empty list
            self.iterator = newNode
            self.firstNode = newNode
            self.lastNode = newNode
        elif self.iterator == self.lastNode:
            self.lastNode.nextNode = newNode
            self.iterator = newNode
            self.lastNode = newNode
        else:
            newNode.nextNode = self.iterator.nextNode
            self.iterator.nextNode = newNode
            self.iterator = newNode
        self.size += 1
        return True

     def elimNode(self): # Eliminates the element over the iterator and
                         # advances the iterator to the next node.
         if(self.iterator == self.firstNode):
             if(self.lastNode == self.firstNode):
                 self.lastNode = None
                 self.firstNode = None
                 self.iterator = None
             else: # more than one node
                 #self.firstNode = self.firstNode.nextNode
                 self.firstNode = self.firstNode.nextNode
                 self.iterator.nextNode = None
                 self.iterator = self.firstNode
         else: # iterator can be under the last or an inner element
             currentNode = self.firstNode
             while currentNode.nextNode != self.iterator:
                 currentNode = currentNode.nextNode
             if self.iterator == self.lastNode:
                 self.lastNode = currentNode
                 self.iterator.nextNode = None
                 self.iterator = None
                 currentNode.nextNode = None
             else:
                 currentNode.nextNode = self.iterator.nextNode
                 currentNode = self.iterator
                 self.iterator = self.iterator.nextNode
                 currentNode.nextNode = None
         self.size = self.size - 1
         return True

The function insNode that inserts a node before the iterator is like this with doubly linked list:

def insNode(self, data):  # insert a node before the iterator
    """Pre condition: Iterator defined
       Pos condition: The data is inserted into a node before the iterator.
                      The iterator stands over this node.
    """
    newNode = ListNode(data, None)  # treats the empty list
    newNode.nextNode = None
    newNode.antNode = None
    if (self.size == 0):  # treats the empty list
        self.iterator = newNode
        self.firstNode = newNode
        self.lastNode = newNode
    elif self.iterator == self.firstNode:  # iterator is on the first element
        newNode.nextNode = self.firstNode
        self.iterator.antNode = newNode
        self.firstNode = newNode
        self.iterator = newNode
    else:  # iterator is on an inner element
        newNode.antNode = self.iterator.antNode
        self.iterator.antNode.nextNode = newNode
        self.iterator.antNode = newNode
        self.iterator = newNode
    self.size += 1
    return True

Solution

  • The answer is: yes you should do similar changes in insNode. However, much of the code is alike, so I would create a helper function to perform the actions these two methods have in common. Also, let the ListNode constructor take an optional argument also for the antNode attribute.

    Not your question, but:

    • The elimNode method should better check that self.iterator is not None.

    • I find the name iterator confusing, since iterator is a very specific concept in Python, and it is not this. So I would rename that to cursor or currentNode or something. I can understand that this was given in some template code, or exercise, but then it doesn't really speak in favour of the quality of that teaching material.

    • I don't really see the purpose for the optional node parameter in the constructor of the main class. I would either omit it, or else let it accept a variable number of data values. That would be more practical.

    Here is how it could look:

    class ListNode:
        def __init__(self, data, antNode=None, nextNode=None):
            self.data = data
            self.nextNode = nextNode
            self.antNode = antNode
    
    
    class DoublyLinkedListWithCursor:  
        def __init__(self):
            self.firstNode = self.lastNode = self.currentNode = None
            self.size = 0
            
        def _insertBetween(self, data, before, after):
            # Helper function for common logic in insNode and addNode methods
            self.currentNode = ListNode(data, before, after)
            if after:  
                after.antNode = self.currentNode 
            else:
                self.lastNode = self.currentNode
            if before:  
                before.nextNode = self.currentNode 
            else:
                self.firstNode = self.currentNode
            self.size += 1
        
        def insNode(self, data):
            self._insertBetween(data, self.currentNode.antNode if self.currentNode else self.lastNode, self.currentNode)
    
        def addNode(self, data):
            self._insertBetween(data, self.currentNode, self.currentNode.nextNode if self.currentNode else self.firstNode)
        
        def elimNode(self):
            if not self.currentNode:  # Sanity check
                return False
            if self.currentNode.antNode:
                self.currentNode.antNode.nextNode = self.currentNode.nextNode
            else:
                self.firstNode = self.currentNode.nextNode
            if self.currentNode.nextNode:
                self.currentNode.nextNode.antNode = self.currentNode.antNode
            else:
                self.lastNode = self.currentNode.antNode
            self.currentNode = self.currentNode.nextNode
            self.size -= 1
            return True