6
\$\begingroup\$

I'm trying to learn linked lists so I can answer interview questions. Even though I've seen plenty of tutorials to understand how they supposedly work, I don't know if I am doing a good job because I have no one to get feedback from.

class Node(object):

    def __init__(self, data, next_node = None):
        self.data = data
        self.next = next_node

    def __str__(self):
        return str(self.data)


class SinglyLinkedList(object):

    def __init__(self):
        self.head = None

    def prepend(self, data):
        '''
        Creates a new node and inserts it at the front, making it the head
        of the list
        '''
        self.head = Node(data, next_node=self.head)

    def append(self, data):
        '''
        Creates a new node and inserts it at the very end of the list
        '''
        new_node = Node(data)
        if self.head == None:
            self.head = new_node
        else:
            iter_node = self.head
            while iter_node.next:
                iter_node = iter_node.next
            iter_node.next = new_node

    def insert(self, after_node, data):
        '''
        A new node is created containing the passed data, and then inserted
        right after the node called after_node in the list
        '''
        new_node = Node(data)
        new_node.next = after_node.next
        after_node.next = new_node

    def delete(self, node):
        '''
        Removes the given node from the list. If the node is at the tail already,
        it is set to None. Otherwise it is "de facto" removed by having it mirror the next
        node's data, and then link to that mirror's next node.
        '''
        if node.next == None:
            node = None
        else:
            node.data = node.next.data
            node.next = node.next.next

    def reverse(self):
        '''
        Reverses the entire linked list.
        '''
        if self.head:
            prev = None
            current = self.head
            while current:
                future = current.next
                current.next = prev
                prev = current
                current = future
            self.head = prev

    def __iter__(self):
        '''
        Yields each node in the list over a loop
        '''
        iter_node = self.head
        while iter_node:
            yield iter_node
            iter_node = iter_node.next

    def __str__(self):
        return "[" + ", ".join(str(node) for node in self) + "]"
\$\endgroup\$
0

2 Answers 2

2
\$\begingroup\$
  • Bug

    SinglyLinkedList.delete called on the tail node doesn't really delete it: the previous node still holds the reference.

  • Interface

    SinglyLinkedList.insert and SinglyLinkedList.delete require a handle to a Node object, which is quite hard to obtain. It seems that the only way is to manually iterate the list breaking on a condition. I recommend prepend, append, insert to return a newly created instance. A find method would also be useful.

\$\endgroup\$
6
  • \$\begingroup\$ I thought insert() etc were meant to be constant time operations, which as far as I know can only be done if you pass in the node directly. Otherwise each deletion is O(n) by traversing from the head each time. And find() what? \$\endgroup\$ Commented Jan 23, 2016 at 4:01
  • \$\begingroup\$ Source: bigocheatsheet.com \$\endgroup\$ Commented Jan 23, 2016 at 4:17
  • \$\begingroup\$ @user5828922 Exactly my point. If you do not return a node, how can a client pass it? Re find: find() a node (or nodes) satisfying a predicate. \$\endgroup\$ Commented Jan 23, 2016 at 4:24
  • \$\begingroup\$ So in practice, if I want to delete something from the middle of a list, it would technically take O(n) time overall? O(n) to find the node, and then O(1) to delete it once its found? And what does find() typically pass in as argument? Data? What if there are multiple nodes with the same data? \$\endgroup\$ Commented Jan 23, 2016 at 4:27
  • \$\begingroup\$ @user5828922 If the client has a handle to a node (or a node just before), insertion/deletion technically and practically is O(1). If obtaining the handle takes O(n) then insertion/deletion is also O(n). Re find: pass a predicate (lambda) an data as required. \$\endgroup\$ Commented Jan 23, 2016 at 4:38
1
\$\begingroup\$

A bit more on that bug...

To expand a bit on the bug in delete pointed out by @vnp, the problem is with the node = None statement:

def delete(self, node):
    if node.next == None:
        node = None
    else:
        node.data = node.next.data
        node.next = node.next.next

It seems you misunderstand what that statement does. It doesn't do what you think. It creates a local variable called node, shadowing the parameter variable with the same name.

To delete the tail, you need to get to the previous node, and set previous_node.next = None.

To access to the previous node, you could either add a .prev property (effectively turning your single-linked list implementation to doubly-linked list), or iterate until before the target node to delete.

Coding style

There are several coding style issues that would be good to correct by following PEP8, the Python style guide.


Instead of:

    if self.head == None:

The correct way to check for None is this:

    if self.head is None:

It's recommended to use triple double-quotes instead of single-quotes for docstrings, so instead of:

    '''
    Creates a new node and inserts it at the very end of the list
    '''

Use this:

    """
    Creates a new node and inserts it at the very end of the list
    """
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.