1. 程式人生 > 實用技巧 >[leetcode] 基本計算器

[leetcode] 基本計算器

本文題目:

題目 772 需要 Plus 會員,可以看這裡的部落格

第三題是位元組跳動線上夏令營的原題。

版本 1 :只考慮個位數

實現需求

  • 有空格
  • 數字只考慮個位數
  • 無括號
  • 支援四則運算

實現思路

資料結構課上學過的棧演算法,將數字與操作符分離入棧,同時利用一個 map 來記錄操作符的優先順序。只有當前操作符 x 優先順序嚴格大於棧頂操作符優先順序,才能進棧,否則需要先執行棧頂操作符。

程式碼實現

#define isDigital(x) (('0' <= (x)) && ((x) <= '9'))
#define isSign(x) (((x) == '+') || ((x) == '-') || ((x) == '*') || ((x) == '/'))
class Solution
{
public:
    unordered_map<char, int> cmp = {{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
    int calculate(string s)
    {
        stack<int> numbers;
        stack<char> signs;
        for (char x : s)
        {
            if (isDigital(x))  numbers.push(x - '0');
            else if (isSign(x))
            {
                if (signs.empty() || (cmp[x] > cmp[signs.top()]))
                    signs.push(x);
                else
                {
                    while (!signs.empty() && cmp[x] <= cmp[signs.top()])
                        exec(numbers, signs);
                    signs.push(x);
                }
            }
        }
        while (!signs.empty())  exec(numbers, signs);
        assert(numbers.size() == 1);
        return numbers.top();
    }
    // exec 函式表示對棧頂操作符運算,該函式不會變
    void exec(stack<int> &nums, stack<char> &signs)
    {
        int a, b, val;
        char op;
        op = signs.top(), signs.pop();
        b = nums.top(), nums.pop();
        a = nums.top(), nums.pop();
        switch (op)
        {
            case '+': val = a + b; break;
            case '-': val = a - b; break;
            case '*': val = a * b; break;
            case '/': val = a / b; break;
            default: assert(0);
        }
        nums.push(val);
    }
};

版本 2 :考慮任意位數字

實現需求

  • 有空格
  • 數字任意多位
  • 無括號
  • 支援四則運算

實現思路

遇見數字,繼續往前掃描,直到遇到非數字,這一整個子串轉換為一個 int .

程式碼實現

這樣就能通過題目 227 啦~

int calculate(string s)
{
    stack<int> numbers;
    stack<char> signs;
    int len = s.length();
    for (int i = 0; i < len; i++)
    {
        char x = s[i];
        if (isDigital(x))
        {
            int j = i;
            while (isDigital(s[j]))  j++;
            numbers.push(stoi(s.substr(i, j - i)));
            i = j - 1;
        }
        else if (isSign(x))
        {
            if (signs.empty() || (cmp[x] > cmp[signs.top()]))  signs.push(x);
            else
            {
                while (!signs.empty() && cmp[x] <= cmp[signs.top()])
                    exec(numbers, signs);
                signs.push(x);
            }
        }
    }
    while (!signs.empty())  exec(numbers, signs);
    assert(numbers.size() == 1);
    return numbers.top();
}

版本 3 :考慮括號

實現需求

  • 有空格
  • 數字任意多位
  • 帶括號
  • 支援四則運算

這樣就是位元組筆試題目啦。

實現思路

很明顯,對於帶括號的表示式 1+2*(1+2*(3)) ,括號中是一個子表示式,同樣要進行求值處理,顯示需要採用遞迴做法。

那麼現在需要解決的就是:當遇到 '(' 時,找到該 '(' 包圍的子表示式。需要使用括號匹配演算法。

程式碼實現

可以通過 224 ,估計 772 也行(沒會員,窮)。

int calculate(string s)
{
    stack<int> numbers;
    stack<char> signs;
    int len = s.length();
    for (int i = 0; i < len; i++)
    {
        char x = s[i];
        if (isDigital(x))
        {
            int j = i;
            while (isDigital(s[j]))  j++;
            numbers.push(stoi(s.substr(i, j - i)));
            i = j - 1;
        }
        else if (isSign(x))
        {
            if (signs.empty() || (cmp[x] > cmp[signs.top()]))  signs.push(x);
            else
            {
                while (!signs.empty() && cmp[x] <= cmp[signs.top()])
                    exec(numbers, signs);
                signs.push(x);
            }
        }
        else if (x == '(')
        {
            int flag = 0;
            int j = i;
            do
            {
                if (s[j] == '(')  flag++;
                else if (s[j] == ')')  flag--;
                j++;
            } while (flag != 0);
            numbers.push(calculate(s.substr(i + 1, j - 1 - i - 1)));
            i = j - 1;
        }
    }
    while (!signs.empty())  exec(numbers, signs);
    assert(numbers.size() == 1);
    return numbers.top();
}