problems with BST in python - python

I have my BST class build like this :
class BST:
"""A Binary Search Tree."""
def __init__(self: 'BST', container: list =None) -> None:
"""
Initialize this BST by inserting the items from container (default [])
one by one, in the order given.
"""
# Initialize empty tree.
self.root = None
# Insert every item from container.
if container:
for item in container:
self.insert(item)
def __str__(self: 'BST'):
"""
Return a "sideways" representation of the items in this BST, with
right subtrees above nodes above left subtrees and each item preceded
by a number of TAB characters equal to its depth.
"""
# Tricky to do iteratively so we cheat,
# You could take up the challenge...
return BST._str("", self.root)
def insert(self: 'BST', item: object) -> None:
"""
Insert item into this BST.
"""
# Find the point of insertion.
parent, current = None, self.root
while current:
if item < current.item:
parent, current = current, current.left
else: # item > current.item
parent, current = current, current.right
# Create a new node and link it in appropriately.
new_node = _BSTNode(item)
if parent:
if item < parent.item:
parent.left = new_node
else: # item > parent.item
parent.right = new_node
else:
self.root = new_node
My problem is how to implement a contains function without using recursion. I have tried a couple loop methods but it always ended up with indent error.

The contains method will look very similar to your insert method, but even simpler because all you have to do is check if the item exists. So, traverse the BST as usual and return False if you reach the end, or True if you find the element:
def contains(self, item):
current = self.root
while current:
if item == current.item:
return True
if item < current.item:
current = current.left
else:
current = current.right
return False

Related

Python - reversing a linked list class module as input

I'm working on a problem that accepts a linked list as input, at this point I don't even know how to set up an example linked list.
My initial problem is understanding the following instruction:
Write a function that accepts a linked list as input, then reverses that linked list.
Does this simply involve defining a 'reversing summary' as part of the following or is there some other way of summarizing a linked lists in Python:
class Node(object):
# Initialize with a single datum and pointer set to None
def __init__(self, data=None, next_node=None):
self.data = data
self.next_node = next_node
# Returns the stored data
def get_data(self):
return self.data
# Returns the next node
def get_next(self):
return self.next_node
# Reset the pointer to a new node
def set_next(self, new_next):
self.next_node = new_next
def set_data(self, data):
self.data = data
class LinkedList(object):
# Top node in the list created on __init__
def __init__(self, head=None):
self.head = head
# Take in the new node and point the new node to the prior head O(1)
def insert(self, data):
new_node = Node(data)
new_node.set_next(self.head)
self.head = new_node
# Counts nodes O(n)
def size(self):
current = self.head
count = 0
while current:
count += 1
current = current.get_next()
return count
# Checks each node for requested data O(n)
def search(self, data):
current = self.head
found = False
while current and found is False:
if current.get_data() == data:
found = True
else:
current = current.get_next()
if current is None:
raise ValueError("Data not in list")
return current
# Similar to search, leapfrogs to delete, resetting previous node pointer to point to the next node in line O(n)
def delete(self, data):
current = self.head
previous = None
found = False
while current and found is False:
if current.get_data() == data:
found = True
else:
previous = current
current = current.get_next()
if current is None:
raise ValueError("Data not in list")
if previous is None:
self.head = current.get_next()
else:
previous.set_next(current.get_next())
It seems all of the code you have for your base Linked List class is there. You just need to reverse your Linked List. Reversing a linked list is like follows.
[1] -> [2] -> [3] -> NULL
You can tell that 1 is the Head and 3 is the Tail (meaning that it's the last node that is in the linked list because it points to NULL or None in Python). What you need to do is figure out some algorithm that will take that linked list and produce this result.
[3] -> [2] -> [1] -> NULL
Now 3 is the Head and 1 is the Tail.
So using Psuedocode:
Initalize 3 Nodes: prev, next, and current
current = head
prev = None
next = None
While current != None
next = current.next
current.next = prev
prev = current
current = next
head = prev

Error when deleting a node in a Linked List <ListNode.ListNode object at 0x0000000267320>

I am making a linkedlist, and I had to add in some different functions such as max, min , count and index for my list. I now have to add a remove function which is this snippet of code.
def removeItem(self, position):
''' removeItem removes a selected, because python has a built in "garbage remover",
you don't have to physically delete the node, you only have to skip that node link and python will destroy it
by it self.'''
currentNode = self.head
previousNode = None
count = 0
while count != position:
#This is a quick check to make sure the next node isn't empty.
if currentNode.link is None:
print("Position Invalid")
return None
previousNode = currentNode
currentNode = currentNode.link
count += 1
#Node.Link should link to the next node in the sequence.
previousNode.link = currentNode.link
return currentNode
I am basically just trying to link over the next node in the sequence, so that the built-in garbage remover will remove that node from the sequence. However, I am getting the following error message, which I know has to do something with my instance.
C:\Python33\python.exe "C:/Users/koopt_000/Desktop/College/Sophomore Semester 2/Computer Science 231/Chapter4/Test.py"
900
1
1
2
<ListNode.ListNode object at 0x0000000002679320>
Process finished with exit code 0
Why is it printing out this weird ListNode.ListNode object at the end?
Here is my testing code:
from ListNode import ListNode
from LinkedList import LinkedList
node1 = ListNode(1)
node2 = ListNode(900)
node3 = ListNode(3)
node4 = ListNode(99)
node1.link = node2
node2.link = node3
node3.link = node4
linked_list = LinkedList((1, 900, 3, 99))
print(linked_list.__max__())
print(linked_list.__min__())
print(linked_list.getCount(900))
print(linked_list.getIndex(3))
print(linked_list.removeItem(3))
This is my code for my ListNode class:
# ListNode.py
class ListNode(object):
def __init__(self, item = None, link = None):
'''creates a ListNode with the specified data value and link
post: creates a ListNode with the specified data value and link'''
self.item = item
self.link = link
This is my code for my LinkedList class:
from ListNode import ListNode
class LinkedList(object):
#--------------------------------------------------------------
def __init__(self, seq=()):
""" Pre: Creates a Linked List
Post: Creates a list containing the items in the seq=()"""
if seq == ():
# If there is no items to be put into the list, then it creates an empty one.
self.head = None
else:
# Creates a node for the first item.
self.head = ListNode(seq[0], None)
# If there are remaining items, then they're added while keeping track of the last node.
last = self.head
for item in seq[1:]:
last.link = ListNode(item, None)
last = last.link
self.size = len(seq)
#-------------------------------------------------------------
def __len__(self):
'''Pre: Nothing.
Post: Returns the number of items in the list.'''
return self.size
#-------------------------------------------------------------
def __max__(self):
''' Goes through each node and compares what the max is for the linked list.
Post: Finds the max of the linked list and returns that value.'''
if self.head is None:
return None
max_value = self.head.item
node = self.head.link
while node is not None:
if node.item > max_value:
max_value = node.item
node = node.link
return max_value
#--------------------------------------------------------------
def __min__(self):
''' Goes through each node and compares what the min is for the linked list.
Post: Finds the min of the linked list and returns that value.'''
if self.head is None:
return None
min_value = self.head.item
node = self.head.link
while node is not None:
if node.item < min_value:
min_value = node.item
node = node.link
return min_value
#--------------------------------------------------------------
def getCount(self, yourData):
''' This function counts the amount of times a certain item is in the Linked List.'''
count = 0
node = self.head
for i in range(self.size):
data = node.item
if data is yourData:
count += 1
node = node.link
return count
#--------------------------------------------------------------
def getIndex(self, yourData):
''' getIndex finds the index of the selected item and returns that value. '''
node = self.head
if node is None:
return None
for i in range(self.size):
data = node.item
if data == yourData:
return i
node = node.link
raise IndexError
#--------------------------------------------------------------
def removeItem(self, position):
''' removeItem removes a selected, because python has a built in "garbage remover",
you don't have to physically delete the node, you only have to skip that node link and python will destroy it
by it self.'''
currentNode = self.head
previousNode = None
count = 0
while count != position:
#This is a quick check to make sure the next node isn't empty.
if currentNode.link == None:
print("Position Invalid")
return None
previousNode = currentNode
currentNode = currentNode.link
count += 1
#Node.Link should link to the next node in the sequence.
previousNode.link = currentNode.link
return currentNode
#--------------------------------------------------------------
If anyone could help me out to find out why my removeItem function isn't working that would be helpful!
On a side note, I'm also trying to make a doubly linked list of this list, I know I need to add a prev_node function into my ListNode function, but what else do I need to add? Thanks again!
If your method is returning a <LinkNode object at 0xmemoryaddr> string then it is working fine. You are printing the removed node, and Python is using the default repr() representation for that instance.
If you wanted to make it more readable, you could give the ListNode a object.__repr__ method:
def __repr__(self):
next = 'None' if not self.link else '...' # just to indicate
return 'ListNode({!r}, {})'.format(self.item, next)
This then will print ListNode(99, None) instead of the <ListNode object at 0xmemoryaddr> string Python defaulted to:
>>> ll = LinkedList((1, 900, 3, 99))
>>> ll.head
ListNode(1, ...)
>>> ll.head.link
ListNode(900, ...)
>>> ll.head.link.link
ListNode(3, ...)
>>> ll.head.link.link.link
ListNode(99, None)
One thing you do have to take into account: you need to adjust the length of the list too; on successful removal, subtract 1 from self.size.

BST problems in python

class BST:
"""A Binary Search Tree."""
def __init__(self: 'BST', container: list =None) -> None:
"""
Initialize this BST by inserting the items from container (default [])
one by one, in the order given.
"""
# Initialize empty tree.
self.root = None
# Insert every item from container.
if container:
for item in container:
self.insert(item)
def insert(self: 'BST', item: object) -> None:
"""
Insert item into this BST.
"""
# Find the point of insertion.
parent, current = None, self.root
while current:
if item < current.item:
parent, current = current, current.left
else: # item > current.item
parent, current = current, current.right
# Create a new node and link it in appropriately.
new_node = _BSTNode(item)
if parent:
if item < parent.item:
parent.left = new_node
else: # item > parent.item
parent.right = new_node
else:
self.root = new_node
this is the code that I built for the BST class, I would like implement a max_node function that finds the maximum node without using recursion , how am I suppose to do that?
In a BST, the maximum node is simply the right-most node, so start at the head and just keep taking right children until you hit a node with no right child. This can easily be done iteratively (and in fact, that's probably how I would do it anyway).
In pseudocode:
max_node = head
while hasRightChild(max_node)
max_node = max_node.right_child;

Find number of elements smaller than a given element in BST

I have been struggling with this problem for a while and I am a Python beginner when it comes to BST, so I would appreciate some help. I am dynamically adding elements from an (unsorted) array into BST. That part is fine, I know how to do that. The next step, proved to be impossible with my current skill set. As I am adding elements to the tree, I need to be able to find current rank of any element in the tree. I know there are subtleties in this problem, so I would need help to at least find the number of nodes that are below the given node in BST. For example, in this case, node 15 has nodes 10,5 and 13 below it, so the function will return 3. Here is my existing code [this is a problem from Cracking the coding interview, chapter 11]
class Node:
"""docstring for Node"""
def __init__(self, data):
self.data = data
self.left=None
self.right=None
self.numLeftChildren=0
self.numRightChildren=0
class BSTree:
def __init__(self):
self.root = None
def addNode(self, data):
return Node(data)
def insert(self, root, data):
if root == None:
return self.addNode(data)
else:
if data <= root.data:
root.numLeftChildren+=1
root.left = self.insert(root.left, data)
else:
root.numRightChildren+=1
root.right = self.insert(root.right, data)
return root
def getRankOfNumber(self,root,x):
if root==None:
return 0
else:
if x>root.data :
return self.getRankOfNumber(root.right,x)+root.numLeftChildren+1
elif root.data==x:
return root.numLeftChildren
else:
return self.getRankOfNumber(root.left,x)
BTree=BSTree()
root=BTree.addNode(20)
BTree.insert(root,25)
BTree.insert(root,15)
BTree.insert(root,10)
BTree.insert(root,5)
BTree.insert(root,13)
BTree.insert(root,23)
You can go by this approach:
1. Have 2 more fields in each node numLeftChildren and numRightChildren.
2. Initialize both of them to 0 when you create a new node.
3. At the time of insertion, you make a comparison if the newly added node's
key is less than root's key than you increment, root's numLeftChildren and
call recursion on root's left child with the new node.
4. Do Same thing if new node's key is greater than root's key.
Now, come back to your original problem, You have to find out the number of children in left subtree. Just find out that node in O(logN) time and just print the numLeftChildren
Time Complexity: O(logN)
PS: I have added another field numRightChildren which you can remove if you are always interested in knowing the number of nodes in left subtree only.
You could modify your BST to contain the number of nodes beneath each node.
Or you could iterate over a traditional BST from least to greatest, counting as you go, and stop counting when you find a node of the required value.
You could simplify the code by using just instances of the Node class, as your BSTree instance operates on the root node anyway. Another optimization could be to not represent duplicate values as separate nodes, but instead use a counter of occurrences of the key:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
self.num_left_children = 0
self.occurrences = 1
def insert(self, data):
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
self.num_left_children += 1
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.occurrences += 1
def get_rank(self, data):
if data < self.data:
return self.left.get_rank(data)
elif data > self.data:
return (self.occurrences + self.num_left_children +
self.right.get_rank(data))
else:
return self.num_left_children
Demo:
root = Node(20)
root.insert(25)
root.insert(15)
root.insert(10)
root.insert(10)
root.insert(5)
root.insert(13)
root.insert(23)
print(root.get_rank(15)) # 4
You can use the below function to find the number of the nodes in the tree (including the root).
def countNodes(self, root):
if root == None:
return 0
else
return (1 + countNodes(root.left) + countNodes(root.right));
To find the number of nodes that lie below root, subtract 1 from the value returned by the function. I think this will help get you started on the problem.
Your code will look like:
class Node:
"""docstring for Node"""
def init(self, data):
self.data = data
self.left=None
self.right=None
self.depth=0
class BSTree:
def init(self):
self.root = None
def addNode(self, data):
return Node(data)
def insert(self, root, data):
if root == None:
return self.addNode(data)
else:
if data <= root.data:
root.left = self.insert(root.left, data)
else:
root.right = self.insert(root.right, data)
return root
BTree=BSTree()
root=BTree.addNode(20)
BTree.insert(root,25)
BTree.insert(root,15)
BTree.insert(root,10)
BTree.insert(root,5)
BTree.insert(root,13)
BTree.insert(root,23)
BTree.insert(root,23)
numLeft = countNodes(root->left);
numRight = countNodes(root->right);
numChildren = numLeft + numRight;

How would I fix this? builtins.AttributeError: 'NoneType' object has no attribute '_helper'

I keep on getting this error, and I don't know how I'd go about correcting it.
I'm trying to implement method count_less in class BST. And I'm writing a helper method in class _BSTNode and calling the helper within count_less using indirect recursion.
Thank you.
class BST:
"""A Binary Search Tree."""
def __init__(self: 'BST', container: list =[]) -> None:
"""
Initialize this BST by inserting the items from container (default [])
one by one, in the order given.
"""
# Initialize empty tree.
self.root = None
# Insert every item from container.
for item in container:
self.insert(item)
def __str__(self: 'BST') -> str:
"""
Return a "sideways" representation of the values in this BST, with
right subtrees above nodes above left subtrees and each value preceded
by a number of TAB characters equal to its depth.
"""
return self.root._str("") if self.root else ""
def insert(self: 'BST', item: object) -> None:
"""
Insert item into this BST.
"""
if self.root:
self.root.insert(item)
else:
self.root = _BSTNode(item)
def count_less(self: 'BST', item: object) -> int:
"""
Return the number of items in this BST that are strictly less tham
item.
"""
return self.root._helper(self.root, item)
class _BSTNode:
"""A node in a BST."""
def __init__(self: '_BSTNode', item: object,
left: '_BSTNode' =None, right: '_BSTNode' =None) -> None:
"""
Initialize this node to store item and have children left and right.
"""
self.item, self.left, self.right = item, left, right
def _str(self: '_BSTNode', indent: str) -> str:
"""
Return a "sideways" representation of the values in the BST rooted at
this node, with right subtrees above nodes above left subtrees and each
value preceded by a number of TAB characters equal to its depth, plus
indent.
"""
return ((self.right._str(indent + '\t') if self.right else '') +
indent + str(self.item) + '\n' +
(self.left._str(indent + '\t') if self.left else ''))
def insert(self: '_BSTNode', item: object) -> None:
"""
Insert item into the BST rooted at this node.
"""
if item < self.item:
if self.left:
self.left.insert(item)
else:
self.left = _BSTNode(item)
elif item > self.item:
if self.right:
self.right.insert(item)
else:
self.right = _BSTNode(item)
# else: # item == self.item
# pass # nothing to do: item is already in the tree
def _helper(self, item):
count = 0
if not self.item:
return 0
if self.item < item:
count += 1
count += count_less(self.left, item)
count += count_less(self.right, item)
return count
You didn't include a traceback, but the only reference to _helper in the code you've shown is here:
return self.root._helper(self.root, item)
The error message is telling you that self.root is None. Since you didn't show the code you ran to trigger this error, nobody can guess why you're calling it with self.root still None. But since BST.__init__() binds self.root to None, it's sure not surprising ;-)

Categories

Resources