Optimize finding diameter of binary tree in Python - python

I'm wondering how I can optimally find the diameter (or longest path between any two leaf nodes) of a binary tree. I have the basic solution below, but the second solution requires passing pointers. How can I do something like this in Python?
def find_tree_diameter(node):
if node == None:
return 0
lheight = height(node.left)
rheight = height(node.right)
ldiameter = find_tree_diameter(node.left)
rdiameter = find_tree_diameter(node.right)
return max(lheight+rheight+1, ldiameter, rdiameter)
def find_tree_diameter_optimized(node, height):
lheight, rheight, ldiameter, rdiameter = 0, 0, 0, 0
if node == None:
# *height = 0;
return 0
ldiameter = diameterOpt(root.left, &lheight)
rdiameter = diameterOpt(root.right, &rheight)
# *height = max(lheight, rheight) + 1;
return max(lh + rh + 1, max(ldiameter, rdiameter));

Python supports multiple return values, so you don't need pointer arguments like in C or C++. Here's a translation of the code:
def diameter_height(node):
if node is None:
return 0, 0
ld, lh = diameter_height(node.left)
rd, rh = diameter_height(node.right)
return max(lh + rh + 1, ld, rd), 1 + max(lh, rh)
def find_tree_diameter(node):
d, _ = diameter_height(node)
return d
The function diameter_height returns the diameter and the height of the tree, and find_tree_diameter uses it to just compute the diameter (by discarding the height).
The function is O(n), no matter the shape of the tree. The original function is O(n^2) in the worst case when the tree is very unbalanced because of the repeated height calculations.

Simple Python 3 solution
def findDepth(root):
if root is None:
return 0
return 1 + max(findDepth(root.left), findDepth(root.right))
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
if root is None:
return 0
left = findDepth(root.left)
right = findDepth(root.right)
ldia = self.diameterOfBinaryTree(root.left)
rdia = self.diameterOfBinaryTree(root.right)
return max(left+right, max(ldia, rdia))

Related

lowest common ancestor (LCA) of two given nodes; getting null for one set of values; getting correct answer for another set

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.
root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8;
expected Output = 6; My output = 6
root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4;
expected Output = 2; My output = null
On using print I am getting the required value but when I return its null.
I think I am missing something very basic here.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
def bst(root,p=p.val,q=q.val):
if not root:
return
if p<root.val and q<root.val:
bst(root.left)
elif p>root.val and q>root.val:
bst(root.right)
else:
return root
return bst(root)
You may want to try the following implementation:
def lca(root, p, q):
if root is None:
return None
if(root.val > p.val and root.val > q.val):
return lca(root.left, p, q)
if(root.val < p.val and root.val < q.val):
return lca(root.right, p, q)
return root

15 puzzle astar search that goes into an infinite loop

I am trying to develop a 15 star puzzle program in Python and its supposed to sort everything in numerical order using the a star search algorithm with the 0 being at the end.
Here is my a star algorithm I've developed so far:
"""Search the nodes with the lowest f scores first.
You specify the function f(node) that you want to minimize; for example,
if f is a heuristic estimate to the goal, then we have greedy best
first search; if f is node.depth then we have breadth-first search.
There is a subtlety: the line "f = memoize(f, 'f')" means that the f
values will be cached on the nodes as they are computed. So after doing
a best first search you can examine the f values of the path returned."""
def best_first_graph_search_manhattan(root_node):
start_time = time.time()
f = manhattan(root_node)
node = root_node
frontier = []
# how do we create this association?
heapq.heappush(frontier, node)
explored = set()
z = 0
while len(frontier) > 0:
node = heapq.heappop(frontier)
print(node.state.tiles)
explored.add(node)
if (goal_test(node.state.tiles)):
#print('In if statement')
path = find_path(node)
end_time = time.time()
z = z + f
return path, len(explored), z, (end_time - start_time)
for child in get_children(node):
# calcuate total cost
f_0 = manhattan(child)
z = z + f_0
print(z)
if child not in explored and child not in frontier:
#print('Pushing frontier and child')
heapq.heappush(frontier, child)
print('end of for loop')
return None
"""
Return the heuristic value for a given state using manhattan function
"""
def manhattan(node):
# Manhattan Heuristic Function
# x1, y1 = node.state.get_location()
# x2, y2 = self.goal
zero_location = node.state.tiles.index('0')
x1 = math.floor(zero_location / 4)
y1 = zero_location % 4
x2 = 3
y2 = 3
return abs(x2 - x1) + abs(y2 - y1)
"""
astar_search() is a best-first graph searching algortithim using equation f(n) = g(n) + h(n)
h is specified as...
"""
def astar_search_manhattan(root_node):
"""A* search is best-first graph search with f(n) = g(n)+h(n).
You need to specify the h function when you call astar_search, or
else in your Problem subclass."""
return best_first_graph_search_manhattan(root_node)
Here is the rest of my program. Assume that everything is working correctly in the following:
import random
import math
import time
import psutil
import heapq
#import utils.py
import os
import sys
from collections import deque
# This class defines the state of the problem in terms of board configuration
class Board:
def __init__(self,tiles):
self.size = int(math.sqrt(len(tiles))) # defining length/width of the board
self.tiles = tiles
#This function returns the resulting state from taking particular action from current state
def execute_action(self,action):
new_tiles = self.tiles[:]
empty_index = new_tiles.index('0')
if action=='l':
if empty_index%self.size>0:
new_tiles[empty_index-1],new_tiles[empty_index] = new_tiles[empty_index],new_tiles[empty_index-1]
if action=='r':
if empty_index%self.size<(self.size-1):
new_tiles[empty_index+1],new_tiles[empty_index] = new_tiles[empty_index],new_tiles[empty_index+1]
if action=='u':
if empty_index-self.size>=0:
new_tiles[empty_index-self.size],new_tiles[empty_index] = new_tiles[empty_index],new_tiles[empty_index-self.size]
if action=='d':
if empty_index+self.size < self.size*self.size:
new_tiles[empty_index+self.size],new_tiles[empty_index] = new_tiles[empty_index],new_tiles[empty_index+self.size]
return Board(new_tiles)
# This class defines the node on the search tree, consisting of state, parent and previous action
class Node:
def __init__(self,state,parent,action):
self.state = state
self.parent = parent
self.action = action
#self.initial = initial
#Returns string representation of the state
def __repr__(self):
return str(self.state.tiles)
#Comparing current node with other node. They are equal if states are equal
def __eq__(self,other):
return self.state.tiles == other.state.tiles
def __hash__(self):
return hash(self.state)
def __lt__(self, other):
return manhattan(self) < manhattan(other)
# Utility function to randomly generate 15-puzzle
def generate_puzzle(size):
numbers = list(range(size*size))
random.shuffle(numbers)
return Node(Board(numbers),None,None)
# This function returns the list of children obtained after simulating the actions on current node
def get_children(parent_node):
children = []
actions = ['l','r','u','d'] # left,right, up , down ; actions define direction of movement of empty tile
for action in actions:
child_state = parent_node.state.execute_action(action)
child_node = Node(child_state,parent_node,action)
children.append(child_node)
return children
# This function backtracks from current node to reach initial configuration. The list of actions would constitute a solution path
def find_path(node):
path = []
while(node.parent is not None):
path.append(node.action)
node = node.parent
path.reverse()
return path
# Main function accepting input from console , running iterative_deepening_search and showing output
def main():
global nodes_expanded
global path
global start_time
global cur_time
global end_time
nodes_expanded = 0
process = psutil.Process(os.getpid())
initial_memory = process.memory_info().rss / 1024.0
initial = str(input("initial configuration: "))
initial_list = initial.split(" ")
root = Node(Board(initial_list),None,None)
print(astar_search_manhattan(root))
final_memory = process.memory_info().rss / 1024.0
print('Directions: ', path)
print('Total Time: ', (end_time-start_time), ' seconds')
print('Total Memory: ',str(final_memory-initial_memory)+" KB")
print('Total Nodes Expanded: ', nodes_expanded)
# Utility function checking if current state is goal state or not
def goal_test(cur_tiles):
return cur_tiles == ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','0']
if __name__=="__main__":main()
I've managed to narrow it down into my for loop in my best_first_graph_search_manhattan function and it appears that the infinite loop is caused if the if statement where its checking if child is not in explored and child is not in frontier. I'm unsure if its the way I'm calling my child function or the way I'm pushing frontier and child into my priority queue. I have imported heapq into my program and I've done extensive research where importing that function allows you to utilize priority queue into your program. Please don't mind other variables that are not used in my a star search.
Here is a test case: 1 0 3 4 5 2 6 8 9 10 7 11 13 14 15 12 | DRDRD
Thank you all very much for your help!

What is the minimum number of case (or if/else) statements required to calculate all unknown values corresponding to properties of an object?

Consider a right-angle triangle, which has the properties
Hypotenuse (side)
Adjacent (side)
Opposite (side)
Area
Given any 2 of these properties, it is always possible to calculate the value of the other 2. My question relates to what the most efficient/elegant way of doing this is.
At present, the only way of doing this that I can think of is to use (4C2)*2 = 12 case statements, each relating to a possible combination of inputsa that may be provided.
For example, using python you might have something like
class RightAngleTriangle():
def __init__(this, propertyType1, propertyValue1, propertyType2, propertyValue2):
this.adjacent = 0
this.opposite = 0
this.hypotenuse = 0
this.area = 0
if (propertyType1 == "adjacent" and propertyType2 == "opposite"):
this.adjacent = propertyValue1
this.opposite = propertyValue2
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "opposite" and propertyType2 == "adjacent"):
this.adjacent = propertyValue2
this.opposite = propertyValue1
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "adjacent" and propertyType2 == "hypotenuse"):
this.adjacent = propertyValue1
this.hypotenuse = propertyValue2
this.opposite = (propertyValue2**2 + propertyValue1**2)**0.5
this.area = (this.opposite * this.adjacent)/2
...and so on...
You could then create your triangle object, and print its four properties, using code (in this case python) like the below.
t1 = RightAngleTriangle("adjacent", 10, "opposite", 12)
print(t1.adjacent)
print(t1.opposite)
print(t1.hypotenuse)
print(t1.area)
This is hideous. Is there a more eligant solution to this problem?
Yes, at least two - one using args and one using key word args. So:
class RightAngleTriangle():
def __init__(self, *args):
self.adjacent = 0
self.opposite = 0
self.hypotenuse = 0
self.area = 0
for property_type, property_value in zip(args[::2], args[1::2]):
setattr(self, property_type, property_value)
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
This would work with your current input, but let's agree - it's still not very elegant solution. So, let's use kwargs:
class RightAngleTriangle():
def __init__(self, adjacent=0, opposite=0, hypotenuse=0):
self.adjacent = adjacent
self.opposite = opposite
self.hypotenuse = hypotenuse
self.area = 0
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
And now you can simply call this code as:
t1 = RightAngleTriangle(adjacent=10, opposite=12)

How to give start,end coordinates

I'm new to qgis and in here I want to find a path between two selected points on the map(Roads-vector layer). The points are selected by the user, using mouse clicks.
So here I used the astar algorithm to find path between two points.
*******************************astar.py**********************************
import heapq
class AStar(object):
def __init__(self, graphAstar):
self.graphAstar = graphAstar
def heuristic(self, node, start, end):
raise NotImplementedError
def search(self, start, end):
openset = set()
closedset = set()
current = start
openHeap = []
openset.add(current)
openHeap.append((0,current))
while openset:
temp = heapq.heappop(openHeap)
current = temp[1]
if current == end:
path = []
while current.parent:
path.append(current)
current = current.parent
path.append(current)
return path[::-1]
openset.remove(current)
closedset.add(current)
for node in self.graphAstar[current]:
if node in closedset:
continue
if node in openset:
new_g = current.gg + current.move_cost(node)
if node.gg > new_g:
node.gg = new_g
node.parent = current
else:
node.gg = current.gg + current.move_cost(node)
node.H = self.heuristic(node, start, end)
node.parent = current
openset.add(node)
heapq.heappush(openHeap, (node.H,node))
return None
class AStarNode(object):
def __init__(self):
self.gg = 0
self.H = 0
self.parent = None
def move_cost(self, other):
raise NotImplementedError
*****************************astar_grid.py*******************************
from astar import AStar, AStarNode
from math import sqrt
class AStarGrid(AStar):
def heuristic(self, node, start, end):
return sqrt((end.x - node.x)**2 + (end.y - node.y)**2)
class AStarGridNode(AStarNode):
def __init__(self, x, y):
self.x, self.y = x, y
super(AStarGridNode, self).__init__()
def move_cost(self, other):
diagonal = abs(self.x - other.x) == 1 and abs(self.y - other.y) == 1
return 14 if diagonal else 10
and in the main code, the following method is used to create graph from vector layer.
**************************plugin.py**********************************
def make_graph(self, mapinfo):
nodes = [[AStarGridNode(x, y) for y in range(mapinfo['height'])] for x in range(mapinfo['width'])]
graphAstar = {}
for x, y in product(range(mapinfo['width']), range(mapinfo['height'])):
node = nodes[x][y]
graphAstar[node] = []
for i, j in product([-1, 0, 1], [-1, 0, 1]):
if not (0 <= x + i < mapinfo['width']): continue
if not (0 <= y + j < mapinfo['height']): continue
graphAstar[nodes[x][y]].append(nodes[x+i][y+j])
return graphAstar, nodes
And I called that method in FindRoutes method..
def findRoutes(self):
vl=self.canvas.currentLayer()
director = QgsLineVectorLayerDirector( vl, -1, '', '', '', 3 )
properter = QgsDistanceArcProperter()
director.addProperter( properter )
crs = self.canvas.mapRenderer().destinationCrs()
builder = QgsGraphBuilder( crs )
global x1
global y1
global x2
global y2
pStart = QgsPoint( x1, y1 )
pStop = QgsPoint( x2, y2 )
graphAstar, nodes = self.make_graph({ "width": 8, "height": 8 })
paths = AStarGrid(graphAstar)
start, end = ??
path = paths.search(start, end)
My question is, how to pass the start and end coordinates to the function above? Because passing them just as coordinates (start, end = pStart, pStop) does not work.
How do add them to the graph created as nodes?
Or is there any easy way to do it?
Please help me to to find a solution to this problem.
Thank You
When i do an astar, the node i use are intern of the astar and contain a reference vers the original point object (your tuple of position).
Maybe it's the same with your AStarGridNode ?
In your case :
start = AStarGridNode(x1, y1)
stop = AStarGridNode(x2, y2)
This part could be in the your search function to hide this from the user.

Displaying a tree in ASCII

As a time-pass activity, I decided to implement a Tree(like) structure in python.
I implemented a Node class (which alone serves the purpose here) like so:
class Node:
def __init__(self, name, parent, *data):
self.name = name
self.parent = parent
self.data = data
self.children = []
self.is_root = False
def __repr__(self):
return 'Node '+repr(self.name)
def dic(self):
retval = {self:[]}
for i in self.children:
retval[self].append(i.dic())
return retval
def display(self): # Here
pass
def has_children(self):
return bool(self.children)
def get_parent(self):
return self.parent
def add_child(self, name, *data):
child = Node(name, self,*data)
self.children.append(child)
return child
As you can see the display function is not implemented.
Here's an example tree.
A = Node('A',Node)
A.is_root = True
B = A.add_child('B')
D = B.add_child('D')
C = A.add_child('C')
E = C.add_child('E')
F = C.add_child('F')
G = C.add_child('G')
Here's some sample output for display.
>>> A.display()
A
+-^-+
B C
| +-+-+
D E F G
>>> C.display()
C
+-+-+
E F G
In the shortest form,
How can I "build" an ASCII tree (like above) from the Node class??
In a longer form,
The "Logic" of printing is:
When there is only one child, | is put above the child. (D)
Else, Every child has a + above it, (B,C,E,F)
When there are even no. of children, ^ is put below the parent. (A)
Else, (there are odd no. of children) + is put below the parent. (C)
I have been thinking of starting from below.
I realized that there has to be a call to the each of the children, but have been unable to implement anything (of that sorts or otherwise) that gave anything close to it.
Here's a solution that covers most of what you're looking for.
Like any tree algorithm, recurse down the children of the tree, and combine results at each node. Here's the trick: display() returns a rectangle of text, for example:
aaaaaa
aaaaaa
aaaaaa
Most of the rectangle will be whitespace. Returning only rectangles of text makes it easy to combine results. We'll use the following two helper functions, one to measure block widths, and the other to combine blocks horizontally into larger blocks:
def block_width(block):
try:
return block.index('\n')
except ValueError:
return len(block)
def stack_str_blocks(blocks):
"""Takes a list of multiline strings, and stacks them horizontally.
For example, given 'aaa\naaa' and 'bbbb\nbbbb', it returns
'aaa bbbb\naaa bbbb'. As in:
'aaa + 'bbbb = 'aaa bbbb
aaa' bbbb' aaa bbbb'
Each block must be rectangular (all lines are the same length), but blocks
can be different sizes.
"""
builder = []
block_lens = [block_width(bl) for bl in blocks]
split_blocks = [bl.split('\n') for bl in blocks]
for line_list in itertools.izip_longest(*split_blocks, fillvalue=None):
for i, line in enumerate(line_list):
if line is None:
builder.append(' ' * block_lens[i])
else:
builder.append(line)
if i != len(line_list) - 1:
builder.append(' ') # Padding
builder.append('\n')
return ''.join(builder[:-1])
See where this is going? Children return a rectangle that displays themselves and their descendants, and each node will combine these rectangles into a larger rectangle that contains itself. The rest of the code just renders the dashes and pluses:
class Node:
def display(self): # Here
if not self.children:
return self.name
child_strs = [child.display() for child in self.children]
child_widths = [block_width(s) for s in child_strs]
# How wide is this block?
display_width = max(len(self.name),
sum(child_widths) + len(child_widths) - 1)
# Determines midpoints of child blocks
child_midpoints = []
child_end = 0
for width in child_widths:
child_midpoints.append(child_end + (width // 2))
child_end += width + 1
# Builds up the brace, using the child midpoints
brace_builder = []
for i in xrange(display_width):
if i < child_midpoints[0] or i > child_midpoints[-1]:
brace_builder.append(' ')
elif i in child_midpoints:
brace_builder.append('+')
else:
brace_builder.append('-')
brace = ''.join(brace_builder)
name_str = '{:^{}}'.format(self.name, display_width)
below = stack_str_blocks(child_strs)
return name_str + '\n' + brace + '\n' + below
# SNIP (your other methods)
And we're off to the races!
a
+-+-+---------------------------+
b e f g
+ +-+-------------------------+
c h i k
+ + +-+-+-+-------------+-------------+-+------+
d j l m p r s O P Q
+ + +-+-+-+---------+ +-----+
n q t u w x y R S
+ + +-------+-------+ +---+---+
o v z A M T U Z
+-+-+-+-+-+-+ + + +
B D E H I K L N V a
+ + + +-+-+ +
C F J W X Y b
+
G
(Requirements like "placing a ^ below the parent" are left as an exercise for the reader)
I'd like to suggest to take a look at ETE module http://ete.cgenomics.org which implements the functionality you describe here and much more.
At the same time, I'd like to provide this entry Pretty Print output in a sideways tree format in console window where a similar question has been asked before. As you can see in such discussion, the _asciiArt function from ETE provides what, I think, you are looking for.
I hope this helps,

Categories

Resources