cs107-lecture-examples

Example codes used during Harvard CS107 lectures
git clone https://git.0xfab.ch/cs107-lecture-examples.git
Log | Files | Refs | README | LICENSE

linked_list.py (4815B)


      1 #!/usr/bin/env python3
      2 # vim: foldmethod=marker
      3 # File       : linked_list.py
      4 # Description: Linked list implementation
      5 # Copyright 2022 Harvard University. All Rights Reserved.
      6 
      7 import copy
      8 
      9 
     10 # Node {{{1
     11 class Node:
     12     def __init__(self, key, *, data=None, next=None):
     13         self.key = key
     14         self.data = data
     15         self.next = next
     16 
     17 
     18 # LinkedList {{{1
     19 class LinkedList:
     20     # List creation {{{2
     21     def __init__(self, key, data, *, reverse=False):
     22         """
     23         Construct a linked list.
     24 
     25         Parameter:
     26         ----------
     27         key : list-like
     28             List of keys used to identify nodes
     29         data : list-like
     30             List of data objects to be associated with nodes
     31         reverse : bool
     32             Create linked list in reverse order
     33 
     34         """
     35         start = Node(key[0], data=data[0])  # create root node
     36         end = start
     37         for k, d in zip(key[1:], data[1:]):
     38             if reverse:
     39                 end = self._prepend(end, Node(k, data=d))
     40             else:
     41                 end = self._append(end, Node(k, data=d))
     42         self.root = start if not reverse else end
     43 
     44     @staticmethod
     45     def _append(anchor, node):
     46         anchor.next = node
     47         return node
     48 
     49     @staticmethod
     50     def _prepend(anchor, node):
     51         node.next = anchor
     52         return node
     53 
     54     # Sequence properties {{{2
     55     def __getitem__(self, key):
     56         _, node = self._getitem(key)
     57         return node
     58 
     59     def __len__(self):
     60         node = self.root
     61         count = 0
     62         while node != None:
     63             count += 1
     64             node = node.next
     65         return count
     66 
     67     def _getitem(self, key):
     68         """
     69         Private method to retrieve a list element given a key.
     70 
     71         Parameters
     72         ----------
     73         key :
     74             Key for node identification
     75 
     76         Returns
     77         -------
     78         Tuple with reference to predecessor node and reference to node with
     79         given key
     80 
     81         """
     82         node = self.root
     83         prev = node
     84         while node != None:
     85             if node.key == key:
     86                 return (prev, node)
     87             prev = node
     88             node = node.next
     89         raise KeyError(f"Key '{key}' not found")
     90 
     91     def __str__(self):
     92         node = self.root
     93         pretty = ""
     94         while node != None:
     95             pretty += f"node: {hex(id(node))} : key={node.key} : data={node.data}\n"
     96             node = node.next
     97         return pretty
     98 
     99     # Node insertion {{{2
    100     def insert(self, key, node, *, before=False):
    101         """Key based node insertion [at worst O(n)]."""
    102         p = self[key]  # search for key explicitly -> O(n)
    103         if before:  # insert before
    104             tmp = copy.copy(p)  # beware!
    105             p.key = node.key
    106             p.data = node.data
    107             node = tmp
    108             p.next = node
    109         else:  # insert after
    110             assert p is not None
    111             node.next = p.next
    112             p.next = node
    113         return node
    114 
    115     # Node removal {{{2
    116     def remove(self, key):
    117         """Key based node removal [at worst O(n)]."""
    118         if self.root.key == key:
    119             del_node = self.root
    120             self.root = self.root.next
    121         else:
    122             # search for key explicitly -> O(n)
    123             prev, del_node = self._getitem(key)
    124             prev.next = del_node.next
    125         del del_node
    126 
    127     # # Iterator {{{2
    128     # def __iter__(self):
    129     #     return LinkedListForwardIterator(self.root)
    130 
    131 
    132 # LinkedListForwardIterator {{{1
    133 class LinkedListForwardIterator:
    134     def __init__(self, start):
    135         self.node = start  # required state to keep track of the iteration
    136 
    137     def __next__(self):
    138         if self.node is None:
    139             raise StopIteration
    140         curr_node = self.node
    141         self.node = self.node.next  # modify state of iterator instance
    142         return curr_node
    143 
    144     def __iter__(self):
    145         return self  # iter() applied on an iterator must return the iterator itself
    146 
    147 
    148 # main() {{{1
    149 def main():
    150     # Create some dummy data class
    151     class Data:
    152         def __init__(self, data):
    153             self.data = data
    154 
    155         def __str__(self):
    156             return f'<value={self.data} {hex(id(self))}>'
    157 
    158     data = [Data(d) for d in list('ABCDE')]
    159     keys = [f'ID_{k}' for k in range(len(data))]
    160 
    161     # create a list
    162     linked_list = LinkedList(keys, data)
    163     print(len(linked_list))  # __len__
    164     print(linked_list['ID_0'])  # __getitem__ (key/index is not an integer!)
    165 
    166     # # Iterate over list {{{2
    167     # for node in linked_list:
    168     #     print(node)
    169     # # 2}}}
    170 
    171     # Insertion / removal examples {{{2
    172     # insert a new node with different key type
    173     linked_list.insert(keys[1], Node(10, data='Data can be arbitrary'))
    174 
    175     # remove the node again
    176     linked_list.remove(10)
    177     linked_list.remove('ID_0')
    178     # 2}}}
    179 
    180 
    181 if __name__ == "__main__":
    182     main()