Introduction
So I'm doing a course on edX and have been working on this practice assignment for
the better part of 3 hours, yet I still can't find a way to implement this method
without it taking to long and timing out the automatic grader.
I've tried 3 different methods all of which did the same thing.
Including 2 recursive approaches and 1 non-recursive approach (my latest).
The problem I think I'm having with my code is that the method to find children just takes way to long because it has to iterate over the entire list of nodes.
Input and output format
Input includes N on the first line which is the size of the list which is given on line 2.
Example:
5
-1 0 4 0 3
To build a tree from this:
Each of the values in the array are a pointer to another index in the array such that in the example above 0 is a child node of -1 (index 0). Since -1 points to no other index it is the root node.
The tree in the example has the root node -1, which has two children 0 (index 1) and 0 (index 3). The 0 with index 1 has no children and the 0 with index 3 has 1 child: 3 (index 4) which in turn has only one child which is 4 (index 2).
The output resulting from the above input is 4. This is because the max height of the branch which included -1 (the root node), 0, 3, and 4 was of height 4 compared to the height of the other branch (-1, and 0) which was height 2.
If you need more elaborate explanation then I can give another example in the comments!
The output is the max height of the tree. The size of the input goes up to 100,000 which was where I was having trouble as it has to do that it in exactly 3 seconds or under.
My code
Here's my latest non-recursive method which I think is the fastest I've made (still not fast enough). I used the starter from the website which I will also include beneath my code. Anyways, thanks for the help!
My code:
# python3
import sys, threading
sys.setrecursionlimit(10**7) # max depth of recursion
threading.stack_size(2**27) # new thread will get stack of such size
def height(node, parent_list):
h = 0
while not node == -1:
h = h + 1
node = parent_list[node]
return h + 1
def search_bottom_nodes(parent_list):
bottom_nodes = []
for index, value in enumerate(parent_list):
children = [i for i, x in enumerate(parent_list) if x == index]
if len(children) == 0:
bottom_nodes.append(value)
return bottom_nodes
class TreeHeight:
def read(self):
self.n = int(sys.stdin.readline())
self.parent = list(map(int, sys.stdin.readline().split()))
def compute_height(self):
# Replace this code with a faster implementation
bottom_nodes = search_bottom_nodes(self.parent)
h = 0
for index, value in enumerate(bottom_nodes):
h = max(height(value, self.parent), h)
return h
def main():
tree = TreeHeight()
tree.read()
print(tree.compute_height())
threading.Thread(target=main).start()
edX starter:
# python3
import sys, threading
sys.setrecursionlimit(10**7) # max depth of recursion
threading.stack_size(2**27) # new thread will get stack of such size
class TreeHeight:
def read(self):
self.n = int(sys.stdin.readline())
self.parent = list(map(int, sys.stdin.readline().split()))
def compute_height(self):
# Replace this code with a faster implementation
maxHeight = 0
for vertex in range(self.n):
height = 0
i = vertex
while i != -1:
height += 1
i = self.parent[i]
maxHeight = max(maxHeight, height);
return maxHeight;
def main():
tree = TreeHeight()
tree.read()
print(tree.compute_height())
threading.Thread(target=main).start()
Simply cache the previously computed heights of the nodes you've traversed through in a dict and reuse them when they are referenced as parents.
import sys, threading
sys.setrecursionlimit(10**7) # max depth of recursion
threading.stack_size(2**27) # new thread will get stack of such size
class TreeHeight:
def height(self, node):
if node == -1:
return 0
if self.parent[node] in self.heights:
self.heights[node] = self.heights[self.parent[node]] + 1
else:
self.heights[node] = self.height(self.parent[node]) + 1
return self.heights[node]
def read(self):
self.n = int(sys.stdin.readline())
self.parent = list(map(int, sys.stdin.readline().split()))
self.heights = {}
def compute_height(self):
maxHeight = 0
for vertex in range(self.n):
maxHeight = max(maxHeight, self.height(vertex))
return maxHeight;
def main():
tree = TreeHeight()
tree.read()
print(tree.compute_height())
threading.Thread(target=main).start()
Given the same input from your question, this (and your original code) outputs:
4
Related
Load the graph at the input. This graph started as a tree (i.e. an unoriented graph that does not contain loops) with n vertices numbered 1 to n, to which one edge was added that it did not contain. The input graph is represented as a list of its edges, where a_i b_i says that the graph has an edge between the vertices a_i and b_i.
Program will list which edge we can remove from the graph to create a tree with n vertices from the graph. If more than one answer is possible, answer with the one at the input last.
For example, to input:
1 2
1 3
2 3
Program will answer 2 3
For input:
1 2
2 3
3 4
1 4
1 5
Answer 1 4
I have a code that can determine if numbers are a tree, but I don't know how to make it so that they can be entered, and how to make it so that it removes unnecessary edges:
from collections import defaultdict
class Graph():
def __init__(self, V):
self.V = V
self.graph = defaultdict(list)
def addEdge(self, v, w):
self.graph[v].append(w)
self.graph[w].append(v)
def isCyclicUtil(self, v, visited, parent):
visited[v] = True
for i in self.graph[v]:
if visited[i] == False:
if self.isCyclicUtil(i, visited, v) == True:
return True
elif i != parent:
return True
return False
def isTree(self):
visited = [False] * self.V
if self.isCyclicUtil(0, visited, -1) == True:
return False
for i in range(self.V):
if visited[i] == False:
return False
return True
g1 = Graph(5)
g1.addEdge(1, 0)
g1.addEdge(0, 2)
g1.addEdge(0, 3)
g1.addEdge(2, 3)
if g1.isTree() == True:
print("Tree")
else:
print("Not Tree")
You can read input from standard input via the input function.
Your code currently creates the graph structure, and then determines whether two nodes are connected by walking through the graph with a depth-first traversal.
I would however suggest a slightly more efficient algorithm: instead of creating a graph, create a disjoint set. There are several libraries out there that offer this data structure, but I'll throw my own in the below code.
This structure keeps track of which nodes belong to the same connected group(s).
Then the algorithm becomes simple: for each edge you read from the input, indicate (using the disjoint set interface) that the two involved nodes belong to the same set. Before doing that however, check whether they already are in the same set. If this happens, stop the algorithm and output this edge.
Here is the generic DisjointSet class I will be using:
class DisjointSet:
class Element:
def __init__(self):
self.parent = self
self.rank = 0
def __init__(self):
self.elements = {}
def add(self, key):
el = self.Element()
self.elements[key] = el
return el
def find(self, key, add_if_not_exists=False):
el = self.elements.get(key, None)
if not el:
if add_if_not_exists:
el = self.add(key)
return el
# Path splitting algorithm
while el.parent != el:
el, el.parent = el.parent, el.parent.parent
return el
def union(self, key=None, *otherkeys):
if key is not None:
root = self.find(key, True)
for otherkey in otherkeys:
el = self.find(otherkey, True)
if el != root:
# Union by rank
if root.rank < el.rank:
root, el = el, root
el.parent = root
if root.rank == el.rank:
root.rank += 1
And here is the code specific for the problem:
def solve():
visited = DisjointSet()
while True:
edge = input().split()
a = visited.find(edge[0])
b = visited.find(edge[1])
if a and a is b: # This edge creates a cycle
print(" ".join(edge))
break # Stop reading more input
visited.union(*edge)
As you can see, this code assumes that the input will have an edge that creates a cycle (like is stated in the problem description). So it only has a way to stop the process when such an offending edge is found.
I tried making a Tree as a part of my Data Structures course. The code works but is extremely slow, almost double the time that is accepted for the course. I do not have experience with Data Structures and Algorithms but I need to optimize the program. If anyone has any tips, advices, criticism I would greatly appreciate it.
The tree is not necessarily a binary tree.
Here is the code:
import sys
import threading
class Node:
def __init__(self,value):
self.value = value
self.children = []
self.parent = None
def add_child(self,child):
child.parent = self
self.children.append(child)
def compute_height(n, parents):
found = False
indices = []
for i in range(n):
indices.append(i)
for i in range(len(parents)):
currentItem = parents[i]
if currentItem == -1:
root = Node(parents[i])
startingIndex = i
found = True
break
if found == False:
root = Node(parents[0])
startingIndex = 0
return recursion(startingIndex,root,indices,parents)
def recursion(index,toWhomAdd,indexes,values):
children = []
for i in range(len(values)):
if index == values[i]:
children.append(indexes[i])
newNode = Node(indexes[i])
toWhomAdd.add_child(newNode)
recursion(i, newNode, indexes, values)
return toWhomAdd
def checkHeight(node):
if node == '' or node == None or node == []:
return 0
counter = []
for i in node.children:
counter.append(checkHeight(i))
if node.children != []:
mostChildren = max(counter)
else:
mostChildren = 0
return(1 + mostChildren)
def main():
n = int(int(input()))
parents = list(map(int, input().split()))
root = compute_height(n, parents)
print(checkHeight(root))
sys.setrecursionlimit(10**7) # max depth of recursion
threading.stack_size(2**27) # new thread will get stack of such size
threading.Thread(target=main).start()
Edit:
For this input(first number being number of nodes and other numbers the node's values)
5
4 -1 4 1 1
We expect this output(height of the tree)
3
Another example:
Input:
5
-1 0 4 0 3
Output:
4
It looks like the value that is given for a node, is a reference by index of another node (its parent). This is nowhere stated in the question, but if that assumption is right, you don't really need to create the tree with Node instances. Just read the input into a list (which you already do), and you actually have the tree encoded in it.
So for example, the list [4, -1, 4, 1, 1] represents this tree, where the labels are the indices in this list:
1
/ \
4 3
/ \
0 2
The height of this tree — according to the definition given in Wikipedia — would be 2. But apparently the expected result is 3, which is the number of nodes (not edges) on the longest path from the root to a leaf, or — otherwise put — the number of levels in the tree.
The idea to use recursion is correct, but you can do it bottom up (starting at any node), getting the result of the parent recursively, and adding one to 1. Use the principle of dynamic programming by storing the result for each node in a separate list, which I called levels:
def get_num_levels(parents):
levels = [0] * len(parents)
def recur(node):
if levels[node] == 0: # this node's level hasn't been determined yet
parent = parents[node]
levels[node] = 1 if parent == -1 else recur(parent) + 1
return levels[node]
for node in range(len(parents)):
recur(node)
return max(levels)
And the main code could be as you had it:
def main():
n = int(int(input()))
parents = list(map(int, input().split()))
print(get_num_levels(parents))
I'm trying to implement a custom tree, for a problem that takes the input as the number of nodes and the edges/connections between each node and gives the output as the length of the shortest path to the leaf node from the root.
For the smaller test cases, I've tried it gave correct output but for some reason, it gives runtime error on hackerank for all the other cases, is there a bug? I tried to find it but I coudnt
Code:
class tree:
class Node:
def __init__(self,parent,children=[]):
self.Parent=parent # p capital
self.children=children
def __init__(self,n,connections=[]):
self.root = self.Node(None,[])
self.n = n # tree.n
self.connections = connections # list of all edges
self.Positions = {} # initializing all positions in dictionary , for all nodes
self.Positions[1]=self.root # 1 is always root
if self.n>1:
for i in range(2,n+1):
self.Positions[i]=self.Node(None,[])
def joinNode(self,parent,node): # number of the two nodes , like 1-3 etc..
self.Positions[parent].children.append(self.Positions[node])
self.Positions[node].Parent=self.Positions[parent]
def joinAll(self):
for p,n in self.connections:
self.joinNode(p,n)
def Depth(self,p):
"""Return the number of levels separating Position p from the root."""
if p==self.root:
return 0
else:
return 1 + self.Depth(p.Parent)
def minDepth(self):
return min(self.Depth(self.Positions[p]) for p in self.leafnodes())
def leafnodes(self):
return list(set(x[0] for x in self.connections)^set(i for i in range(1,n+1)))
if __name__=='__main__':
n=int(input())
if n==1:
print(0)
else:
connections=[]
for i in range(1,n):
array=input()
edge=list(map(int,array.split()))
connections.append(edge)
mytree=tree(n,connections)
mytree.joinAll()
print(mytree.minDepth())
print(mytree.connections)
Sample input
5
1 4
4 2
2 3
3 5
sample output
4
I generated perfectly balanced binary tree and I want to print it. In the output there are only 0s instead of the data I generated. I think it's because of the line in function printtree that says print(tree.elem), cause in the class self.elem = 0.
How can I connect these two functions generate and printtree?
class BinTree:
def __init__(self):
self.elem = 0
self.left = None
self.right = None
def generate(pbt, N):
if N == 0:
pbt = None
else:
pbt = BinTree()
x = input()
pbt.elem = int(x)
generate(pbt.left, N // 2)
generate(pbt.right, N - N // 2 - 1)
def printtree(tree, h):
if tree is not None:
tree = BinTree()
printtree(tree.right, h+1)
for i in range(1, h):
print(end = "......")
print(tree.elem)
printtree(tree.left, h+1)
Hope somebody can help me. I am a beginner in coding.
For example:
N=6, pbt=pbt, tree=pbt, h=0
input:
1
2
3
4
5
6
and the output:
......5
............6
1
............4
......2
............3
I'd suggest reading up on: https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/
Basically, there are three ways to traverse a binary tree; in-order, post-order and pre-order.
The issue with your print statement is that, you're reassigning the tree that is being passed in, to an empty tree.
if tree is not None:
tree = BinTree()
Right? If tree is not none and has something, lets reassign that to an empty tree.
Traversing a tree is actually a lot more simpler than you'd imagine. I think the complexity comes in just trying to imagine in your head how it all works out, but the truth is that traversing a tree can be done in 3 - 4 lines.
i' m new to algorithm, and I developed such function for finding the tree height of the input sequence.
import sys, threading
class TreeHeight:
def read(self):
self.n = int(sys.stdin.readline())
self.parent = list(map(int, sys.stdin.readline().split()))
def compute_height(self):
# Replace this code with a faster implementation
maxHeight = 0
for vertex in range(self.n):
height = 0
i = vertex
while i != -1:
height += 1
i = self.parent[i]
maxHeight = max(maxHeight, height)
return maxHeight
def main():
tree = TreeHeight()
tree.read()
print(tree.compute_height())
threading.Thread(target=main).start()
That works like this:
5
4 -1 4 1 1
And output
3
Because there are 5 nodes with numbers from 0 to 4, node 0 is a child of node 4, node 1 is the root,node 2 is a child of node 4, node 3 is a child of node 1 and node 4 is a child of node1. The height of this tree is 3, because the number of vertices on the path from root 1 to leaf 2 is 3
My solution is working, but it is not optimized for deep trees. For example, program crashes for input 100000.
What is the better solution in my case?