Struggling to debug my leetcode solution for LRU Cache - python

I am working on the LeetCode problem LRU Cache and I want to understand why my code does not work.
My idea was to keep a queue of valid keys and a dictionary of tuples (isValid, value). Everytime get is called I wanted to make the next key on the queue invalid and update the dictionary tuple for that key to be (False, value).
I know that there are better solutions out there I just really want to know why mine doesn't work, so I can become a better debugger/programmer in general. Thank you so much in advance for any and all help!
This is my code so far:
class LRUCache:
def __init__(self, capacity: int):
self.d = dict()
self.capacity = capacity
self.validKeys = Queue()
def get(self, key: int) -> int:
(isValid, value) = self.d.get(key, (False, -1))
if isValid:
return value
else:
return -1
def put(self, key: int, value: int) -> None:
if self.validKeys.getSize() >= self.capacity:
lastUsedKey = self.validKeys.dequeue()
(_, val) = self.d[lastUsedKey]
self.d[lastUsedKey] = (False, val)
# only enqueue a new key if it doesn't exist in the cache already, was part of the problem statement
if self.d.get(key, None) is None:
self.d[key] = (True, value)
self.validKeys.enqueue(key)
This is the expected output:
[null,null,null,1,null,-1,null,-1,3,4]
This is my output:
[null,null,null,1,null,2,null,-1,3,4]
Note: I'm pretty sure my queue implementation is correct I've posted it below with the test cases I tested it with:
class Node:
def __init__(self, val):
self.prev = None
self.next = None
self.val = val
class Queue:
def __init__(self):
self.head = None
self.tail = self.head
self.size = 0
def enqueue(self, val):
if self.size == 0:
self.head = Node(val)
self.tail = self.head
else:
self.tail.next = Node(val)
self.tail = self.tail.next
self.size += 1
def dequeue(self):
if self.size == 0:
raise "Queue empty"
else:
val = self.head.val
self.head = self.head.next
self.size -= 1
return val
def getSize(self):
return self.size
def __repr__(self):
curr = self.head
res = ''
while curr is not None:
res += str(curr.val)
curr = curr.next
return res
obj = Queue()
assert obj.getSize() == 0
obj.enqueue(1)
assert obj.getSize() == 1
assert obj.dequeue() == 1
assert obj.getSize() == 0
obj.enqueue(1)
obj.enqueue(2)
obj.enqueue(3)
assert obj.getSize() == 3
assert obj.dequeue() == 1
assert obj.dequeue() == 2
assert obj.dequeue() == 3
assert obj.getSize() == 0

Related

Remove Leading and Trailing Single Quote when String Formatting in Python

I'm trying to learn python so I decided to implement a Singly Linked List but I stumbled upon a weird problem.
As you could see from the screenshot above, the output when I'm trying to search for the index of a node has a leading and trailing single quote.
class Node:
next_node = None
def __init__(self, data):
self.data = data
def __repr__(self):
return "<Node data: %s>" % self.data
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
def is_empty(self):
return self.head == None
def size(self):
current = self.head
count = 0
while current:
count += 1
current = current.next_node
return count
def append(self, data):
new_node = Node(data)
if self.is_empty():
self.head = new_node
else:
self.tail.next_node = new_node
self.tail = new_node
def search_node(self, key):
current = self.head
position = 0
while current:
if current.data == key:
# This returns with a leading and trailing single quote
return "<Node index: %s>" % position
else:
current = current.next_node
position += 1
return None
def search_index(self, index):
if index == 0:
return self.head
elif index == self.size() - 1:
return self.tail
else:
current = self.head
position = 0
while position < index:
current = current.next_node
position += 1
return current
def __repr__(self):
nodes = []
current = self.head
if self.is_empty():
return "<Empty>"
if self.size() == 1:
return "<Head: Tail: %s>" % current.data
while current:
if current is self.head:
nodes.append("<Head: %s>" % current.data)
elif current.next_node is None:
nodes.append("<Tail: %s>" % current.data)
else:
nodes.append("<%s>" % current.data)
current = current.next_node
return "-> ".join(nodes)
What's causing this behavior? I saw some articles about the repr function causing it but I'm not really sure.
I made some notes in your code that will hopefully help with identifying why your outputs are not consistent.
class LinkedList:
def __init__(self):
self.head = None < - node or None
self.tail = None < - node or None
def search_node(self, key): # <- function return will be str or None
current = self.head
position = 0
while current:
if current.data == key:
return "<Node index: %s>" % position # <-- Returning a string
else:
current = current.next_node
position += 1
return None # <--- Returning None
def search_index(self, index): # <- function returns node or None
if index == 0:
return self.head # <- returing node or None
elif index == self.size() - 1:
return self.tail # <- returning node or None
else:
current = self.head
position = 0
while position < index:
current = current.next_node
position += 1
return current # <- returning node or None
The __repr__(str) of a string will always have the quotes surrounding it.

Python initialize a list in a class

I am trying to solve a leetcode question using list: Design Circular Queue.
But it throws an error:
IndexError: list assignment index out of range
self.queue[self.tail] = value
My code:
class MyCircularQueue:
def __init__(self, k: int):
self.size = k
self.queue = []
self.head = -1
self.tail = -1
def enQueue(self, value: int) -> bool:
if self.tail + 1 == self.head: #queue full
return False
elif self.tail + 1 == self.size:
self.tail = 0
self.queue[self.tail] = value
return True
else:
self.tail += 1
self.queue[self.tail] = value
return True
def deQueue(self) -> bool:
if len(self.queue) == 0: #queue empty
return False
elif self.tail + 1 == self.size:
self.queue.pop(self.head)
self.head = -1
return True
else:
self.queue.pop(self.head)
self.head += 1
return True
def Front(self) -> int:
if self.head == -1:
return False
return self.queue[self.head]
def Rear(self) -> int:
if self.tail == -1:
return False
return self.queue[self.tail]
def isEmpty(self) -> bool:
if len(self.queue) == 0:
return True
return False
def isFull(self) -> bool:
if len(self.queue) == self.size:
return True
return False
Other similar posts say maybe the list is not initialized but I can't figure out what I did wrong here.
Question link: https://leetcode.com/problems/design-circular-queue/
Edit:
As pointed out below, append will be an organic way to do it in Python. However, I need index to implement circular queue so I took a different approach:
self.tail = (self.tail + 1) % self.size #example circular increment
initialize list with None other than an empty list
deQueue: replacing item with None other than pop
Updated code:
class MyCircularQueue:
def __init__(self, k: int):
self.size = k
self.queue = [None] * k #replace queue = [] -> add queue[idx] will throw a error
self.head = -1
self.tail = -1
def enQueue(self, value: int) -> bool:
if self.isFull() == True: #queue full
return False
elif self.tail == -1: #add first item
self.tail += 1
self.head += 1
self.queue[self.tail] = value
else:
self.tail = (self.tail + 1) % self.size
self.queue[self.tail] = value
return True
def deQueue(self) -> bool:
if self.isEmpty() == True: #queue empty
return False
elif self.head == self.tail: #at last item
self.queue[self.head] = None
self.head = -1
self.tail = -1
else:
self.queue[self.head] = None #replace item with None other than pop, which will remove None from the list
self.head = (self.head + 1) % self.size #not self.head += 1
return True
def Front(self) -> int:
if self.head == -1:
return -1
return self.queue[self.head]
def Rear(self) -> int:
if self.tail == -1:
return -1
print(self.tail)
return self.queue[self.tail]
def isEmpty(self) -> bool:
if self.head == -1:
return True
return False
def isFull(self) -> bool:
if (self.tail + 1) % self.size == self.head:
return True
return False
Reference: Circular Queue Structure Explanation
queue[index] refers to the indexth item in the list queue.
If there are no items in the list, index 0 does not exist, so you can't access queue[0].
The exception "list assignment index out of range" raises when index >= len(list). You initialize self.tail to be 0, and the length of the list is also 0.
The Pythonic way to add an item to the last place of a list is to use list.append:
self.queue.append(value)

How does a node with return self work?(Python)

So this is the node part of a singly linked list. I am not supposed to change the way it has been coded, but I dont know how this type of structure would work. Self.link cannot event be accessed to point towards another part of the list. Does anyone know how to work with such a Node class?
class Node:
def __init__(self, inval=None):
self.val = inval
if inval==None:
self.link = self
print (self)
def __str__(self):
if self.val == None:
return ''
else:
return str(self.val)
def __repr__(self):
return str(self)
Here is another implementation of the linked list, which has a slightly different styled node.
class LinkedList:
lock = 0
if lock == 0:
tempdata = None
def __init__(self, *args):
self.head = Node() # Node at the head of the list
self.current = None # Node currently pointed to by the iterator
self.count = 0
def insert(self, value):
NewNode =Node(value)
NewNode.link = self.head
self.head = NewNode
self.count += 1
def __iter__(self):
self.current = self.head
return self
def __next__(self):
self.current = LinkedList.tempdata
if LinkedList.lock == 0:
self.current = self.head
LinkedList.lock += 1
else:
pass
if self.current.value == None:
LinkedList.lock = 0
raise StopIteration
previous = self.current
self.current = self.current.link
LinkedList.tempdata = self.current
return previous
def __str__(self):
result = ''
self.current = self.head
while self.current.value is not None:
if self.current.link.value is None:
result += str(self.current.value)
else:
result += str(self.current.value) + ' -> '
self.current = self.current.link
return result
def search(self, value):
found = 0
temp = None
out= False
while found == 0:
try:
temp = LinkedList.__next__(self)
if temp.value == value:
found += 1
out = temp
except StopIteration:
pass
return out
def delete(self, value):
print ("hwta")
found = 0
temp = None
head = self.head
if head.value == value:
print ("Head")
if head.link.value != None:
self.head = head.link
else:
self.head = Node()
else:
while found == 0:
try:
temp = LinkedList.__next__(self)
if temp.link.value == value:
if temp.link.link.value == None:
temp.link = Node()
break
else:
temp.link = temp.link.link
print ("tails")
break
except StopIteration:
pass
def __repr__(self):
return str(self)
#a = Node()
#print(a) # 3
#b = Node("Hullo")
#print(b) # 'Hullo'
#lst = LinkedList()
#lst.insert(2)
#lst.insert(3)
#lst.insert(5)
#lst.insert(6)
#lst.insert(7)
#lst.insert(6)
#print(lst) # 5 -> 3 -> 2
#c = lst.search(2)
#print(c) # 3
#print(c.link) # 5
#lst.insert(2)
#print(lst.head.link.link) # 3
lst.delete(6)
print (lst)
#print(next(lst)) # should print 5, 3, 2 on separate lines
#lst.delete(2)
#print(lst) # 5 -> 3
#print(len(lst)) # 2
#for u in lst:
# print(u)
Nothing in the Node implementation that would prevent you from using it in a List class. Just pretend that the final three lines of Node.__init__() don't exist.
Here is one way to use the professor's Node in your List.
class Node:
def __init__(self, inval=None):
self.val = inval
if inval==None:
self.link = self
print (self)
def __str__(self):
if self.val == None:
return ''
else:
return str(self.val)
def __repr__(self):
return str(self)
class List:
def __init__(self):
self.head = None
def prepend(self, val):
head = Node(val)
head.link = self.head
self.head = head
def append(self, val):
if self.head is None:
self.prepend(val)
else:
p = self.head
while p.link is not None:
p = p.link
p.link = Node(val)
p.link.link = None
def __str__(self):
result = '<'
p = self.head
while p is not None:
result += str(p) + ', '
p = p.link
result += '>'
return result
l = List()
l.append(3)
l.prepend(2)
l.append(4)
l.prepend(1)
l.append(5)
print(str(l))
And here is the result:
<1, 2, 3, 4, 5, >

Implemeting custom iterable object in python

This is my implementation of a custom singly linked list in Python.
class SList:
def __init__(self):
self.root = None
self.size = 0
def insert(self, item):
if not item:
raise ValueError('Cannot add None item to a list')
self.size += 1
if self.root is None:
self.root = Node(item)
else:
p = Node(item)
p.next = self.root
self.root = p
"""Remove the element at the specific index"""
def remove(self, index):
if index < 0 or index >= self.size:
raise ValueError('Index cannot be negative or greater than the size of the list')
current = self.root
if index == 0:
self.root = self.root.next
else:
for _ in range(index -1):
current = current.next
p = current.next.next
if p is not None:
current.next = p
else:
current.next = None
self.size -= 1
def __len__(self):
return self.size
def __repr__(self):
res = '[ '
current = self.root
while current is not None:
res += str(current.data)
res += ' '
current = current.next
res += ']'
return res
def __iter__(self):
return self
def next(self):
........
This is the Node object
class Node:
def __init__(self, data):
try:
if not data:
raise ValueError
self.data = data
self.next = None
except ValueError:
raise ValueError('Node cannot be instantiated without an item')
I'm at a little loss at implementing the iter method. I see there are multiple ways to implement it and yield seems to be the common way forward. Would appreciate some help in implmenting it with yield
You can make your class iterable by making its __iter__ method a generator.
Here's some code that runs correctly on Python 2 or Python 3.
from __future__ import print_function
class Node(object):
def __init__(self, data):
if data is None:
raise ValueError('Node cannot be instantiated without an item')
self.data = data
self.nextnode = None
def __repr__(self):
return 'Node({})'.format(self.data)
class SList(object):
def __init__(self):
self.root = None
self.size = 0
def insert(self, item):
if item is None:
raise ValueError('Cannot add None item to a list')
self.size += 1
if self.root is None:
self.root = Node(item)
else:
p = Node(item)
p.nextnode = self.root
self.root = p
def remove(self, index):
""" Remove the element at the specific index """
if index < 0 or index >= self.size:
raise ValueError('Index cannot be negative or greater than the size of the list')
current = self.root
if index == 0:
self.root = self.root.nextnode
else:
for _ in range(index - 1):
current = current.nextnode
current.nextnode = current.nextnode.nextnode
self.size -= 1
def __len__(self):
return self.size
def __repr__(self):
res = []
current = self.root
while current is not None:
res.append(current.data)
current = current.nextnode
return str(res)
def __iter__(self):
current = self.root
while current is not None:
yield current
current = current.nextnode
# test
a = SList()
for c in 'ABCDE':
a.insert(c)
print(a)
gen = iter(a)
print('root', next(gen))
for node in gen:
print(node)
a.remove(2)
print(list(a))
for node in a:
print(node)
output
['E', 'D', 'C', 'B', 'A']
root Node(E)
Node(D)
Node(C)
Node(B)
Node(A)
[Node(E), Node(D), Node(B), Node(A)]
Node(E)
Node(D)
Node(B)
Node(A)
Try to follow this explanation:
pythontips.com/2013/09/29/the-python-yield-keyword-explained
In short and simplified form - in order to create a generator, you should create a function that contains the yield keyword once or more. Anytime yield is reached in the function execution, it is put on hold, and passes the value after the keyword to the caller.
def my_gen(arg):
yield arg * 10
for i in xrange(5):
if i / 2 == 0:
yield i
for x in my_gen(3):
print(x)
Will print:
30
0
2
4
The constructor of the Node class is also faulty, in addition to catching your own exception, you also have an error with your if condition not data will be True not only if data is False or None, but also if data is 0,an empty list, empty string and many more. Use data is None instead.

Python LinkedList Search

I'm writing code for a Linked List in Python and here's part of the code:
class LinkedList:
def __init__(self):
self.head = None
def search(self, n, value):
if n is None:
return False
elif n.data == value:
return True
else:
return search(n.next, value)
def append(self, new_value):
if self.head is None:
self.head = LinkedListNode(new_value)
else:
node = self.head
while node.next != None:
node = node.next
node.next = LinkedListNode(new_value)
def remove(self, position):
if position > 0:
node = self.head
l = 0
while node != position - 1:
l += 1
node = node.next
node.next = node.next.next
elif position == 0:
self.head = self.head.next
I'm just wondering how to implement the search() method? I think I have the right idea, but it's not working. Thank you!
When you call the method inside the same class, you need to qualify it with self.
def search(self, n, value):
if n is None:
return False
elif n.data == value:
return True
else:
return self.search(n.next, value) # <--
BTW, current search implementation requires user to pass n (LinkedList.head maybe). So I would make a wrapper to search from head, so user doesn't need to specify linked_list_instance.head every time:
def search_from_head(self, value):
return self.search(self.head, value)

Categories