The linked list in Python

This was my first attempt at a Python script. Actually, I lie. I’ve tried before, but lost interest after a little bit. Not sure why, because Python is fun, fun, fun! I did have to think a little bit before getting into it, but once I got going, it was smooth sailing. As with my Java Linked List, I used a Unit Testing framework, thanks to Python’sunittest package. It’s easy peasy. Oh, and of course, I used Eclipse with the great PyDev plugin for Python scripting.

One of the things that caught my eye with the Python linked list is that I did not need to declare the member variables for the class. I can simply set them, and the class magically knows about them. That’s pretty cool. In addition, what I would call a coding style of indentation has semantic meaning in Python: instead of blocks being enclosed in braces, they’re indented. Otherwise, no massive issues to report.

So, as you would expect, the linked list implementation has O(1) insert (because we insert at the end of the list) and O(n) delete (because we need to iterate the entire list to find the right node to delete). The other operations I’ve provided are add, remove, get_size, index_of, contains and to_string.

Note: I’ve only tested this implementation with strings. Python doesn’t have the same capability as Java with something like the equals() method. The rules for object comparison may make containing objects of arbitrary types in the list a little tricky. An excerpt from the Python documentation: “Most other types compare unequal unless they are the same object; the choice whether one object is considered smaller or larger than another one is made arbitrarily but consistently within one execution of a program.

The code and unit tests are available in my GitHub repository.

Part 1: The LinkedListNode class
The list node is simply the data and a reference to the next node. It’s stored in its own source file and is a class. The member variables holding the data and next are initialised in the __init__ method.

class LinkedListNode:

    def __init__(self, inData, inNext):
        """Construct a new Linked List Node"""
        self.data = inData
        self.next = inNext

Part 2: The LinkedList class
The linked list class has a first and last node reference and a size. We import the LinkedListNode class from its source file.

from com.danielacton.datastructures.LinkedListNode import LinkedListNode

class LinkedList:

    def __init__(self):
        """Construct a new LinkedList. The first node and last node are the same. Size is 0"""
        self.firstNode = LinkedListNode(None, None)
        self.lastNode = self.firstNode
        self.size = 0

Part 3: The add() method(s)
The add method replaces the lastNode reference with the node being added and makes the old last node reference the new node as the next node in the list. There are 2 add methods, one to add a single node and one to add multiple nodes (with a Python list being used to pass values to add in the latter method).

def add(self, data):
        """Add a node to the list"""
        node = LinkedListNode(data, None)
        node.data = data;

        if self.firstNode.data == None:
            self.firstNode = node
            self.lastNode = node
        else:
            self.lastNode.next = node
            self.lastNode = node

        self.size += 1

    def add_many(self, list_of_data):
        """Add a list of nodes to the linked list"""
        for x in list_of_data:
            self.add(x)

Part 4: The remove() method(s)
The remove method iterates the list looking for the node to delete. We actually look ahead in the list because we need to loop around the deleted node by having the previous node point to the following node as its next node. There are 2 remove methods, one to remove a single node and one to remove multiple nodes (with a Python list being used to pass values to remove in the latter method).

def remove(self, data):
        """Remove a node from the list"""
        currentNode = self.firstNode
        wasDeleted = False

        if self.size == 0:
            pass

        # The first node is being removed
        if data == currentNode.data:
            # This is the case where we have only one node in the list
            if currentNode.next == None:
                self.firstNode = LinkedListNode(None, None)
                self.lastNode = self.firstNode
                self.size = self.size - 1
                return

            # Here there are more than one nodes in the list
            currentNode = currentNode.next
            self.firstNode = currentNode
            self.size = self.size - 1
            return;

        while True:

            if currentNode == None:
                wasDeleted = False
                break

            # Check if the data of the next is what we're looking for
            nextNode = currentNode.next
            if nextNode != None:
                if data == nextNode.data:
                    # Found the right one, loop around the node
                    nextNextNode = nextNode.next
                    currentNode.next = nextNextNode

                    nextNode = None
                    wasDeleted = True
                    break

            currentNode = currentNode.next

        if wasDeleted:
            self.size = self.size - 1;

    def remove_many(self, list_of_data):
        """Remove a list of nodes from the linked list"""
        for x in list_of_data:
            self.remove(x)

Part 5: The helper method(s)
The to_string(), contains(), index_of and get_size() methods are provided to make things more useful.

def to_string(self):
        """Get a string representation of the list"""
        result = ""
        currentNode = self.firstNode
        i = 0

        result = result + "{"

        while currentNode != None:
            if i > 0:
                result = result + ","

            dataObj = currentNode.data

            if dataObj != None:
                result = result + dataObj

            currentNode = currentNode.next

            i = i + 1

        result = result + "}"
        return result

    def contains(self, data):
        """Check whether a node is in the list or not"""
        currentNode = self.firstNode

        while currentNode != None:
            if currentNode.data == data:
                return True
            else:
                currentNode = currentNode.next

        return False

    def index_of(self, data):
        """Find the position of a node in the list"""
        currentNode = self.firstNode
        pos = 0

        while currentNode != None:
            if (currentNode.data == data):
                return pos
            else:
                currentNode = currentNode.next
                pos = pos + 1

        return -1    

    def get_size(self):
        """Get the size of the list"""
        return self.size