Python : I am not able to prepend a Value in a Singly Linked list

Question:

Code written in python:

prepend Function is supposed to add a value to the start of the Linked List
display Function is supposed to display the linked list in a List format in python
fromarr Function is supposed to add new nodes to the linked list

class Node:
    def __init__(self,value=None):
        self.value = value
        self.next = None
    
class SinglyLL:
    def __init__(self):
        self.head = Node()
        self.tail = Node()
    def prepend(self,value):
        newnode  = Node(value)
        newnode.next = self.head
        self.head = newnode
    def append(self,data):
        newnode = Node(data)
        if self.head is None:
            self.head = newnode
            return
        cur = self.head
        while cur.next != None:
            cur = cur.next
        cur.next = newnode
        newnode.next = None
    def display(self):
        lis = []
        cur = self.head
        while cur.next != None:
            cur = cur.next
            lis.append(cur.value)
        return lis
    def fromarr(self,list):
        for i in list:
            temp = Node(i)
            self.append(temp.value)

newsll = SinglyLL()
newsll.fromarr([1,2,3,4,5])
newsll.prepend(10)
print(newsll.display())

Expected output : [10,1,2,3,4,5]

Actual Output: [None,1,2,3,4,5]

Asked By: Harsha Vardhan

||

Answers:

well you are initalising your head with a empty node, and displaying values from self.head.next, so instead of setting element at position 0 you need to set element at position 1.

Adding below code for prepend method

class Node:
    def __init__(self,value=None):
        self.value = value
        self.next = None

class SinglyLL:
    def __init__(self):
        self.head = Node()
        self.tail = Node()
    def prepend(self,value):
        newnode  = Node(value)
        if self.head is None:
            self.head = newnode
        else:
            tmp = self.head.next
            self.head.next = newnode
            newnode.next = tmp

    def append(self,data):
        newnode = Node(data)
        if self.head is None:
            self.head = newnode
            return
        cur = self.head
        while cur.next != None:
            cur = cur.next
        cur.next = newnode
        newnode.next = None
    def display(self):
        lis = []
        cur = self.head
        while cur.next != None:
            cur = cur.next
            lis.append(cur.value)
        return lis
    def fromarr(self,list):
        for i in list:
            temp = Node(i)
            self.append(temp.value)

newsll = SinglyLL()
newsll.fromarr([1,2,3,4,5])
newsll.prepend(10)
print(newsll.display())
Answered By: sahasrara62

Your code has a mix of several approaches to organise a linked list data structure, but they are not compatible:

  • Your constructor creates a dummy node for the head attribute, which could be an approach when you intend to append all data nodes after that dummy node. The display method is in line with that approach as it doesn’t include the value of the dummy node in the result. But the prepend nor the append method use that approach as they treat the head node as a data node.

  • Your constructor creates a dummy node for the tail attribute. This is an approach that might be used for a doubly linked list, but is not useful for a singly linked list, as the process to append a new node at the end of the list cannot take benefit from this reference.

  • Although the constructor defines a tail attribute none of the other methods make use of it, nor update it when necessary.

Some other remarks:

  • It is odd that the display method does not display anything. It would be more appropriate to have an __iter__ method instead which would yield the linked list’s values.

  • fromarr is not a very good name for a method that accepts a list.

  • It is not intuitive that this is an instance method, as its name suggests that it will create a new list from those values, but you actually need to first create a linked list instance yourself, and then call this method. I would suggest to drop this method and extend the constructor with optional arguments which will be used to populate the constructed linked list.

  • list is a bad name for a parameter as this name is already in use by native Python.

  • It would be nice if the Node constructor would accept an optional argument for defining the next attribute.

Here is an update of your code that takes those points into account, and which uses the approach where:

  • A tail attribute is set and used
  • No dummy nodes are created
class Node:
    def __init__(self, value=None, nxt=None):
        self.value = value
        self.next = nxt
    
class SinglyLL:
    def __init__(self, *values):
        # Don't create nodes here
        self.head = self.tail = None
        # Allow immediate population from arguments
        for value in values:
            self.append(value)
        
    def prepend(self,value):
        self.head = Node(value, self.head)
        self.tail = self.tail or self.head
        
    def append(self, data):
        if not self.tail:
            self.prepend(data)
        else:
            self.tail.next = self.tail = Node(data)

    def __iter__(self):
        cur = self.head
        while cur:
            yield cur.value
            cur = cur.next
        

newsll = SinglyLL(1,2,3,4,5)
newsll.prepend(10)
print(list(newsll))  # [10, 1, 2, 3, 4, 5]
Answered By: trincot