【八】栈的应用实例(二)

1、问题的提出

尝试用计算机读入字符串“9+(3-1)*5+8/2+(5/2)”并计算值

2、中缀表达式 与 后缀表达式

波兰科学家在20世纪50年代提出了一种将运算符放在数字后面的后缀表达式
对应的,我们习惯的数学表达式叫做中缀表达式
实例:
5 + 3 => 5 3 +
1 + 2 * 3 => 1 2 3 * +
9 + ( 3 – 1 ) * 5 => 9 3 1 – 5 * +

中缀表达式符合人类的阅读和思维习惯
后缀表达式符合计算机的“运算习惯”

3、将中缀表达式转换成后缀表达式?

实现思路:

  • 遍历中缀表达式中的数字和符号
  • 对于数字:直接输出 直接输出
  • 对于符号:
    • 左括号:进栈
    • 符号:与栈顶符号进行优先级比较
    • 栈顶符号优先级低:进栈
    • 栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈
    • 右括号:将栈顶符号弹出并输出,直到匹配左括号
  • 遍历结束:将栈中的所有符号弹出并输出

4、实现代码

/*
  中缀表达式到后缀表达式的转换
  参数:
    str:    指向中缀表达式的指针,传入参数
    retval: 指向返回结果的后缀表达式,传出参数
*/
void transform(const char *str,char *retval)
{
  LinkStack *stack = LinkStack_Create();
  int i = 0,j = 0;
  //判断返回数据的指针是否有效
  if(retval == NULL)
  {
    return;
  }
  while(str[i] != '\0')
  {
    //如果是数字,直接输出它
    if(isNumber(str[i]))
    {
      retval[j] = str[i];
      j++;
    }
    else
      if(isOperator(str[i]))
      {
        /*
          如果是运算符,则检查栈顶符号优先级是否大于等于当前符号优先级;
          如果是,则弹出栈顶运算符并输出,然后进栈当前优先级低的运算符
          如果否,即栈顶的运算符优先级低,则直接进栈
          特殊情况,栈中没有元素时,不用比较优先级,直接进栈
        */
        char *top = (char*)LinkStack_Top(stack);
        if(top != NULL)
        {
          if(priority(*top) >= priority(str[i]))
          {
            retval[j] = *(char*)LinkStack_Pop(stack);
            j++;
          }

        }
        LinkStack_Push(stack,(LinkStackNode*)(str + i));
      }
      else
        if(isLeft(str[i]))
        {
          //如果是左括号,直接进栈
          LinkStack_Push(stack,(LinkStackNode*)(str + i));
        }
        else
          if(isRight(str[i]))
          {
            /*
              如果是右括号,依次弹出栈顶元素,并输出,知道匹配左括号
              即是,知道栈顶弹出的是左括号为止
              但是,并不输出左右括号
            */
            char temp = '\0';
            while(!isLeft(temp))
            {
              if(temp != '\0')
              {
                retval[j] = temp;
                j++;
              }
              temp = *(char*)LinkStack_Pop(stack);
            }
          }
          else
          {
            printf("Invalid expression!");
            break;
          }
    i++;
  }

  /*
    如果遍历结束,并且栈中还有数据,那么依次弹出它们,并输出
    这里判断str[i] == '\0' 是因为可能中缀表达式中有为止符号,
    那么将使上面的循环提前终止,并没有检测完中缀表达式,即使这时候
    栈中有元素,那转换还是错误的!!
  */
  while((LinkStack_Size(stack) > 0) && (str[i] == '\0'))
  {
    retval[j] = *(char*)LinkStack_Pop(stack);
    j++;
  }
  LinkStack_Destroy(stack);
}

5、对后缀表达式的计算

计算机对后缀表达式的运算也是基于栈的!

实现思路:

  • 遍历后缀表达式中的数字和符号
  • 对于数字:进栈
  • 对于符号:
    • 从栈中弹出右操作数
    • 从栈中弹出左操作数
    • 根据符号进行运算
    • 将运算结果压入栈中
  • 遍历结束:栈中的唯一数字为计算结果

6、实现代码

/*
  用于计算后缀表达式的值
  参数str:指向后缀表达式的指针
*/
DataType compute(const char *str)
{
  LinkStack *stack = LinkStack_Create();
  DataType iret = 0,i = 0;
  while(str[i] != '\0')
  {
    //如果是数据,进栈
    if(isNumber(str[i]))
    {
      //直接存放DataType类型数据的值
      LinkStack_Push(stack,(LinkStackNode*)chartoDataType(str[i]));
    }
    else
      if(isOperator(str[i]))
      {
        //如果是操作符,则依次弹出栈顶,分别为该操作符的右操作数和左操作数
        DataType right = (DataType)LinkStack_Pop(stack);
        DataType left = (DataType)LinkStack_Pop(stack);
        //计算它们的值,然后将其压栈
        DataType result = express(left,right,str[i]);
        LinkStack_Push(stack,(LinkStackNode*)result);
      }
      else
      {
        printf("Invalid expression!\n");
        break;
      }
    i++;
  }

  /*
    运算结束,如果栈中只有一个数据,并且str[i] == '\0'
    那么运算是成功的;
    判断str[i] == '\0'是因为如果后缀表达式中有非运算符和数值的字符,
    循环会提前结束,导致结果是不正确的
  */
  if(LinkStack_Size(stack) == 1 && str[i] == '\0')
  {
    //获取最终的结果
    iret = (DataType)LinkStack_Pop(stack);
  }
  LinkStack_Destroy(stack);
  return iret;
}

7、完整源码下载

文件名:compute-1.0.tar.gz
链接: http://pan.baidu.com/s/1sjHTaMx 密码: twd5

文件名:compute-1.1.tar.gz
链接: http://pan.baidu.com/s/1hq1qFA0 密码: nufi
说明:修复了由linklist.c中的List_Get()函数所引起的BUG,详情见链接

编译步骤:
0.1 解压缩:tar -zxvf compute-1.0.tar.gz
0.2 进入目录:./configure
0.3 生成Seqlist:make
0.4 运行程序:./Compute

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值