数据结构:使用栈完成算术表达式求值

日常生活中,我们所遇见的数学表达式都是类似于a+b-xy这样的中缀表达式。但在计算机中,是如何翻译这个表达式并对其求值呢?这里引入一个名词波兰表示,也叫做逆波兰记号、PNP,是一种计算机中无括号的后缀表达式,是中缀表达式的替代形式,参加运算的操作数总在操作符前面。例如,x+y*(a-b)-d/e所对应的后缀表达式是xyab-*+de/。计算机运用该方法进行计算的原理在于,从左向右扫描时,当遇到操作数,则将操作数进栈;当遇到操作符时,弹出两个操作数,进行运算后,将新的结果压入栈中。循环如此,直到栈内不含操作符,弹出结果。
该算法的特点在于,操作数与操作符使用同一个栈,但是要将算术表达式先转换为后缀表达式。
受此启发,我运用两个栈,一个是存放操作数的data栈,一个是存放操作符op栈。利用事先定义好的运算符优先级,将操作符压栈或退栈。

运算符优先级如下:

  • 栈内操作符(instack)优先级:#=0;(=1;*=5;/=5;%=5;+=3;-=3
  • 栈外操作符(outstack)优先级:#=0;(=6;*=4;/=4;%=4;+=2;-=2

算法步骤:

1.操作符站初始化,将#压入op栈内,从表达式中读取字符ch。
2.若ch是操作数,则直接压入data栈内。
3.若ch是操作符,则判断instack(op)与outstack(ch)的优先级

  • 若instack(op)>=outstack(ch),弹出op栈的栈顶元素,并弹出data栈的两个元素,进行运算后,将结果压入data栈内。再比较op栈的栈顶元素与ch的优先级大小。
  • 若instack(op)<outstack(ch),将ch压入op栈内。

4 算法结束

注意:
若是用字符数组来容纳表达式,则需要注意处理多位数的情况。
办法很简单,判断当前单元是否为数字,若是,则计数变量count加一,再接着判断它的下一个单元,若下一个单元仍然是数字,count加一;若不是,则将count个数字组合成所需要的数值,代替count个数字压入栈内成为一个数字。

代码实现:

//栈内优先级定义
template <class T>
int Seqstack<T>::instack(char c)
{
 int x;
 switch (c)
 {
 case '#':x = 0; break;
 case '(':x = 1; break;
 case '*':
 case '/':
 case '%':x = 5; break;
 case '+':
 case '-':x = 3; break;
 case ')':x = 6; break;
 }
 return x;
}


//栈外优先级定义
template <class T>
int Seqstack<T>::outstack(char c)
{
 int z;
 switch (c)
 {
 case '#':z = 0; break;
 case '(':z = 6; break;
 case '*':
 case '/':
 case '%':z = 4; break;
 case '+':
 case '-':z = 2; break;
 case ')':z = 1; break;
 }
 return z;
}


//关键算法
template <class T>
void Seqstack<T>::calculate()
{
 Seqstack<char> s;
 Seqstack<double>temp;
 char ch = '#', ch1, op;
 double left, right,val, value;
 int i = 0, count = 0;
 double re = 0;
 s.Push(ch);
 char exp[100];
 cout << "请输入算术表达式(并以#号结束):";
 cin >> exp;
 while (s.IsEmpty() == false )  /*
 {
  if (isdigit(exp[i]))
  {
   temp.Push(exp[i] - '0');
      count += 1;
   if (!isdigit(exp[i + 1]))
   {
    for (int k = 0; k < count; k++)             这一块是处理多位数的数字
    {
     temp.Pop(val);
     re += val*pow(10, k);
    }
    temp.Push(re);
   }
  }                                 */
  else
  {
   re = 0;
   count = 0;
   s.Peek(ch1);
   while (instack(ch1) > outstack(exp[i]))
   {
    temp.Pop(right);
    temp.Pop(left);
    s.Pop(op);
    switch (op)
    {
    case '+':value = left + right; temp.Push(value); break;
    case '-':value = left - right; temp.Push(value); break;
    case '*':value = left * right; temp.Push(value); break;
    case '/':
     if (right == 0.0)
     {
      cout << "Divide by 0!" << endl;
      MakeEmpty();
     }
     else{ value = left / right; temp.Push(value); }
     break;
    }
    s.Peek(ch1);
   }
   if (instack(ch1) < outstack(exp[i]))
   {
    s.Push(exp[i]);
   }
   else
   {
    s.Pop(op);
   }
   
  }
  i++;
 }
 cout << temp <<s<< endl;
 temp.MakeEmpty();
 s.MakeEmpty();
}

程序结果:

在这里插入图片描述

  • 6
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值