Linked list, stacks, and queues - Sam647254/Programetoj GitHub Wiki

Linked list

The "list" data structure that you encountered in CS 135 is a linked list, specifically a singly linked list. It consists of nodes that hold a value and a pointer to the next node. The last node points to None.

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

A list is then just a wrapper around a Node. It supports the following operations: prepend (i.e. cons), head (i.e. first), and tail (i.e. rest):

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

   def prepend(self, new_value):
      self.list = Node(new_value, self.list)

   def head(self):
      if self.list is None:
         raise Exception("list is empty")
      return self.list.value

   def tail(self):
      if self.list is None:
         raise Exception("list is empty")
      return self.list.next

   def is_empty(self):
      return self.list is None

   def length(self):
      next_node = self.list
      count = 0
      while next_node is not None:
         count += 1
         next_node = next_node.after
      return count

All operations run in O(1) time, except for length which runs in O(n) time, in which n is the number of nodes.

Doubly linked list

In a doubly linked list, every node has a pointer to both the element before and after it.

class DoublyLinkedNode:
   def __init__(self, before, value, after):
      self.before = before
      self.value = value
      self.after = after

A doubly linked list holds a reference to both the head (the first element) and the tail (the last element):

class DoublyLinkedList:
   def __init__(self):
      self.head = None
      self.tail = None

   def prepend(self, new_value):
      new_head = DoublyLinkedNode(None, new_value, self.head)
      if self.head is not None:
         self.head.before = new_head
      if self.tail is None:
         self.tail = new_head
      self.head = new_head

   def append(self, new_value):
      new_tail = DoublyLinkedNode(self.tail, new_value, None)
      if self.tail is not None:
         self.tail.after = new_tail
      if self.head is None:
         self.head = new_tail
      self.tail = new_tail

   def length(self):
      next_node = self.head
      count = 0
      while next_node is not None:
         count += 1
         next_node = next_node.after
      return count

Stacks

A stack is a data structure that stores a sequence of data in which the entries are removed in the reversed order in which they are inserted (last in first out).

A stack supports the following operations:

  • push: prepends a node onto a stack. O(1) time.
  • pop: removes the last-pushed element from a stack. O(1) time.
  • (optional) size/length/count: returns the size of a stack. O(1) time.

A stack can be implemented with a singly linked list, or even more simply the built-in list:

stack = []
stack.append(1) # push
stack.append(2) # push
len(stack) # => 2
stack.pop() # => 2
stack.pop() # => 1

Queue

A queue is a data structure that stores a sequence of data in which the first elements inserted will be the first to be removed (first in first out). It supports the following operations:

  • enqueue/push: pushes a node to the end of a queue. O(1) time.
  • dequeue/shift: removes a node from the beginning of a queue. O(1) time.
  • (optional) size/length/count: returns the size of a queue. O(1) time.

A related data structure is called the "deque", or double-ended queue. It supports pushing and removing from both ends of the queue. The normal queue is a subset of the deque. The deque is built into Python.