Basically a tried to rewrite a recursive function in Python without using a helper function. The code is almost identical, but I'm getting lots of unexpected results. Can anyone tell what I'm missing?
Here is the working code:
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def reconstructBst(preOrderTraversalValues):
root_index = [0]
return reconstructFromRange(float("-inf"), float("inf"), preOrderTraversalValues, root_index)
def reconstructFromRange(low, high, values, root_index):
if root_index[0] == len(values):
return None
root_value = values[root_index[0]]
if root_value < low or root_value >= high:
return None
root_index[0] += 1
left_subtree = reconstructFromRange(low, root_value, values, root_index)
right_subtree = reconstructFromRange(root_value, high, values, root_index)
return BST(root_value, left_subtree, right_subtree)
Here is the non-working rewrite:
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def reconstructBst(preOrderTraversalValues, low=float("-inf"), high=float("inf"), root_index=[0]):
if root_index[0] == len(preOrderTraversalValues):
return None
root_value = preOrderTraversalValues[root_index[0]]
if root_value < low or root_value >= high:
return None
root_index[0] += 1
left_subtree = reconstructBst(preOrderTraversalValues, low, root_value, root_index)
right_subtree = reconstructBst(preOrderTraversalValues, root_value, high, root_index)
return BST(root_value, left_subtree, right_subtree)
Here is the error that comes up for most of the test cases:
list index out of range
Traceback (most recent call last):
File "/tester/json_wrapper.py", line 30, in getActual
result = program.reconstructBst(preOrderTraversalValues)
File "/tester/program.py", line 13, in reconstructBst
root_value = preOrderTraversalValues[root_index[0]]
IndexError: list index out of range
In the original code, whenever reconstructBst runs, it creates a new root_index = [0] and gives it to reconstructFromRange, which mutates root_index in the line root_index[0] += 1.
In your edit, you moved the creation root_index=[0] to a default argument of reconstructBst. It is not created when reconstructBst runs, but at its DEFINITION; think of it as an object that belongs to the function itself. Now every time reconstructBst runs, it changes its root_index value.
I'll bet that root_index was no longer [0] after the first run, and that caused a problem because preOrderTraversalValues only starts with 1 value and could only be indexed by preOrderTraversalValues[0].
The easy fix is to use a dummy immutable value to specify when to create a mutable object:
def reconstructBst(preOrderTraversalValues, low=float("-inf"), high=float("inf"), root_index=None):
if root_index == None:
root_index = [0]
...
Related
Trying to write a recursive algorithm for a depth limited search for a 6-puzzle game but for some reason i keep getting the above error and cannot understand why.
This is my code for the recursve depth limited search:
def rec_dls(node, limit):
global cutoff_occurred
cutoff = 0
failure = -1
if goal_state == node.state:
return node
elif limit == 0:
return cutoff # cutoff
else:
cutoff_occurred = False
actions = moves(node.state) # all possibles children of this state
for action in actions:
child = State(action, node, node.depth+1, node.cost+1)
result = rec_dls(child, limit - 1)
if result == cutoff:
cutoff_occurred = True
elif result != failure:
return result
if cutoff_occurred:
return cutoff
else:
return failure
def dls(limit):
node = State(initial_state, None, 0, 0)
return rec_dls(node, limit)
and also the State class:
class State:
def __init__(self, state, parent, depth, cost):
self.state = state
self.parent = parent
self.depth = depth
self.cost = cost
if self.state:
self.map = ''.join(str(e) for e in self.state)
def __eq__(self, other):
return self.map == other.map
def __lt__(self, other):
return self.map < other.map
this is the error i have in more details:
for reference, i am basing the logic of my work on this (from "Artificial Intelligence, A Modern Approach"):
The problem isn't when rec_dls returns an int. It's when it returns one of your State objects.
Consider the following lines of code:
result = rec_dls(child, limit - 1)
if result == cutoff:
# ...
Suppose rec_dls here returns one of your State objects. You then compare your State object against cutoff, which contains the int value 0, and because you override __eq__ in your State class, this comparison causes State.__eq__ to be called with other set to 0.
Being an int, 0 doesn't have a map attribute, hence your error.
Perhaps you want to include in your __eq__ method a check that other is another State object:
def __eq__(self, other):
return isinstance(other, State) and self.map == other.map
I have to determine whether given a list representing a tree, whether the tree is a valid BST (this question is taken from leetcode). I have seen other posts on this but I was wondering if someone could help me with my approach, since it is clearly not right. For example, for the tree [1,2,3] where 1 is the root, 2 is the left child, and 3 is the right child, my code returns true. Hopefully it only requires small changes, but it might be that the entire function's approach is incorrect.
Here is my code:
def isValidBST(self, root):
if (root == None):
return True
if (root.left == None or root.left.val < root.val):
return self.isValidBST(root.left)
if (root.right == None or root.right.val > root.val):
return self.isValidBST(root.right)
return False
Secondly, I have seen approaches with a helper function that takes in a min/max value, but that confuses me. If anyone would also like to explain why that approach is a good/better one, that would be greatly appreciated!
I'd make a min_max method for Nodes that finds the min and max values of the tree rooted at that Node. Do sanity checking while finding those, and then isValidBST can just catch the exception
def max_min(self):
'''
Returns maximum and minimum values of the keys of the tree rooted at self.
Throws an exception if the results are not correct for a BST
'''
l_max, l_min = self.left.max_min() if self.left else (self.val, self.val)
if l_max > self.val:
raise ValueError('Not a BST')
r_max, r_min = self.right.max_min() if self.right else (self.val, self.val)
if r_min < self.val:
raise ValueError('Not a BST')
return l_min, r_max
def isValidBST(self):
try:
if self.max_min():
return True
except ValueError:
return False
Here is one way to implement the validity check:
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def isValidBST(self):
'''
Simultaneously check for validity and set our own min and max values.
'''
self.min = self.max = self.value
if self.left:
if not self.left.isValidBST():
return False
if self.left.max >= self.value:
return False
self.min = self.left.min
if self.right:
if not self.right.isValidBST():
return False
if self.right.min < self.value:
return False
self.max = self.right.max
return True
assert BST(2, BST(1), BST(3)).isValidBST()
case = BST(2, BST(1, None, BST(3)))
assert case.left.isValidBST()
assert not case.isValidBST()
I have to reverse a doubly linked list(DLL) between two nodes. I've done this with singly linked lists(SLL), but I find it harder to do with DLL's? It might be the having to do it between two particular nodes.
Here is the code for my DLLNode & DLL. When I test this code it seems to do nothing to my DLL. Any tips on what I'm doing wrong??
EDIT: So I'm inputting a linked list 'a','b','c','d','e','f' and call twist('b','e'): This should result in the linked list 'a' 'b' 'e' 'd' 'c' 'f'
class DoublyLinkedListNode:
def __init__(self, item, prevnode = None, nextnode = None):
self._data = item
self.setnext(nextnode)
self.setprev(prevnode)
def data(self):
return self._data
def next(self):
return self._next
def prev(self):
return self._prev
def setprev(self, prevnode):
self._prev = prevnode
def setnext(self, nextnode):
self._next = nextnode
class DoublyLinkedList:
def __init__(self):
self._head = None
self._tail = None
self._length = 0
def _newnode(self, item, nextnode = None, prevnode = None):
return DoublyLinkedListNode(item, nextnode, prevnode)
def addfirst(self, item):
if self.isempty():
return self._addtoempty(item)
node = self._newnode(item, None, self._head)
self._head.setprev(node)
self._head = node
self._length += 1
def addlast(self, item):
if self.isempty():
return self._addtoempty(item)
node = self._newnode(item, self._tail, None)
self._tail.setnext(node)
self._tail = node
self._length += 1
def _addtoempty(self, item):
node = self._newnode(item, None, None)
self._head = self._tail = node
self._length = 1
def removefirst(self):
if len(self) <= 1:
return self._removelastitem()
data = self._head.data()
self._head = self._head.next()
self._head.setprev(None)
self._length -= 1
return data
def removelast(self):
if len(self) <= 1:
return self._removelastitem()
data = self._tail.data()
self._tail = self._tail.prev()
self._tail.setnext(None)
self._length -= 1
return data
def _removelastitem(self):
if self.isempty():
return None # Should probably raise an error.
data = self._head.data()
self._head = self._tail = None
self._length = 0
return data
def twist(self, endpt1, endpt2):
current = self._head
while current != None:
if current.data() == endpt1:
current.next().setnext(endpt2.next())
endpt2.setnext(current.next())
current.setnext(endpt2)
else:
current = current.next()
def isempty(self):
return len(self) == 0
def _nodes(self):
node = self._head
while node is not None:
yield node
node = node.next()
def __iter__(self):
for node in self._nodes():
yield node.data()
def __len__(self):
return self._length
def __str__(self):
items = [str(data) for data in self]
return ", ".join(items)
Here is the test I'm running:
def testtwist1(self):
n = [0,1,2,3,4,5]
L = DoublyLinkedList()
for i in n:
L.addlast(i)
L.twist(2,5)
print(L) # returns the list 0,1,2,3,4,5
I don't see how this executes at all. You've apparently set up a DLL, and then called DLL.twist('b', 'e'). Thus, endpt1 = 'b' and endpt2 = 'e'. You then compare endpt1 to current.data, but then you access endpt2.next. endpt2 is a single-character string.
You never reference the prev pointers at all.
The only time you try to alter anything is when you hit node b. Then you seem to try to exchange the next pointers of b and e (so b.next is f, and e.next is c, but that's all.
At this point, I expect that your forward links give you two lists: a->b->f and c->d->e->c (a cycle), and that the backward links are unchanged.
Get pencil and paper or a white board and walk through these changes, one statement at a time; that's what I do ...
I recovered twist from your previous post, fixed the indentation, and ran. As I explain above, this faults in execution:
Traceback (most recent call last):
File "so.py", line 113, in <module>
L.twist(2,5)
File "so.py", line 102, in twist
current.next().setnext(endpt2.next())
AttributeError: 'int' object has no attribute 'next'
There is no such thing as 2.next(). I don't think you're actually getting to your print statement. Also, print(L) will not print the node values in order; you haven't included a __repr__ method for your DLL object. __str__ would do the job, but you've use the list head pointer as if it were an iterable over the forward-linked list.
I strongly recommend that you back up and approach this with incremental programming: write a method or a few lines, test that, and don't continue until that works as you expect. Yes, this means that you're writing detailed tests as you go ... that's a good thing. Keep them, and keep running them.
class MyHashTable:
def __init__(self, capacity):
self.capacity = capacity
self.slots = [None] * self.capacity
def __str__(self):
return str(self.slots )
def __len__(self):
count = 0
for i in self.slots:
if i != None:
count += 1
return count
def hash_function(self, key):
slot = key % len(self.slots)
if key in self.slots:
return slot
elif (not key in self.slots) and len(self.slots) == self.capacity:
return slot
else:
for i in self.slots:
count = 0
if i == None:
return count
count += 1
def insert(self, key):
print(len(self.slots)) #Why does this show an output of 2?
if key in self.slots:
return -2
elif (not key in self.slots) and (len(self.slots) != self.capacity): #Now this cant execute
num = hash_function(key)
self.slots[num] = key
return num
elif (not key in self.slots) and len(self.slots) == self.capacity:
return -1
Im wondering why the commented part above in the insert(self, key) the print statement gives (2) instead of (0). The elif statement underneath wont execute since its giving a result of (2) instead of (0)
A function call of
x = MyHashTable(2)
print(len(x))
Should give: 0
You're initializing self.slots = [None] * self.capacity, so with capacity = 2, self.slots is [None, None], which is of length 2.
Your __len__ method doesn't run because len(self.slot) calls self.slot.__len__, not self.__len__. If you'd like to use your override method, you should be calling len(self) instead.
You have to call your __len__ function (by calling self.__len__() ) if you want the length of the elements which are not None. For lists None are valid entries.
By the way. It is always best to compare with None by a is None or a is not None instead of == or !=.
I want to search a value/character in a linked list and return the number of times the value/character is in the linked list. Also would it be easier if I just used recursion instead of tail recursion?
class MyList():
__slots__=('head','size')
class Empty():
__slots__=()
class NonEmpty():
__slots__=('data','next')
def mkMyList():
lst = MyList()
lst.head = mkEmpty()
lst.size = 0
return lst
def mkEmpty():
return Empty()
def mkNonEmpty(data,lst):
node = NonEmpty()
node.data = data
node.next = lst
return node
def count(l, value, c = 0):
l = mkMyList()
if l.head != value:
l.head = l.head.next
if l.head == value:
return count(l.head.next, value, c + 1)
if l.size == 0:
return c
When I try to test it, I get this:
count(s,'s',c= 0)
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
count(s,'s',c= 0)
File "C:\Users\Qasim\Desktop\Linked Lists.py", line 30, in count
l.head = l.head.next
AttributeError: 'Empty' object has no attribute 'next'
\
Rather than use recursion, I would use the iterator pattern. Here is one way to do it in the context of your problem:
class LinkedList(object):
class Node(object):
__slots__ = ('prev', 'next', 'value')
def __init__(self, prev=None, next=None, value=None):
self.prev = prev
self.next = next
self.value = value
def __init__(self, iterable=[]):
self.head = LinkedList.Node() # dummy node
self.tail = self.head
self.size = 0
for item in iterable:
self.append(item)
def __iter__(self):
current = self.head
while True:
if current.next is not None:
current = current.next
yield current.value
else:
raise StopIteration
def append(self, value):
self.tail.next = LinkedList.Node(prev=self.tail, value=value)
self.tail = self.tail.next
self.size += 1
def pop(self):
if self.size > 0:
value = self.tail.value
self.tail = self.tail.prev
self.tail.next = None
self.size -= 1
return value
else:
raise IndexError('pop from empty list')
def count(self, value):
cumsum = 0
for item in self:
if item == value:
cumsum += 1
return cumsum
By my defining a Python special method __iter__, one can sequentially access the elements of a LinkedList in the following manner:
l = LinkedList([1, 2, 3, 3, 3, 4, 5])
for value in l:
print(value)
which then makes the desired method count straight-forward to implement.
Note that I have used the Python generator syntax to implement __iter__, you can read about generators and the yield statement here.
Tracing your code:
l = mkMyList() # => head = Empty()
if l.head != value: # True since head is Empty()
l.head = l.head.next # Empty does not have a ".next" attribute
This is what the Traceback is telling you.
EDIT: Two more things: (1) I'm not sure why count is even calling mkMyList when it seems your intent is to pass it the list, l, in the function args. (2) I'm guessing you want to put the size-check if statement at the top of this function:
if l.size == 0:
return c
The issue I see is that the list in count is never initialized properly. In mkMyList(), the head element is set to and Empty, which has no next attribute. In count(), you only use mkMyList(). This means that l.head is an Empty, and there's no way it could have a next attribute. To fix this, I would recommend instantiating the list l using the given input.
With regards to the question about recursion: no, there is very little difference in terms of composing a tail recursive function versus a regularly recursive function.