Nodes at given distance in binary tree (Amazon SDE-2) - python-3.x

Given a binary tree, a target node in the binary tree, and an integer value k, find all the nodes that are at distance k from the given target node. No parent pointers are available.
link to the problem on GFG: LINK
Example 1:
Input :
20
/ \
8 22
/ \
4 12
/ \
10 14
Target Node = 8
K = 2
Output: 10 14 22
Explanation: The three nodes at distance 2
from node 8 are 10, 14, 22.
My code
from collections import defaultdict
class solver:
def __init__(self):
self.vertList = defaultdict(list)
def addEdge(self,u,v):
self.vertList[u].append(v)
def makeGraph(self,root):
visited = set()
queue = []
queue.append(root)
while len(queue) > 0:
curr = queue.pop(0)
visited.add(curr)
if curr.left is not None and curr.left not in visited:
self.vertList[curr.data].append(curr.left.data)
self.vertList[curr.left.data].append(curr.data)
queue.append(curr.left)
if curr.right is not None and curr.right not in visited:
self.vertList[curr.data].append(curr.right.data)
self.vertList[curr.right.data].append(curr.data)
queue.append(curr.right)
def KDistanceNodes(self,root,target,k):
self.makeGraph(root)
dist = {}
for v in self.vertList:
dist[v] = 0
visited2 = set()
queue2 = []
queue2.append(target)
while len(queue2) > 0:
curr = queue2.pop(0)
visited2.add(curr)
for nbr in self.vertList[curr]:
if nbr not in visited2:
visited2.add(nbr)
queue2.append(nbr)
dist[nbr] = dist[curr] + 1
ans = []
for v in dist:
if dist[v] == k:
ans.append(str(v))
return ans
#{
# Driver Code Starts
#Initial Template for Python 3
from collections import deque
# Tree Node
class Node:
def __init__(self, val):
self.right = None
self.data = val
self.left = None
# Function to Build Tree
def buildTree(s):
# Corner Case
if (len(s) == 0 or s[0] == "N"):
return None
# Creating list of strings from input
# string after spliting by space
ip = list(map(str, s.split()))
# Create the root of the tree
root = Node(int(ip[0]))
size = 0
q = deque()
# Push the root to the queue
q.append(root)
size = size + 1
# Starting from the second element
i = 1
while (size > 0 and i < len(ip)):
# Get and remove the front of the queue
currNode = q[0]
q.popleft()
size = size - 1
# Get the current node's value from the string
currVal = ip[i]
# If the left child is not null
if (currVal != "N"):
# Create the left child for the current node
currNode.left = Node(int(currVal))
# Push it to the queue
q.append(currNode.left)
size = size + 1
# For the right child
i = i + 1
if (i >= len(ip)):
break
currVal = ip[i]
# If the right child is not null
if (currVal != "N"):
# Create the right child for the current node
currNode.right = Node(int(currVal))
# Push it to the queue
q.append(currNode.right)
size = size + 1
i = i + 1
return root
if __name__ == "__main__":
x = solver()
t = int(input())
for _ in range(t):
line = input()
target=int(input())
k=int(input())
root = buildTree(line)
res = x.KDistanceNodes(root,target,k)
for i in res:
print(i, end=' ')
print()
Input:
1 N 2 N 3 N 4 5
target = 5
k = 4
Its Correct output is:
1
And Your Code's output is:
[]
My logic:
-> First convert the tree into a undirected graph using BFS / level order traversal
-> Traverse the graph using BFS and calculate the distance
-> Return the nodes at k distance from the target
What I think:
First of all in the given test case the tree representation seems to be in level order however, in the failing test case it doesn't look like level order or maybe my logic is wrong?
Input Format:
Custom input should have 3 lines. First line contains a string representing the tree as described below. Second line contains the data value of the target node. Third line contains the value of K.
The values in the string are in the order of level order traversal of the tree where, numbers denote node values, and a character ā€œNā€ denotes NULL child.
For the above tree, the string will be: 1 2 3 N N 4 6 N 5 N N 7 N

The mistake is that the logic that is done in "init" should be done for every use-case (reinitializing self.vertList), not just once at the beginning.
Change:
def __init__(self):
self.vertList = defaultdict(list)
to:
def __init__(self):
pass
and:
def KDistanceNodes(self,root,target,k):
self.makeGraph(root)
dist = {}
...
to:
def KDistanceNodes(self, root, target, k):
self.vertList = defaultdict(list) # <-- move it here
self.makeGraph(root)
dist = {}
...
The original code kept accumulating the nodes and "remembered" the previous use-cases.
Also pay attention that you should return ans sorted, meaning that you should not append the numbers as strings so that you'll be able to sort them, change: ans.append(str(v)) to: ans.append(v) and return sorted(ans).

Related

Is there a way to nest a recursive function that updates a list with outer enclosing scope?

I'm trying to solve the following problem.
Given the root of a binary tree, construct a 0-indexed m x n string matrix res that represents a formatted layout of the tree. The formatted layout matrix should be constructed using the following rules:
The height of the tree is height and the number of rows m should be equal to height + 1.
The number of columns n should be equal to 2**(height+1) - 1.
Place the root node in the middle of the top row (more formally, at location res[0][(n-1)/2]).
For each node that has been placed in the matrix at position res[r][c], place its left child at res[r+1][c-2height-r-1] and its right child at res[r+1][c+2**(height-r-1)].
Continue this process until all the nodes in the tree have been placed.
Any empty cells should contain the empty string "".
Return the constructed matrix res.
This is my code:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
def find_h(node: Optional[TreeNode]) -> int:
if not node:
return 0
return 1 + max(find_h(node.left), find_h(node.right))
class Solution:
def printTree(self, root: Optional[TreeNode]) -> List[List[str]]:
height = find_h(root)
res = [['' for _ in range(2 ** height - 1)] for _ in range(height)]
def dispNode(node: Optional[TreeNode], r: int, c: int):
res[r][c] = str(node.val)
if node.left:
dispNode(node.left, r + 1, c - 2**(height-r-2))
if node.right:
dispNode(node.right, r + 1, c + 2**(height-r+2))
dispNode(root, 0, 2**(height - 1) - 1)
return res
I get an index out of bounds error and if I use a try-except block on the res[r][c] = str(node.val) line, I still only get the left nodes only. Is there something wrong with the way variable scopes are working?

Dijkstra's algorithm in graph (Python)

I need some help with the graph and Dijkstra's algorithm in python 3. I tested this code (look below) at one site and it says to me that the code works too long. Can anybody say me how to solve that or paste the example of code for this algorithm? I don't know how to speed up this code. I read many sites but l don't found normal examples...
P.S. Now l edit code in few places and tried to optimize it, nut it still too slow(
from collections import deque
class node:
def __init__(self, name, neighbors, distance, visited):
self.neighbors = neighbors
self.distance = distance
self.visited = visited
self.name = name
def addNeighbor(self, neighbor_name, dist): # adding new neighbor and length to him
if neighbor_name not in self.neighbors:
self.neighbors.append(neighbor_name)
self.distance.append(dist)
class graph:
def __init__(self):
self.graphStructure = {} # vocabulary with information in format: node_name, [neighbors], [length to every neighbor], visited_status
def addNode(self, index): # adding new node to graph structure
if self.graphStructure.get(index) is None:
self.graphStructure[index] = node(index, [], [], False)
def addConnection(self, node0_name, node1_name, length): # adding connection between 2 nodes
n0 = self.graphStructure.get(node0_name)
n0.addNeighbor(node1_name, length)
n1 = self.graphStructure.get(node1_name)
n1.addNeighbor(node0_name, length)
def returnGraph(self): # printing graph nodes and connections
print('')
for i in range(len(self.graphStructure)):
nodeInfo = self.graphStructure.get(i + 1)
print('name =', nodeInfo.name, ' neighborns =', nodeInfo.neighbors, ' length to neighborns =', nodeInfo.distance)
def bfs(self, index): # bfs method of searching (also used Dijkstra's algorithm)
distanceToNodes = [float('inf')] * len(self.graphStructure)
distanceToNodes[index - 1] = 0
currentNode = self.graphStructure.get(index)
queue = deque()
for i in range(len(currentNode.neighbors)):
n = currentNode.neighbors[i]
distanceToNodes[n - 1] = currentNode.distance[i]
queue.append(n)
while len(queue) > 0: # creating queue and visition all nodes
u = queue.popleft()
node_u = self.graphStructure.get(u)
node_u.visited = True
for v in range(len(node_u.neighbors)):
node_v = self.graphStructure.get(node_u.neighbors[v])
distanceToNodes[node_u.neighbors[v] - 1] = min(distanceToNodes[node_u.neighbors[v] - 1], distanceToNodes[u - 1] + node_u.distance[v]) # update minimal length to node
if not node_v.visited:
queue.append(node_u.neighbors[v])
return distanceToNodes
def readInputToGraph(graph): # reading input data and write to graph datatbase
node0, node1, length = map(int, input().split())
graph.addNode(node0)
graph.addNode(node1)
graph.addConnection(node0, node1, length)
def main():
newGraph = graph()
countOfNodes, countOfPairs = map(int, input().split())
if countOfPairs == 0:
print('0')
exit()
for _ in range(countOfPairs): # reading input data for n(countOfPairs) rows
readInputToGraph(newGraph)
# newGraph.returnGraph() # printing information
print(sum(newGraph.bfs(1))) # starting bfs from start position
main()
The input graph structure may look like this:
15 17
3 7 2
7 5 1
7 11 5
11 5 1
11 1 2
1 12 1
1 13 3
12 10 1
12 4 3
12 15 1
12 13 4
1 2 1
2 8 2
8 14 1
14 6 3
6 9 1
13 9 2
I'm only learning python so l think l could do something wrong(
The correctness of Dijkstra's algorithm relies on retrieving the node with the shortest distance from the source in each iteration. Using your code as an example, the operation u = queue.popleft() MUST return the node that has the shortest distance from the source out of all nodes that are currently in the queue.
Looking at the documentation for collections.deque, I don't think the implementation guarantees that popleft() always returns the node with the lowest key. It simply returns the left most item in what is effectively a double linked list.
The run time of Dijkstra's algorithm (once you implement it correctly) almost entirely lies on the underlying data structure used to implement queue. I would suggest that you first revisit the correctness of your implementation, and once you can confirm that it is actually correct, then start experimenting with different data structures for queue.

Directed Graph: How to find the longest path of Node A to N Considering the ff code:

I have considered using DFS for tracking the vertex for each edges present to find the longest path but the problem is it gives only the L input to it and the edge corresponding to it. I want to find the longest path traversed by these vertices as a alphabetically nondecreasing string and if the string playes indefenitely it outputs infinite. Here is my initial working code:
from collections import defaultdict
def DFS(G,v,seen=None, path=None):
if seen is None: seen = []
if path is None: path = [v]
seen.append(v)
paths =[]
for i in G[v]:
if i not in seen:
i_path = path + [i]
paths.append(tuple(i_path))
paths.extend(DFS(G, i, seen[:], i_path))
return paths
t = int(input())
i,j = 1,0
graph = defaultdict(list)
while i < t:
n, a = [int(i) for i in input().split()]
while j < a:
n1,n2,l = input().split()
graph[l].append(n1)
graph[l].append(n2)
j+= 1
print(graph)
print(DFS(graph, 'A'))
i+=1
i want to know where i did wrong or maybe i missed something essential since the given output for the sample input is:
INPUT:
1 // test case
5 7 // n, a
2 3 B
3 1 E
1 2 A
1 2 R
2 4 D
3 5 E
4 3 D
Expected : ADDER
ACTUAL: D, D

MST challenge gives "time exceeded" error [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am doing the BLINNET problem on Sphere Online Judge where I need to find the cost of a minimum spanning tree. I should follow a structure with Edge and Vertex instances. Vertices represent cities in this case.
I get a "time exceeded" error, and I feel like too many for loop iterations are at the cause, but that is the best I can do. I want to try the binary sort to see if it works with that, but that is not easy as it should be sorted using the key property in the City class.
Sample input
2
4
gdansk
2
2 1
3 3
bydgoszcz
3
1 1
3 1
4 4
torun
3
1 3
2 1
4 1
warszawa
2
2 4
3 1
3
ixowo
2
2 1
3 3
iyekowo
2
1 1
3 7
zetowo
2
1 3
2 7
Output for the Sample
3
4
My code
import sys
import heapq
class City:
def __init__(self, city_id):
self.city_id = city_id
self.key = float('inf')
self.parent = None
self.edge_list = list()
self.visited = False
#self.city_name = None
def is_not_visited(self):
if self.visited is False:
return True
return False
def add_neighbor(self, edge):
self.edge_list.append(edge)
def __lt__(self, other):
return self.key < other.key
class Edge:
def __init__(self, to_vertex, cost):
self.to_vertex = to_vertex
self.cost = cost
#
# def find_and_pop(queue):
# min = queue[0]
# index = 0
# for a in range(0, len(queue)):
# if queue[a].key < min.key:
# min = queue[a]
# index = a
# return queue.pop(index)
#
def MST(vertices_list):
queue = vertices_list
current = queue[0]
current.key = 0
#visited_list = list()
#heapq.heapify(queue)
total_weight = 0
while queue:
#current = find_and_pop(queue)
current = queue.pop(0)
for edge in current.edge_list:
if edge.to_vertex.is_not_visited():
if edge.cost < edge.to_vertex.key:
edge.to_vertex.key = edge.cost
edge.to_vertex.parent = current
total_weight = total_weight + current.key
current.visited = True
queue = sorted(queue, key=lambda x: x.city_id)
#heapq.heapify(queue)
#visited_list.append(current)
# total_weight = 0
# for x in visited_list:
# total_weight = total_weight + x.key
sys.stdout.write("{0}\n".format(total_weight))
class TestCase:
def __init__(self, vertices):
self.vertices = vertices
testcases = []
def main():
case_num = int(sys.stdin.readline())
#skip_line = sys.stdin.readline()
for n_case in range(0, case_num):
sys.stdin.readline()
vertices_list = list()
number_of_city = int(sys.stdin.readline())
#interate and make for the time of number of cities
for n_city in range(0, number_of_city):
city = City(n_city)
vertices_list.append(city)
for n_city in range(0, number_of_city):
c_name = sys.stdin.readline()
#vertices_list[n_city].city_name = c_name
num_neighbor = int(sys.stdin.readline())
for n_neigh in range(0, num_neighbor):
to_city_cost = sys.stdin.readline()
to_city_cost = to_city_cost.split(" ")
to_city = int(to_city_cost[0])
cost = int(to_city_cost[1])
edge = Edge(vertices_list[to_city-1], cost)
vertices_list[n_city].edge_list.append(edge)
testcase = TestCase(vertices_list)
testcases.append(testcase)
count = 0
for testcase in testcases:
MST(testcase.vertices)
# if count < case_num -1:
# print()
# count = count + 1
if __name__ == "__main__":
main()
The sorted call in your MST loop makes the solution inefficient. You have some commented-out code that relies on heapq, and that is indeed the way to avoid having to sort the queue each time you alter it. Anyway, I don't understand why you would sort the queue by city id. If anything, it should be sorted by key.
Although it could work with the key property as you did it, it seems more natural to me to add edges to the queue (heap) instead of vertices, so you have the edge cost as the basis for the heap property. Also, that queue should not have all the items from the start, but add them as they are selected during the algorithm. And, that corresponds more the the MST-building algorithm, which adds edge after edge, each time the one with the minimum cost.
If edges are pushed on a heap, they must be comparable. So __lt__ must be implemented on the Edge class like you did for the Vertex class.
class Edge:
# ... your code remains unchanged... Just add:
def __lt__(self, other):
return self.cost < other.cost
def MST(vertices_list):
# first edge in the queue is a virtual one with zero cost.
queue = [Edge(vertices_list[0], 0)] # heap of edges, ordered by cost
total_weight = 0
while queue:
mst_edge = heapq.heappop(queue) # pop both cost & vertex
current = mst_edge.to_vertex
if current.visited: continue
for edge in current.edge_list:
if not edge.to_vertex.visited:
heapq.heappush(queue, edge)
current.visited = True
total_weight += mst_edge.cost
sys.stdout.write("{0}\n".format(total_weight))

Create an empty Binary Tree in Python

I'm a beginner in programming, and I couldn't find the answer in other questions.
I'd like to create an empty binary tree of height h.
My code:
class node:
def __init__(self, a = None):
self.value = a
self.left = None
self.right = None
def postorder(tree,abba):
if tree != None:
postorder(tree.left,abba)
postorder(tree.right,abba)
abba.append(tree.value)
def inorder(tree,abba):
if tree != None:
inorder(tree.left,abba)
abba.append(tree.value)
inorder(tree.right,abba)
I would like to define a function
def getBinaryTree(h):
That gives me a tree with level h. So:
empty tree of level
Any ideas?
Updated
To make a binary tree with height h, you need to add 2^(h+1)-1 nodes. A tree with height 0 means, the tree contains only one node and that is the root. For example, to create a tree with height 3, you need to add 2^(3+1)-1 = 15 nodes.
If you want to create a set of nodes which can form a binary tree with your given code, you can do something like this.
import math
class node:
def __init__(self, a = None):
self.value = a
self.left = None
self.right = None
def getBinaryTree(tree_height):
tree_nodes = [None] * int((math.pow(2, tree_height+1) - 1))
for i in range(tree_height):
start_index = int(math.pow(2, i) - 1)
end_index = int(math.pow(2, i+1) - 1)
for j in range(start_index, end_index):
tree_nodes[j] = node(j)
if j == 0: continue
if j % 2 == 0: # a right child
parent_index = int((j - 2) / 2)
tree_nodes[parent_index].right = tree_nodes[j]
else: # a left child
parent_index = int((j - 1) / 2)
tree_nodes[parent_index].left = tree_nodes[j]
return tree_nodes[0] # returns the root node
def printTree(tree_node, inorder_tree_walk):
if tree_node != None:
printTree(tree_node.left, print_tree)
inorder_tree_walk.append(tree_node.value)
printTree(tree_node.right, print_tree)
root = getBinaryTree(4)
inorder_tree_walk = []
printTree(root, inorder_tree_walk)
print(inorder_tree_walk)
So, what the program does?
The program creates 2^(h+1)-1 nodes to form a tree with height h. Nodes are stored in list tree_nodes. Parent-child relationship between nodes are stored in tree_nodes as follows.
Left child of element i: (2i + 1)th element
Right child of element i: (2i + 2)th element
When a children node is created, it is set as a left/right children of its parent.

Resources