Skip to content

Evaluate Reverse Polish Notation⚓︎

Link

Description⚓︎

You are given an array of strings tokens that represents an arithmetic expression in a Reverse Polish Notation.

Evaluate the expression. Return an integer that represents the value of the expression.

Note that:

  • The valid operators are '+', '-', '*', and '/'.
  • Each operand may be an integer or another expression.
  • The division between two integers always truncates toward zero.
  • There will not be any division by zero.
  • The input represents a valid arithmetic expression in a reverse polish notation.
  • The answer and all the intermediate calculations can be represented in a 32-bit integer.

Example 1:

  • Input: tokens = ["2","1","+","3","*"]
  • Output: 9
  • Explanation: ((2 + 1) * 3) = 9

Example 2:

  • Input: tokens = ["4","13","5","/","+"]
  • Output: 6
  • Explanation: (4 + (13 / 5)) = 6

Example 3:

  • Input: tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
  • Output: 22
  • Explanation:
1
2
3
4
5
6
7
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

Constraints:

  • 1 <= tokens.length <= 10^4
  • tokens[i] is either an operator: "+", "-", "*", or "/", or an integer in the range [-200, 200].

Solution⚓︎

Iterate through the entire array and:

  • Push each number that we meet to the stack;
  • If we meet operation token - pop two numbers from the stack and evaluate the operation;
  • Push the result of our operation back to the stack.

Original Solution⚓︎

class Solution {
    public:
        bool isInteger(const string& str) {
            if (str.empty()) return false;
            string::size_type sz;
            try {
                int num = stoll(str, &sz);
                return sz == str.size();
            } catch (const invalid_argument& e) {
                return false;
            } catch (const std::out_of_range& e) {
                return false;
            }
        }

        int evalRPN(vector<string>& tokens) {
            stack<int> stk;
            for (auto &c : tokens) {
                if (isInteger(c)) stk.push(stoll(c));
                else if (!stk.empty()) {
                    auto num1 = stk.top();
                    stk.pop();
                    auto num2 = stk.top();
                    stk.pop();
                    if (c == "+") stk.push(num1 + num2);
                    else if (c == "-") stk.push(num2 - num1);
                    else if (c == "*") stk.push(num1 * num2);
                    else stk.push(num2 / num1);
                }
            }
            return stk.top();
        }
};
  • Time complexity: \(O(n)\);
  • Space complexity: \(O(n)\).

Easier Way of Writing⚓︎

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> stk;
        for (auto &s : tokens) {
            if (s == "+" || s == "-" || s == "*" || s == "/") {
                auto num1 = stk.top(); stk.pop();
                auto num2 = stk.top(); stk.pop();
                if (s == "+") num2 += num1;
                else if (s == "-") num2 -= num1;
                else if (s == "*") num2 *= num1;
                else num2 /= num1;
                stk.push(num2);
            } else stk.push(stoi(s));
        }
        return stk.top();
    }
};

Note: The Polish postfix notation is similar to the post-ordering of a binary tree consisting of all the operators and operands.

Other Easy Ways of Writing⚓︎

Fancy way 1:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        unordered_map<string, function<int (int, int)> > mapping = {
            { "+", [] (int a, int b) {return a + b; } },
            { "-", [] (int a, int b) {return a - b; } },
            { "*", [] (int a, int b) {return a * b; } },
            { "/", [] (int a, int b) {return a / b; } }
        };

        stack<int> stk;
        for (auto &s : tokens) {
            if (!mapping.count(s)) stk.push(stoi(s));
            else {
                auto b = stk.top(); stk.pop();
                auto a = stk.top(); stk.pop();
                stk.push(mapping[s](a, b));
            }
        }
        return stk.top();
    }
};

Fancy way 2:

int evalRPN(vector<string>& tokens) {
    stack<int> s;
    unordered_map<string, function<int(int,int)>> mapping {
        { "+", std::plus<int>() },
        { "-", std::minus<int>() },
        { "*", std::multiplies<int>() },
        { "/", std::divides<int>() }
    };

    for (const auto& token : tokens) {
        const auto& op = mapping.find(token);
        if (op != mapping.end()) {
            assert(s.size() >= 2);
            int rhs = s.top(); s.pop();
            int lhs = s.top(); s.pop();
            s.push((*op).second(lhs, rhs));
        } else {
            s.push(stoi(token));
        }
    }

    assert(s.size() == 1);
    return s.top();
}

Fancy way 3:

class Solution {
public:
    // Function to evaluate Reverse Polish Notation
    int evalRPN(vector<string>& tokens) {
        // Lambda function to return the appropriate arithmetic operation
        // based on the operator character.
        constexpr auto eval = [](char op) -> function<int(int, int)> {
            switch (op) {
                case '+': return plus<>();      // For addition
                case '-': return minus<>();     // For subtraction
                case '*': return multiplies<>();// For multiplication
                case '/': return divides<>();   // For division
                default: return nullptr;        // For invalid operators
            }
        };

        stack<int> stk; // Stack to store integers

        // Iterate over each token in the input vector
        for (const auto &str : tokens) {
            // Check if the token is an operator
            if (str.length() == 1 && eval(str[0])) {
                const auto rhs = stk.top(); stk.pop(); // Right-hand side operand
                const auto lhs = stk.top(); stk.pop(); // Left-hand side operand

                // Apply the operation and push the result back onto the stack
                stk.push(eval(str[0])(lhs, rhs));
            } else {
                // If the token is a number, convert it to an integer and push onto the stack
                stk.push(stoi(str));
            }
        }
        // Return the top element of the stack, which is the final result
        return stk.top();
    }
};

Grammar and Concepts Used:

  • Lambda Functions: Anonymous functions used for defining the eval function inline.
  • Stack: A LIFO (Last In, First Out) data structure used for storing operands.
  • Auto Keyword: For type inference in C++.
  • Range-based For Loop: To iterate over each element in the tokens vector.
  • Function Objects: Standard library function objects for arithmetic operations.
  • Ternary Operator: Used in the lambda function for concise conditional logic.
  • Constexpr: Indicates that the value of a variable or function can be evaluated at compile time.