Friday, October 24, 2014

Stacking arithmetic expressions

As I was reviewing linked list data structure, I came across a beautifully simple algorithm to evaluate an arithmetic expression, developed by E.W. Dijkstra. Say, you have an expression in a form like "(1+(2*3)+3)". Dijkstra's algorithm uses two stacks: one holds the values and the second one holds the operators. As you are iterating over the string, you push values and operators onto corresponding stack if you encounter an opening parenthesis. Once you encounter a closing parenthesis, you pop a value and an operator, perform an arithmetic operation and push an updated value to the value stack. This implementation requires that the expression has a complete set of opening and closing parentheses.

Instead of using Ruby's Array and corresponding pop() and push() methods, I decided to implement my own stack class as a singly linked list from scratch. By the way, the time complexity for stack operations are as follows: O(1) for push() and pop(). This makes sense since we are just accessing the top element. However, the search for an element would be slow (O(n)), since linked lists do not support indices, and one would have to traverse all the elements to get to the desired one.

class Stack

    attr_accessor :first

    def initialize
        @first = Node.new
        @len = 0
    end

    def push(item)
        oldfirst = @first
        @first = Node.new
        @first.item = item
        @first.next = oldfirst
        @len += 1
    end

    def peek
        return @first.item
    end

    def pop
        item = @first.item
        @first = @first.next
        @len -= 1
        item
    end

    def empty?
        return @len == 0
    end

    def len
        return @len
    end

    class Node
        attr_accessor :item, :next

        def initialize
            @item = nil
            @next = nil
        end
    end
end

def calculate(str)

    values = Stack.new
    operators = Stack.new

    str.split(" ").each do |i|
        if i == '('
            # just skip
        elsif i == '+'
            operators.push(i)
        elsif i == '-'
            operators.push(i)
        elsif i == '*'
            operators.push(i)
        elsif i == '/'
            operators.push(i)
        elsif i == 'sqrt'
            operators.push(i)
        elsif i == ')'
            operator = operators.pop
            value = values.pop
            if operator == '+'
                value = values.pop + value
            elsif operator == '-'
                value = values.pop - value
            elsif operator == '*'
                value = values.pop * value
            elsif operator == '/'
                value = values.pop / value
            elsif operator == 'sqrt'
                value = Math.sqrt(value)
            end
            values.push(value)
        else
            values.push(i.to_f)
        end
    end
    values.pop
end

str = "( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )"
puts calculate(str)
str2 = "( ( 1 + sqrt ( 5.0 ) ) / 2.0 )"
puts calculate(str2)

No comments :

Post a Comment