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()