More abstract data types

Linked Lists

Let’s say we want a type that has the following operations:

append(value)
stores a value
nth(n)
retrieve the n-th value stored
remove(n)
remove the nt-th value stored

These operations should sound familiar: they are the core operations on Python’s lists. These are also the operations on another abstract data type, which we might call a Sequence. We saw a similar type in Pyret: Pyret’s lists, defined as

data List:
  | empty
  | link(fst, rst :: List)
end

Python’s lists are implemented using contiguous chunks of memory. Pyret’s lists are different: they are constructed as a linked structure. Could we build something similar in Python? Yes–similar to binary search trees?

Linked list code

Here’s the implementation of linked lists that we developed in class:

class ListNode:
    def __init__(self, data, parent=None):
        self.data = data
        self.parent = parent
        self.next = None

class LinkedList:
    def __init__(self):
        self.fst = None

    def append(self, data):
        if not self.fst:
            self.fst = ListNode(data)
        else:
            node = self.fst
            while node.next:
                node = node.next
            node.next = ListNode(data, node)

    def find_nth(self, n: int) -> ListNode:
        node = self.fst
        while node:
            if n == 0:
                return node
            n -= 1
        raise IndexError("Index out of bounds")

    def nth(self, n: int):
        return self.find_nth(n).data

    def remove_node(self, node):
        node.parent.next = node.next
        node.next.parent = node.parent

    def remove(self, index: int):
        self.remove_node(self.find_nth(index))

Here’s a version with recursive methods:

class ListNode:
    def __init__(self, data, parent=None):
        self.data = data
        self.parent = parent
        self.next = None

class LinkedList:
    def __init__(self):
        self.fst = None

    def append_to(self, node: ListNode, data):
        if not node.next:
            node.next = ListNode(node, data)
        else:
            self.append_to(node.next, data)

    def append(self, data):
        if not self.fst:
            self.fst = ListNode(data)
        else:
            self.append_to(self.fst, data)

    def find_nth_after(self, node: ListNode, n: int) -> ListNode:
        if not node:
            raise IndexError("Index out of bounds")
        if n == 0:
            return node
        return self.find_nth_after(node.next, n - 1)

    def find_nth(self, n: int) -> ListNode:
        return self.find_nth_after(self.root, n)

    def nth(self, n: int):
        return self.find_nth(n).data

    def remove_node(self, node):
        if node.parent:
            node.parent.next = node.next
        if node.next:
            node.next.parent = node.parent

    def remove(self, index: int):
        self.remove_node(self.find_nth(index))