3.棧的應用-表達式求值


實驗3-棧的應用-表達式求值

1、實驗目的:

  • 掌握棧的定義及實現;
  • 掌握利用棧求解算術表達式的方法。

2、實驗內容:

  • 通過修改完善教材中 P78-79 的算法,利用棧來實現算術表達式求值的算法。
  • 程序運行時,輸入合法的算術表達式(中間值及最終結果要在 0~9 之間,可以包括加減乘除和括號),便可輸出相應的計算結果。

說明

  • 發的代碼有幾處變量類型錯誤,使得char變為int,導致運行結果異常,已經修改

參考代碼

#include <iostream>
using namespace std;

#define OK 1
#define ERROR 0
#define OVERFLOW -2

typedef int Status;
typedef char SElemType;

struct SNode
{
    SElemType data; // 此處類型錯誤,不是int而是char(SElemType)
    struct SNode *next;
};

typedef SNode *LinkStack;

// ------------- 鏈棧相關函數  -------------------------
Status InitStack(LinkStack &S)
{
    S = NULL;
    return OK;
}

bool StackEmpty(LinkStack &S)
{
    if (!S) // 棧不存在,返回true
        return true;
    return false;
}

Status Push(LinkStack &S, SElemType e)
{
    SNode *p;
    p = new SNode;
    p->data = e;
    p->next = S;
    S = p;
    return OK;
}

Status Pop(LinkStack &S, SElemType &e)
{
    if (StackEmpty(S))
        return ERROR;
    e = S->data;
    SNode *p = S;
    S = S->next;
    delete p;
    return OK;
}

SElemType GetTop(LinkStack &S) // 這里返回類型錯誤,不是int(Status),而是char(SElemType)
{
    if (!StackEmpty(S))
        return S->data;
}

// -------------------------- End of Stack ----------------------------------

// ------------------------ 表達式求值相關函數--------------------------
// 判斷ch是否為運算符
const char oper[7] = {'+', '-', '*', '/', '(', ')', '#'};
bool In(char ch)
{
    for (int i = 0; i < 7; i++)
    {
        if (ch == oper[i])
        {
            return true;
        }
    }
    return false;
}

// 判斷運算符優先級
char Precede(char theta1, char theta2)
{
    if ((theta1 == '(' && theta2 == ')') || (theta1 == '#' && theta2 == '#'))
    {
        return '=';
    }
    else if (theta1 == '(' || theta1 == '#' || theta2 == '(' || (theta1 == '+' || theta1 == '-') && (theta2 == '*' || theta2 == '/'))
    {
        return '<';
    }
    else
        return '>';
}

// 計算兩數運算結果
char Operate(char first, char theta, char second)
{
    switch (theta)
    {
    case '+':
        return (first - '0') + (second - '0') + 48; //為什么要減 0, 要加 48
    case '-':
        return (first - '0') - (second - '0') + 48;
    case '*':
        return (first - '0') * (second - '0') + 48;
    case '/':
        return (first - '0') / (second - '0') + 48;
    }
    return 0;
}

char EvaluateExpression()
{
    LinkStack OPTR, OPND; //算術表達式求值的算符優先算法,設OPTR和OPND分別為運算符棧和操作數棧
    char ch, theta, a, b, x, top;
    InitStack(OPND); //初始化OPND棧
    InitStack(OPTR); //初始化OPTR棧
    Push(OPTR, '#'); //將表達式起始符“#”壓入OPTR棧
    cin >> ch;
    while (ch != '#' || (GetTop(OPTR) != '#')) //表達式沒有掃描完畢或OPTR的棧頂元素不為“#”
    {
        if (!In(ch))
        {
            Push(OPND, ch);
            cin >> ch;
        } //ch不是運算符則進OPND棧
        else
            switch (Precede(GetTop(OPTR), ch)) //比較OPTR的棧頂元素和ch的優先級
            {
            case '<':
                Push(OPTR, ch);
                cin >> ch; //當前字符ch壓入OPTR棧,讀入下一字符ch
                break;
            case '>':
                Pop(OPTR, theta); //彈出OPTR棧頂的運算符
                Pop(OPND, b);
                Pop(OPND, a);                     //彈出OPND棧頂的兩個運算數
                Push(OPND, Operate(a, theta, b)); //將運算結果壓入OPND棧
                break;
            case '=': //OPTR的棧頂元素是“(”且ch是“)”
                Pop(OPTR, x);
                cin >> ch; //彈出OPTR棧頂的“(”,讀入下一字符ch
                break;
            }            //switch
    }                    //while
    return GetTop(OPND); //OPND棧頂元素即為表達式求值結果
}

int menu()
{
    int c;
    cout << "0-9以內的多項式計算" << endl;
    cout << "1.計算" << endl;
    cout << "0.退出" << endl;
    cout << "選擇:";
    cin >> c;
    return c;
}

// ------------- 程序入口 --------------------------------
int main()
{

    char res;
    while (true)
    {
        switch (menu())
        {
        case 1:
            cout << "請輸入要計算的表達式(操作數和結果都在0-9的范圍內,以#結束):" << endl;
            res = EvaluateExpression(); //算法3.22 表達式求值
            cout << "計算結果為" << res - 48 << endl
                 << endl;
            break;
        case 0:
            cout << "退出成功" << endl;
            exit(0);
        default:
            break;
        }
    }
}

OOP 版

使用了 C++17 的特性,編譯時加上參數 -std=c++17

在實驗要求的基礎是進行了擴展,添加了一點功能

  • 支持浮點數運算
  • 支持基本運算加(+)、減(-)、乘(*)、除(/)、取余(%)、乘方(^)運算
  • 支持對數運算 ln(N) logN(M)
  • 支持三角函數運算 sin(N) cos(N) tan(N)
  • 支持反三角函數運算 asin(N) acos(N) atan(N)
  • 其它運算
    • 求絕對值 abs(N)
    • 平方根 sqrt(N)
    • 向上取整 ceil(N) 向下取整 floor(N)
    • 產生 [0~1] 的隨機數 random()
  • 內置常量
    • pi=3.14159265359
    • e=2.718281828
    • ans 保存了上一次的運算結果

代碼

// #define _DEBUG  // 開啟調試
#include <iostream>
#include <optional> // C++17
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <cmath>
#include <regex>
#include <random>

using std::cin;
using std::cout;
using std::endl;
using std::map;
using std::optional;
using std::ostringstream;
using std::string;
using std::vector;

// Stack template class
// Examples:
//      Stack<int> stack;
//      auto top = stack.GetTop();
//      cout << top.value_or(-1) << endl; // top == -1 if stack is empty
//      stack.Push(100);
//      stack.Push(44);
//      stack.Pop();
//      stack.Push(97);
//      cout << stack.Length() << endl;             // 2
//      if (!stack.IsEmpty())                       // check Stack status before GetTop() manually
//           cout << stack.GetTop().value() << endl; // 97
template <typename ElemType>
class Stack
{
    struct Node
    {
        ElemType data;
        Node *next;
    };

private:
    size_t length_; // 當前棧高
    Node *head_;    // 頭結點(無數據)

public:
    Stack();
    ~Stack();
    Stack(const Stack &) = delete;
    Stack &operator=(const Stack &) = delete;

    size_t Length() const { return length_; }
    bool IsEmpty() const { return length_ == 0; }
    bool Push(const ElemType e);
    optional<ElemType> Pop();
    optional<ElemType> GetTop();
};

template <typename ElemType>
Stack<ElemType>::Stack() : length_(0)
{
    head_ = new Node();
    if (!head_)
        throw std::bad_alloc(); // 內存分配失敗
    head_->next = nullptr;
}

template <typename ElemType>
Stack<ElemType>::~Stack()
{
    Node *p = head_->next;
    while (p)
    {
        head_->next = p->next;
        // cout << "Deleted: " << p->data << endl;
        delete p;
        p = head_->next;
    }
    delete head_;
}

template <typename ElemType>
bool Stack<ElemType>::Push(const ElemType e)
{
    Node *node = new Node();
    if (!node)
        return false;
    node->data = e; // 前插法
    node->next = head_->next;
    head_->next = node;
    length_++;
    return true;
}

template <typename ElemType>
optional<ElemType> Stack<ElemType>::GetTop()
{
    if (head_->next)
        return head_->next->data;
    return std::nullopt;
}

template <typename ElemType>
optional<ElemType> Stack<ElemType>::Pop()
{
    if (IsEmpty())
        return std::nullopt;
    Node *p = head_->next;
    ElemType e = p->data; // 保存棧頂元素
    head_->next = p->next;
    delete p;
    length_--;
    return e;
}

// ------------ End of Stack ------------------------

class SimpleCalculator
{
private:
    double ans = 0.0;
    map<string, int> op_priority = {
        {"(", 10}, {"random", 7}, {"sqrt", 7}, {"sin", 7}, {"cos", 7}, {"tan", 7},
        {"acos", 7}, {"asin", 7}, {"atan", 7}, {"ln", 7}, {"log", 7}, {"abs", 7},
        {"log", 7}, {"floor", 7}, {"ceil", 7}, {"^", 3}, {"%", 2}, {"*", 2},
        {"/", 2}, {"+", 1}, {"-", 1}, {")", -1}}; // 運算符優先級

public:
    SimpleCalculator() = default;
    SimpleCalculator(const SimpleCalculator &) = delete;
    SimpleCalculator &operator=(const SimpleCalculator &) = delete;

    void Run();
    void ShowHelp() const;
    vector<string> ToSuffixExpr(string &); // 轉換為后綴表達式
    vector<string> PreProcess(string &);   // 預處理輸入的表達式字符串, 算法界限符與數值分離
    double Evaluate(string &);             // 計算后綴表達式的值
};

void SimpleCalculator::ShowHelp() const
{
    cout << "SimpleCalculator v0.1\n\n"
         << "Supported Operates: \n"
         << "\t+ - * / ^ %\n"
         << "\tsin(N)   cos(N)      tan(N)\n"
         << "\tsin(N)   acos(N)     atan(N)\n"
         << "\tln(N)    logN(M)     random()\n"
         << "\tabs(N)   ceil(N)     floor(N)    sqrt(N)\n"
         << "\nInner constant:\n"
         << "\tpi=3.14159265359\te=2.718281828"
         << endl;
}

vector<string> SimpleCalculator::PreProcess(string &expr)
{
    expr = std::regex_replace(expr, std::regex("\\s+"), "");                             // 刪除空格
    expr = std::regex_replace(expr, std::regex("\\bpi"), "3.14159265359");               // 替換 pi
    expr = std::regex_replace(expr, std::regex("\\be"), "2.718281828");                  // 替換 e
    expr = std::regex_replace(expr, std::regex("\\bans"), std::to_string(ans));          // 替換上次計算的結果
    expr = std::regex_replace(expr, std::regex("^-(\\d)"), "0-$1");                      // 負數開頭補0
    expr = std::regex_replace(expr, std::regex("(\\D)(-)(\\d)"), "$010$02$03");          // 表達式內部負數前補0
    expr = std::regex_replace(expr, std::regex("([\\-\\+\\*/%\\^\\(\\)]|log)"), " $1 "); // 數值和算符用空格分開, 因為logN(M), log 作為整體,
    std::istringstream iss(expr);
    vector<string> results((std::istream_iterator<string>(iss)), std::istream_iterator<string>()); // 使用空格 split
#ifdef _DEBUG
    cout << "DEBUG: Expression split: ";
    for (auto s : results)
        cout << "[" << s << "] ";
    cout << endl;
#endif
    return results;
}

vector<string> SimpleCalculator::ToSuffixExpr(string &expr)
{
    vector<string> expr_list = PreProcess(expr);
    vector<string> ret;
    Stack<string> op_stack;

    for (string str : expr_list)
    {
        if (isdigit(str[0]))
            ret.push_back(str);
        else if (op_stack.IsEmpty() || op_priority[str] > op_priority[op_stack.GetTop().value()])
            op_stack.Push(str);
        else if (str == ")")
        {
            while (op_stack.GetTop().value() != "(")
                ret.push_back(op_stack.Pop().value());
            op_stack.Pop(); // 丟棄棧中匹配的 (
        }
        else
        {
            while (!op_stack.IsEmpty() && op_stack.GetTop().value() != "(")
                ret.push_back(op_stack.Pop().value());
            op_stack.Push(str);
        }
    }
    while (!op_stack.IsEmpty())
        ret.push_back(op_stack.Pop().value());
#ifdef _DEBUG
    cout << "DEBUG: Suffix Expression: ";
    for (auto s : ret)
        cout << "[" << s << "] ";
    cout << endl;
#endif
    return ret;
}

double SimpleCalculator::Evaluate(string &expr)
{

    vector<string> suffix_expr = ToSuffixExpr(expr);
    Stack<double> stack;
    for (string str : suffix_expr)
    {
        if (isdigit(str[0]))                          // 字符串第一個是數字
            stack.Push(strtod(str.c_str(), nullptr)); // 轉化為數字入棧
        else if (str == "abs")
            stack.Push(fabs(stack.Pop().value()));
        else if (str == "sqrt")
            stack.Push(sqrt(stack.Pop().value()));
        else if (str == "sin")
            stack.Push(sin(stack.Pop().value()));
        else if (str == "asin")
            stack.Push(asin(stack.Pop().value()));
        else if (str == "cos")
            stack.Push(cos(stack.Pop().value()));
        else if (str == "acos")
            stack.Push(acos(stack.Pop().value()));
        else if (str == "tan")
            stack.Push(tan(stack.Pop().value()));
        else if (str == "atan")
            stack.Push(atan(stack.Pop().value()));
        else if (str == "ln")
            stack.Push(log(stack.Pop().value()));
        else if (str == "floor")
            stack.Push(floor(stack.Pop().value()));
        else if (str == "ceil")
            stack.Push(ceil(stack.Pop().value()));
        else if (str == "random") // 產生一個 [0, 1) 的隨機數
        {
            std::random_device rd; // 隨機數生成器
            std::mt19937 mt(rd());
            std::uniform_real_distribution<double> dist(0.0, 1.0);
            stack.Push(dist(mt));
        }

        else // 二元運算符
        {
            double rhs = stack.Pop().value(); // 先出棧的運算符右邊的數
            double lhs = stack.Pop().value();
            if (str == "^")
                stack.Push(pow(lhs, rhs));
            else if (str == "log")
                stack.Push(log(rhs) / log(lhs)); // 換底公式求 log_a(b)
            else if (str == "%")
                stack.Push(fmod(lhs, rhs));
            else if (str == "*")
                stack.Push(lhs * rhs);
            else if (str == "/")
                stack.Push(lhs / rhs);
            else if (str == "+")
                stack.Push(lhs + rhs);
            else if (str == "-")
                stack.Push(lhs - rhs);
        }
    }
    return stack.Pop().value(); // 棧頂就是表達式的值
}

void SimpleCalculator::Run()
{
    while (true)
    {
        string input;
        cout << ">>> ";
        getline(cin, input);
        if (input == "help")
        {
            ShowHelp();
            continue;
        }
        try
        {
            double output = Evaluate(input);
            cout << "\nans=" << output << endl
                 << endl;
            ans = output; // 保存運算結果下次使用
        }
        catch (const std::exception &e)
        {
            cout << "表達式有誤!" << endl;
        }
    }
}

// --------------------- End of Calculator -------------------------------

int main(int argc, char const *argv[])
{
    SimpleCalculator calculator;
    calculator.Run();
}

運行截圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM