Why is hashing failing in LRU Cacheing Problem in Python - python-3.x

I recently stumbled across an example online for solving the LRUCache problem for interview questions. When I copied the end code into an IDE and compile/run the code, it was giving a KeyError while running through the test code in the main section.
class DLL:
def __init__(self, key, value):
self.key = key
self.value = value
self.next = None
self.prev = None
class LRUCache:
def __init__(self, capacity: int):
self.head = DLL(-1, -1)
self.tail = self.head
self.tracker = {}
self.capacity = capacity
self.length = 0
def get(self, key: int) -> int:
if key not in self.tracker:
return -1
node = self.tracker[key]
value = node.value
while node.next:
node.prev.next = node.next
node.next.prev = node.prev
self.tail.next = node
node.prev = self.tail
node.next = None
self.tail = node
return value
def put(self, key: int, value: int) -> None:
if key not in self.tracker:
node = self.tracker[key]
node.value = value
while node.next:
node.prev.next = node.next
node.next.prev = node.prev
self.tail.next = node
node.prev = self.tail
node.next = None
self.tail = node
else:
node = DLL(key, value)
self.tracker[hash] = node
self.tail.next = node
node.prev = self.tail
self.tail = node
self.length += 1
if self.length > self.capacity:
remove = self.head.next
self.head.next = self.head.next.next
self.head.next.prev = self.head
del self.tracker[remove.key]
self.length += 1
if __name__ == '__main__':
lru = LRUCache(3)
lru.put(1, 2)
lru.put(2, 2)
lru.put(3, 2)
lru.get(3)
lru.get(1)
lru.put(4, 2)
lru.get(4)
With an error message spitting out:
Traceback (most recent call last):
File "LRUCache.py", line 63, in <module>
lru.put(1, 2)
File "LRUCache.py", line 36, in put
node = self.tracker[key]
KeyError: 1
The problem is linked to the line self.tracker[hash] = node but I cannot figure out why. Can someone please explain what is going on here? The original posting does not have a comments section, so I have been stuck trying to figure out someone else's code on my own.

Related

how to make this code short for reverse circular LL in python

class node:
def __init__(self,data):
self.data = data
self.next = None
class circularLL:
def __init__(self):
self.head = None
self.tail = None
def insertNode(self,data):
newnode = node(data)
if self.head is None:
self.head = newnode
self.tail = newnode
else:
newnode.next = self.head
tail = self.tail
tail.next = newnode
self.tail = tail.next
def printCLL(self):
current = self.head
while current.next is not None and current.next is not self.head:
print(current.data, end="----")
current = current.next
print(current.data)
def reverseCLL(self):
head = self.head
tail = self.tail
if head == tail:
return
else:
count = 0
while head.next is not tail:
count +=1
if count ==1:
next = head.next
prev = head
head.next = tail
newtail = head
head = next
else:
next = head.next
head.next = prev
prev = head
head = next
if count is not 0:
next = head.next
head.next = prev
prev = head
tail.next = prev
self.head = tail
self.tail = newtail
else:
head.next = tail
self.tail.next = head
self.head = tail
self.tail = head
c = circularLL()
c.insertNode(1)
c.printCLL()
c.insertNode(2)
c.insertNode(3)
c.insertNode(4)
c.insertNode(5)
c.insertNode(6)
c.printCLL()
c.reverseCLL()
c.printCLL()
I have written a code for circular linkedlist. but I think the reverse part can be shorter so how to make the reverse part short ? and what are the alternative to do this ???
and can any one tell me when I am using the assignment operator for a object then that variable is pointing to that original object or the object copy is assign to the variable's memory location ??
There are still some issues in your code:
When the first node is added, that node's next reference is left to None, which means the list is not really circular. You should actually never have a node whose next reference is None, so I'd suggest to change the Node constructor so that it doesn't assign that value, but instead makes the node self referencing (self.next = self). That also means you can remove most of the None checks from your code.
The printCLL method fails when the list is empty. It should first check that condition.
You should not use is not when comparing with a literal. So instead do if count != 0: or even if count:
Some other remarks:
It is common practice to use PascalCase for class names, so Node instead of node and CircularLL instead of circularLL.
As in a circular, non-empty list the head node is always the one after the tail node, there is no real need to store a separate reference to the head node. You just need self.tail.
As the class name already clearly indicates that we're dealing with a circular linked list, there is no need to have the CLL suffix in the method names. And for a similar reason I'd call the insert method just insert.
Instead of providing a print method, it is probably better practice to provide a __repr__ method, which the native print function will call when it exists.
Make a linked list instance iterable by defining an __iter__ method. Then the above __repr__ can also rely on that.
As to the core of your question: yes this code for reversing a list can be greatly reduced. There is no need to have a counter.
Proposed code:
class Node:
def __init__(self,data):
self.data = data
self.next = self
class CircularLL:
def __init__(self):
self.tail = None
def insert(self, data):
newnode = Node(data)
if self.tail:
newnode.next = self.tail.next
self.tail.next = newnode
self.tail = newnode
def __iter__(self):
if not self.tail:
return
current = self.tail
while current.next is not self.tail:
current = current.next
yield current.data
yield current.next.data
def __repr__(self):
return "----".join(map(repr, self))
def reverse(self):
tail = self.tail
if not tail:
return
prev = tail
curr = prev.next
self.tail = curr
while curr != tail:
curr.next, prev, curr = prev, curr, curr.next
curr.next = prev
c = CircularLL()
c.insert(1)
print(c)
c.insert(2)
c.insert(3)
c.insert(4)
c.insert(5)
c.insert(6)
print(c)
c.reverse()
print(c)

Linked List Implementation Python , printing none as first node

I have created a linked list implementation and everything seems to be working however when I print the linked list it begins with None and then continues to print the numbers accordingly. I am not sure why this happens because if the incoming value is the first value it assigns head to that value
class Node:
def __init__(self,value=None):
self.value = value
self.next = None
class LinkedList:
def __init__(self):
self.head = Node()
def append(self, value):
new_node = Node(value)
new_node.value = value
new_node.next = None
if self.head is None:
self.head = new_node
else:
tmp = self.head
while tmp.next is not None:
tmp = tmp.next
tmp.next = new_node
def show(self):
start = self.head
while start.next is not None:
print(start.value," -> ",end="")
start = start.next
print(start.value)
linked_list = LinkedList()
linked_list.append(4)
linked_list.append(3)
linked_list.append(8)
linked_list.append(2)
linked_list.append(9)
linked_list.show()
You set self.head = Node() in the __init__ function, which would make the first value in your linkedlist None. Maybe you meant to set self.head = None?
In LinkedList constructor you have set self.head=Node(). you may replace it with None but it throws an error or it runs infintly.
In def show(self): point the start node (val=None) to the next node i.e start=self.head.next
start=self.head
None -> 4 -> 3 -> 8 -> 2 -> 9
After moving the pointer
start=self.head.next
4 -> 3 -> 8 -> 2 -> 9

I'm trying to make my basics strong with data structures and linked lists, I keep facing this error

Here is the code:
class Node:
def __init__(self, data, next):
self.data = data
self.next = None
class Linked List:
def __init__(self, data):
self.data = data
self.head = None
def print_list(self):
cur_node = self.head
while cur_node:
print(cur_node.data)
cur_node = cur_node.next
def append(self, data):
self.data = data
new_node = Node(data, next)
if self.head is None:
self.head = new_node
return
last_node = self.head
while last_node:
last_node = last_node.next
last_node.next = new_node
I call it as below:
lst=Linked List()
lst.append("A")
lst.append("B")
lst.print_list()
The error i get is:
C:\Users\vaish\PycharmProjects\hello\venv\Scripts\python.exe C:/Users/vaish/PycharmProjects/hello/hackerrank.py
Traceback (most recent call last):
File "C:/Users/vaish/PycharmProjects/hello/hackerrank.py", line 202, in <module>
llist=LinkedList()
TypeError: __init__() missing 1 required positional argument: 'data'
Process finished with exit code 1
Any corrections?
Your Linked list implementation has some bugs. I have comment the code where you need changes
class Node:
def __init__(self, data): # do not pass next here
self.data = data
self.next = None # new node should have next=None by default
class Linked_List:
def __init__(self): # no need to pass data here, pass data in append function
self.head = None # self.head is our head of linked list
def print_list(self):
cur_node = self.head
while cur_node:
print(cur_node.data)
cur_node = cur_node.next
def append(self, data):
#self.data = data
new_node = Node(data) # data will be assigned to the newley created node automatically
if self.head is None:
self.head = new_node
return
last_node = self.head
while last_node.next: # you have get the last node
last_node = last_node.next
last_node.next = new_node # this should come outside of the while loop
#set next of last node as newly created node
lst=Linked_List()
lst.append("a")
lst.append("b")
lst.append("c")
lst.print_list()
Your Linked List Constructor is a parameterized one def init(self, data):
So python expects the value of data when you create Linked List using this lst=Linked List().
Just provide some data there, it will work.
eg.
lst=Linked List("A")

delete node in BST python

class Node:
def __init__(self,data):
self.data = data
self.left = None
self.right = None
self.count = 1
class BST:
def __init__(self):
self.root = None
self.size = 0
def _insert(self,val,node):
if val<node.data:
if node.left:
self._insert(val,node.left)
else:
self.size+=1
node.left = Node(val)
elif val>node.data:
if node.right:
self._insert(val,node.right)
else:
self.size+=1
node.right = Node(val)
else:
self.size+=1
node.count+=1
def insert(self,val):
if not self.root:
self.size+=1
self.root = Node(val)
else:
self._insert(val,self.root)
def _inorder(self,node):
if node:
self._inorder(node.left)
self.bstview.append(node.data)
self._inorder(node.right)
def view(self):
if self.root:
self.bstview = []
self._inorder(self.root)
return self.bstview
def _min(self,node):
if node.left:
return self._min(node.left)
return node
def _max(self,node):
if node.right:
return self._max(node.right)
return node
def min(self):
if self.root:
return self._min(self.root).data
def max(self):
if self.root:
return self._max(self.root).data
def _remove(self,val,node):
if not node:
return
if val < node.data:
node.left = self._remove(val,node.left)
elif val > node.data:
node.right = self._remove(val,node.right)
else:
if node.count >1:
self.size-=1
node.count-=1
return node
elif not node.left:
temp = node.right
del node
self.size-=1
return temp
elif not node.right:
temp = node.left
del node
self.size-=1
return temp
else:
temp = self._min(node.right)
node.data = temp.data
node.right = self._remove(temp.data,node.right)
return node #WHY THIS IS NECESSARY PLEASE EXPLAIN!!
def remove(self,val):
if self.root:
self._remove(val,self.root)
b = BST()
for x in list(map(int,"20 10 30 5 15 25 35 3 7 23 27 40 4 45".split(" "))):
b.insert(x)
print(b.view())
print(b.min(),b.max(),b.size)
b.remove(20)
b.remove(45)
b.remove(1)
print(b.view())
print(b.min(),b.max(),b.size)
In the above code of BST, why is it necessary to return node in the _remove() method? The if/elif statements are already returning nodes?
Also, I would be greatly thankful to you if you can guide me to an easy/less time consuming way to understand recursive functions (I know what they are and how they work, my question is if someone asks me how a particular recursive function works then I would be able to explain him without confusing myself).

Cant insert item at start of linked list - python

I'm currently learning about linked lists for a class and ran into trouble.
I have to be able to insert an item before a specified item so
(Apple,banana,pear)before pear would be (Apple,banana,newItem,pear)
and I managed this with this code:
def insert_before(head,data,location):
current = head
found = False
while not found:
if current.next.data == location:
new_node = Node(data)
new_node.next = current.next
current.next = new_node
found = True
else:
current = current.next
But my issue arose when trying to insert an item before the first item in the list, to try this I thought of doing it as such:
if head.data == location:
new_node = Node(data)
head.next = head.next
new_node.next = head`
But this doesn't seem to work. Any guides I found on here were to add an item AFTER the first item, any tips would be appreciated.
In the provided source code, the use of head.next = head.next do nothing and doesn't change the value of the head node.
To modify the value of the first node in a linked list by inserting a new node before, it is necessary update the head variable.
Solution 1 - assign the head as the returned value of the insert_before() function.
The variable previous is used to modify the node before and when is
= None, the first node must be modified.
def insert_before(head,data,location):
current = head
previous = None
found = False
while not found:
if (current != None):
if (current.data == location):
new_node = Node(data)
if (previous != None):
new_node.next = previous.next
previous.next = new_node
else:
new_node.next = head
head = new_node
found = True
else:
previous = current
current = current.next
else:
print('location ',location,' not found.')
break
return head
The use of the function becomes:
myhead = Node(5)
print(myhead.data) # >> 5
myhead = insert_before(myhead,3,5)
print(myhead.data) # >> 3
print(myhead.next.data) # >> 5
Solution 2 - use the OOP of Python by adding a headattribute in the Node class.
The OOP approach is quite similar, but instead of storing the head
node outside the function, the function insert_before() is added to
the class Node.
Step 1 - add in the class Node the attribute head.
class Node(object):
def __init__(self, data=None, next_node=None):
self.data = data
self.next = next_node
self.head = self # store the first node
Step 2 - use the internal self.head instead of the head as parameter
In that case, when the new node shall be inserted before the first
node, the assignment is very simple self.head = new_node.
def Insert_Before(self,data,location):
current = self.head
previous = None
found = False
print('insert ',data,' before ',location)
while not found:
if (current != None):
if (current.data == location):
new_node = Node(data)
if (previous != None):
new_node.next = previous.next
previous.next = new_node
else:
new_node.next = self.head
self.head = new_node # update the first node
found = True
else:
previous = current
current = current.next
else:
print('location ',location,' not found.')
break
return
The use of the function becomes:
myhead = Node(5)
print(myhead.data)
myhead.insert_before(3,5)
print(myhead.head.data)
print(myhead.head.next.data)
Warning: instead of using myhead as the first node, use the
myhead.head attribute.

Resources