pythontreepolish-notation

Height of Polish Notation Tree (prefix) with Binary and Unary Nodes


I need to get the depth of a Polish notation tree (prefix) with binary and unary nodes. The solution here can work by simply reversing the expression string beforehand; however, if the prefix expression is incomplete, the solution is unable to infer what the minimum depth would be. How can one extend the solution?

Edit: Below I post the code to explain more clearly:

def getPNdepth(expression):
    stack = []
    expression = expression[::-1]
    for val in expression:
        if val in ['-', '+', '*', '/']:  # all binary operators
            stack.append(max(stack.pop(), stack.pop()) + 1)
        elif val in ['cos', 'exp']:  # all unary operators
            stack[-1] += 1
        else:  # an operand (x)
            stack.append(1)  
    while len(stack) > 1:
        stack.append(max(stack.pop(), stack.pop()) + 1)
        
    return stack.pop()

test_expressions = (('+', 'x', '-', 'y', 'cos', 'z'), ('+', 'x', '-', 'y', 'z'), ('+', 'x', '-', 'y', 'cos'), ('+', 'x', '-', 'y'))
for expression in test_expressions:
    try:
        print(f"Depth of {expression} = {getPNdepth(expression)}")
    except IndexError as e:   
        print(f"Error for {expression}: {e}")

Output

Depth of ('+', 'x', '-', 'y', 'cos', 'z') = 4 
Depth of ('+', 'x', '-', 'y', 'z') = 3
Error for ('+', 'x', '-', 'y', 'cos'): list index out of range
Error for ('+', 'x', '-', 'y'): pop from empty list

Solution

  • With pre-order you can take a different approach based on the observation that when the deepest node is visited, all ancestors will be on the stack, and so the maximum stack size is a direct indication of the depth of the tree.

    For incomplete trees we can also note that if the longest path ends in an operator, the minimum depth will be reached by adding the operand(s) below it as leaves, adding just one to the depth.

    Covering both principles, we can keep track of this minimal depth whenever we add an operator to the stack, always counting for one extra needed level (at least) for the operand(s). We don't put anything on the stack to represent operands, only operators.

    def getPNdepth(expression):
        stack = []
        depth = 0
        for val in expression:
            if val in ['-', '+', '*', '/']:  # all binary operators
                stack.append(2)  # = number of operands
            elif val in ['cos', 'exp']:  # all unary operators
                stack.append(1)
            else:  # an operand (x)
                while stack and stack[-1] == 1:  # remove fulfilled operators
                    stack.pop()
                if stack:  # indicate an operand is consumed
                    stack[-1] -= 1
            depth = max(depth, len(stack) + 1)
                
        return depth