栈的应用:解析算术表达式

原博客:http://blog.csdn.net/zhangxiangDavaid/article/details/27176751

类似于 1*2-3+4-(5-6)-2/4 的式子,我们称为算术表达式。下面我们利用栈这种数据结构来解析它,即用栈来辅助计算算术表达式。
首先我们得明确计算规则:
先左后右:从左算到右
先乘除,后加减
先括号内,后括号外
原理:
使用两个栈来存储读入的字符:数字栈和符号栈
读入数字,则压入数字栈
读入符号,则把当前符号栈顶的符号与读入的符号进行优先级比较。若当前符号栈顶符号的优先级小,则继续把符号压入符号栈;若当前读入符号的优先级小,则数字栈依次出栈两个数,加上符号栈顶的符号,构成一算术表达式,通过计算得出结果,把结果压入数字栈。
当读入界限符或符号栈弹出界限符,如#,运算结束,数字栈弹栈,即最后的表达式值。
难点:
1、优先级数组priority的理解是难点。
+ - * / ( ) #
+ 1 1 2 2 2 1 1
- 1 1 2 2 2 1 1
* 1 1 1 1 2 1 1
/ 1 1 1 1 2 1 1
( 2 2 2 2 2 3 0
) 1 1 1 1 0 1 1
#2 2 2 2 2 0 3

每一个位置可以用(i,j)表示,其中i是行,j是列。有一点要明确:符号i是先于符号j的。也就是说,在算术表达式中符号i位于符号j的左边。1:>,2:<,3:=。界限符#的优先级是最低的。举几个例子说明:
(+,+)=1,两个’+’是同级的,但第一个先于第二个,由上述的计算规则1知:>。
((,#)=0,如果左括号’(‘出现了,至少右括号’)’出现了之后,才会有界限符,否则出错。
(),()=0,我们不支持(…)(…)这种写法,两括号之间至少要有其它的操作符才行,如(…)*(…)。
(#,))=0,没遇到左括号’(‘,怎能出现右括号’)’,故出错。
2、当符号栈顶的符号优先级高于当前读入的操作符时,数字栈出栈的第一个数字作为第二个操作数,下一个出栈的数字才作为第一个操作数,这一点得明白。operand:操作数 operator:操作符
细节看代码:

#include<iostream>  
#include<stack>  
using namespace std;  

char OP[7] = {'+','-','*','/','(',')','#'};  //运算符集合  
int priority[7][7] =     //各运算符相遇时,优先级比较 1:大于,2:小于,3:等于,0:不可能,错误  
{  
    { 1, 1, 2, 2, 2, 1, 1 },  
    { 1, 1, 2, 2, 2, 1, 1 },  
    { 1, 1, 1, 1, 2, 1, 1 },  
    { 1, 1, 1, 1, 2, 1, 1 },  
    { 2, 2, 2, 2, 2, 3, 0 },  
    { 1, 1, 1, 1, 0, 1, 1 },  
    { 2, 2, 2, 2, 2, 0, 3 }  
};  
bool isOpat(char c)   //是否是OP[]中的操作符  
{  
    for (int i = 0; i < 7; i++)  
    {  
        if (c == OP[i])  
            return true;  
    }  
    return false;  
}  
int getPriority(char c1,char c2)  //比较优先级  
{  
    int i, j;  
    for (int r = 0; r < 7; r++)  
    {  
        if (c1 == OP[r])  
            i = r;  
        if (c2 == OP[r])  
            j = r;  
    }  
    return priority[i][j];  
}  
int compute(char a, char op, char b)  
{  
    switch (op)  
    {  
    case '+':  
        return (a - '0') + (b - '0');  
    case '-':  
        return (a - '0') - (b - '0');  
    case '*':  
        return (a - '0')*( b - '0');  
    case '/':  
        if (b == '0')  
        {  
            cout << "错误!" << endl;  
            exit(0);  
        }  
        return (a - '0')/(b - '0');  
    }  
}  
void evaluateExpression()   //计算  
{  
    stack<char> opan,opat;    //构建两个栈 operand:操作数,operator:操作符  
    opat.push('#');    // # 压入符号栈,作为界限符  
    cout << "输入算术表达式" << endl;  
    char op,a,b,c;  
    c=getchar();  
    while (c != '#' || opat.top() != '#')  //没有读到 '#',或者符号栈也没空,则继续读取字符  
    {  
        //对读入的字符进行判断:是操作数还是操作符?  
        if (!isOpat(c))  //是操作数则压入操作数栈  
        {  
            opan.push(c);  
            c = getchar();  
        }  
        else   //若是操作符,则需把符号栈顶的操作符与当前读入的操作符,进行优先级比较  
        {  
            switch(getPriority(opat.top(), c))  
            {  
            case 1:  
                op = opat.top(); opat.pop();  
                b = opan.top(); opan.pop();  
                a = opan.top(); opan.pop();  
                opan.push(char(compute(a,op,b)+'0'));  
                break;  
            case 2:  
                opat.push(c);  
                c = getchar();  
                break;  
            case 3:  
                opat.pop();  
                c = getchar();  
                break;  
            case 0:  
                cout << "错误!" << endl;  
                exit(0);  
            }  
        }  
    }  
    cout << "= " << opan.top()-'0' << endl;  
}  
int main()  
{  
    cout << "使用栈结构解析计算表达式"<<endl;  
    evaluateExpression();  
    system("pause");  
    return 0;  
}  

说明:可以使用栈的实现:顺序栈中的代码,但这里为了方便,减少代码量,使用#include,其实效果是一样的。以上程序,只是完成了简单的运算。说它简单是因为:只能输入0-9的数字,并且不能处理以符号开头的表达式,如-3*4,+2-5,则出错。后续补充增强版,能处理诸如 34+34,多位数的运算。

update 2014-5-28 00:28

这次更新,解决了上述问题,并添加了求余%运算符。代码如下:

#include<iostream>  
#include<stack>  
using namespace std;  
char OP[8] = {'+','-','*','/','%','(',')','#'};  //运算符集合  
int priority[8][8] =   //各运算符相遇时,优先级比较 1:大于,2:小于,3:等于,0:不可能,错误  
{  
    { 1, 1, 2, 2, 2, 2, 1, 1 },  
    { 1, 1, 2, 2, 2, 2, 1, 1 },  
    { 1, 1, 1, 1, 1, 2, 1, 1 },  
    { 1, 1, 1, 1, 1, 2, 1, 1 },  
    { 1, 1, 1, 1, 1, 2, 1, 1 },  
    { 2, 2, 2, 2, 2, 2, 3, 0 },  
    { 1, 1, 1, 1, 1, 0, 1, 1 },  
    { 2, 2, 2, 2, 2, 2, 0, 3 }  
};  
bool isOpat(char c)   //是否是OP[]中的操作符  
{  
    for (int i = 0; i < 8; i++)  
    {  
        if (c == OP[i])  
            return true;  
    }  
    return false;  
}  
int getPriority(char c1,char c2)  //比较优先级  
{  
    int i, j;  
    for (int r = 0; r < 8; r++)  
    {  
        if (c1 == OP[r])  
            i = r;  
        if (c2 == OP[r])  
            j = r;  
    }  
    return priority[i][j];  
}  
int compute(int a, char op, int b)  
{  
    switch (op)  
    {  
    case '+':  
        return a + b;  
    case '-':  
        return a - b;  
    case '*':  
        return a*b;  
    case '/':  
    case '%':  
        if (b == 0)  
        {  
            cout << "错误!" << endl;  
            exit(0);  
        }  
        if (op == '/')  
            return a / b;  
        else  
            return a%b;  

    }  
}  
void evaluateExpression()   //计算  
{  
    stack<int> opan;     //操作数栈  
    stack<char> opat;    //操作符栈  
    opat.push('#');    // # 压入符号栈,作为界限符  
    cout << "输入算术表达式" << endl;  
    char op,c,cn;    //cn:char next下一个字符  
    int a,b,data;  
    c=getchar();  
    if (isOpat(c))   //如果第一个字符就是操作符,则把0压入数字栈  
        opan.push(0);  
    while (c != '#' || opat.top() != '#')  //没有读到 '#',或者符号栈也没空,则继续读取字符  
    {  
        //对读入的字符进行判断:是操作数还是操作符?  
        if (!isOpat(c))  //是操作数则压入操作数栈  
        {  
            data = c - '0';  
            while (!isOpat(cn = getchar()))   //下一个字符任为数字  
            {  
                data = data * 10 + cn - '0';  
            }  
            opan.push(data);  
            c = cn;   //把cn中的操作符赋给c  
        }  
        else   //若是操作符,则需把符号栈顶的操作符与当前读入的操作符,进行优先级比较  
        {  
            switch(getPriority(opat.top(), c))  
            {  
            case 1:  
                op = opat.top(); opat.pop();  
                b = opan.top(); opan.pop();  
                a = opan.top(); opan.pop();  
                opan.push(compute(a,op,b));  
                break;  
            case 2:  
                opat.push(c);  
                c = getchar();  
                break;  
            case 3:  
                opat.pop();  
                c = getchar();  
                break;  
            case 0:  
                cout << "错误!" << endl;  
                exit(0);  
            }  
        }  
    }  
    cout << "= " << opan.top() << endl;  
}  
int main()  
{  
    cout << "使用栈结构解析计算表达式"<<endl;  
    evaluateExpression();  
    system("pause");  
    return 0;  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值