I'm trying to make binary code from a Huffman tree. I am stuck at tree traversal. To traverse tree and print code for every character frequency, I've written following code.
def printCode(node,prefix=""):
if node.left:
prefix = prefix+"0"
printCode(node.left,prefix)
elif node.right:
prefix = prefix+"1"
printCode(node.right,prefix)
elif node.internal ==False:
print(node.data,prefix)
printCode(node,prefix="") #need change
Here is the necessary explanation:
If a node is not an internal node(node.internal=False) then it is a leaf, and at this point of traversal I'm printing the code with character frequencies. But i'm unable to go back to the previous internal node and continue with another branch of tree that has not been traversed yet. So this code is ended with only returning binary code for the first leaf of the tree.
The every node of tree is created with following code:
class Node:
def __init__(self,data,internal=False):
self.data = data #frequency of a char
self.internal = internal
self.left = None
self.right = None
The main problem with your logic is the use of the elif
def printCode(node):
if node.left:
node.left.prefix = node.prefix+"0"
printCode(node.left)
if node.right:
node.right.prefix = node.prefix+"1"
printCode(node.right)
if node.internal == False:
print(node.data,node.prefix)
This way it will traverse the left side of the tree until it reaches a leaf, when it reaches a leaf it will print the node data. At this point in the code it goes back to the last recursive call (the node before the leaf) and it will go to the right node, if this right node is a leaf it will print out its node information, then it will go back to the last recursive call
Let me know if you need a better explanation, or if there was a misunderstanding and this doesn't do what you want it to
UPDATE:
class Node:
def __init__(self,data,internal=False):
self.data = data #frequency of a char
self.internal = internal
self.left = None
self.right = None
#Add a prefix to your nodes, so each node keeps track of its own prefix
self.prefix = ""
Related
so i guess you are all fimilliar with a binary heap data structure if not.. Brilliant. org say
i.e. a binary tree which obeys the property that the root of any tree is greater than or equal to (or smaller than or equal to) all its children (heap property). The primary use of such a data structure is to implement a priority queue.
will one of the properties of a binary heap is that it must be filled from top to bottom (from root) and from right to left
I coded this algorithm to find the next available spot to insert the next number I add (I hard coded the first nodes so I can track more further down the tree
this search method is inspired by BFS(Breadth First Search) algorithm
note that in this code I only care about finding the next empty node without the need to keep the heap property
I tested the code but I don't think I tested it enough so if you spot problems, bugs or suggest any ideas, every comment is welcomed
def insert(self, data):
if self.root.data == None:
self.root.data = data
print('root', self.root.data)
else:
self.search()
def search(self):
print('search..L31')
queue = [self.root]
while queue:
curr = queue.pop(0)
print(curr.data)
if curr.right_child == None:
print('made it')
return
else:
queue.append(curr.left_child)
queue.append(curr.right_child)
h = Min_heap(10)
h.insert(2)
h.root.left_child = Node(3)
h.root.right_child = Node(5)
h.root.left_child.left_child = Node(8)
h.root.left_child.right_child = Node(7)
h.root.right_child.left_child = Node(9)
# The tree I am building...
# __2__
# / \
# 3 5
# / \ / \
# 8 7 9 ⨂
# ↑
# what am
# looking for
h.search()
there is another way to figuring this out which is basically translating the tree into an array/list using special formulas and then we just assume that the next data we want to insert is the last element in the previous array and then work back through the same formulas but I already know that algorithm and I thought why not trying to solve it as a graph soooo...
You should better implement a binary heap as a list (array). But if you want to do it with node objects that have left/right attributes, then the position for the next node can be derived from the size of the tree.
So if you enrich your heap class instances with a size attribute and maintain that attribute to reflect the current number of nodes in the tree, then the following method will tell you where the next insertion point is, in O(logn) time:
Take the binary representation of the current size plus 1. So if the tree currently has 4 nodes, take the binary representation of 5, i.e. 101. Then drop the leftmost (most significant) bit. The bits that then remain are an encoding of the path towards the new spot: 0 means "left", 1 means "right".
Here is an implementation of a method that will return the parent node of where the new insertion spot is, and whether it would become the "left" or the "right" child of it:
def next_spot(self):
if not self.root:
raise ValueError("empty tree")
node = self.root
path = self.size + 1
sides = bin(path)[3:-1] # skip "0b1" and final bit
for side in sides:
if side == "0":
node = node.left
else:
node = node.right
# use final bit for saying "left" or "right"
return node, ("left", "right")[path % 2]
If you want to guarantee balanced, just add to each node how many items are there or below. Maintain that with the heap. And when placing an element, always go to where there are the fewest things.
If you just want a simple way to place, just randomly place it. You don't have to be perfect. You will still on average be O(log(n)) levels, just with a worse constant.
(Of course your constants are better with the array approach, but you say you know that one and are deliberately not implementing it.)
I'm trying to retake my Python exams and i've never really gotten the hang of binary trees.For one exercise i have to make a function that takes an int and a tree as args and returns a list of each node mark on the specified depth.
i have the basic
#dataclass
class Node:
mark: Any
left: Optional['Node']
right: Optional['Node']
to work with. I know how to determine max depth of a binary tree
def maxDepth(node:Node):
if node is None:
return -1
else :
lDepth = maxDepth(node.left)
rDepth = maxDepth(node.right)
if (lDepth > rDepth):
return lDepth+1
else:
return rDepth+1
and tried using that with a while loop to stop at a certain depth.
def layer(n:int,node:Node):
result=[]
depth=maxDepth(node)
while depth != n:
new_depth=maxDepth(node)
result.append(node)
return result
but my code makes no sense. I've also thought if i could make a recursive depth-finding function that also counts each time it's called, but have no idea how to implement that. Any help is welcome, i don't want the direct solution, but if you could point me in the right direction that would be great :)
I think that something like this might help:
from typing import Any, Optional
class Node:
mark: Any
left: Optional['Node']
right: Optional['Node']
def traverse_tree(node:Node,currentDepth:int,desiredDepth:int):
if node != None:
traverse_tree(node.left,currentDepth+1,desiredDepth)
traverse_tree(node.right,currentDepth+1,desiredDepth)
if currentDepth==desiredDepth:
print(node)
You just have to start off with currentDepth as 0 and this code will traverse the tree, printing those elements with the desired depth.
I know this might be trivial but i just want to make sure. I believe it's runtime will be at most O(n). My reasoning is that every node will return a height value once throughout this recursive method. Or in other words we will visit every node in the tree once.
def height(self):
if self.is_empty():
return 0
else:
left_max = self._left.height()
right_max = self._right.height()
return max(left_max, right_max) + 1
You are performing DFS traversal on tree all nodes will be visited only one time.
So obviously it will take time of O(N) only.
I was looking at code for if a Binary Tree is a BST, and I was confused at how it is doing the comparison.
def is_bst(cur_node, prev_node_list):
if (not cur_node):
return True
#Check if the left sub-tree is a BST
if (not TreeNode.is_bst(cur_node.left, prev_node_list)):
return False
#If data in cur_node is <= previous node then it is not a BST
prev_node = prev_node_list[0]
if (prev_node and cur_node.data <= prev_node.data):
return False
#Update previous node to current node
prev_node_list[0] = cur_node
#Check if the right sub-tree is a BST
return TreeNode.is_bst(cur_node.right, prev_node_list)
I was wondering what
if (prev_node and cur_node.data <= prev_node.data):
return False
is doing. If the code is constantly checking the left subtrees, shouldn't the next value be less than the previous node?
The code visits all elements in sorted order. That is, first left nodes then current node than right node.
If you replace the check with the previous node with a print statement, you get the elements from smallest to biggest (if the tree was valid).
Now, it is sufficient to check, if these vistited elements are sorted.
To answer your question: the current node is checked after the left node. The code first goes to the very left leaf node.
I'm trying to recursively build a binary decision tree, for diagnosing diseases in python 3.
The builder takes a list of records (each is an illness and a list of its symptoms), and a list of symptoms, shown bellow:
class Node:
def __init__(self, data = "", pos = None, neg = None):
self.data = data
self.positive_child = pos
self.negative_child = neg
class Record:
def __init__(self, illness, symptoms):
self.illness = illness
self.symptoms = symptoms
records= [Record('A',['1','3']),
Record('B',['1','2']),
Record('C',['2','3']),
]
symptoms = ['1','2','3']
And builds a binary tree, each level checks if symptom is true, or false, with a child node for each one. The right child is always means the symtom is not present and the left one that it is present. For the example data the tree should look like this:
1
2 2
3 3 3 3
None B A None C None None Healthy
For example, the leaf A is reached by asking:
1 : True
2 : False
3 : True
and it's path is [1,3] (the trues)
Here is the code I'm using, but isn't working:
def builder(records, symptoms, path):
#Chekl if we are in a leaf node that matches an illness
for record in records:
if path == record.symptoms:
return Node(record.illness,None,None)
#No more symptoms means an empty leaf node
if len(symptoms) == 0:
return Node(None,None,None)
#create subtree
else:
symptom = symptoms.pop(0)
right_child = builder(records,symptoms,path)
path.append(symptom)
left_child = builder(records,symptoms,path)
return Node(symptom,right_child,left_child)
I tried a cold run, and in paper it worked fine. I'm not sure of what I'm missing, but the resulting tree has a lot of empty nodes, and not one with the illness. Maybe I'm messing up the path thing, but I'm not sure how to fix it right now.
Your symptoms.pop(0) is affecting the one symptoms list shared by all calls to builder. This is fine on the way down, since you want to consider only the subsequent symptoms. But when a recursive call returns, your list is missing elements. (If it returns without finding a match, it’s empty!) Similarly, the shared path keeps growing forever.
The simple if inefficient answer is to make new lists when recursing:
symptom=symptoms[0]
symptoms=symptoms[1:]
path=path+[symptom] # not +=