1. 程式人生 > >C++用字尾表示式(逆波蘭)求四則表示式值,採用STL中的stack

C++用字尾表示式(逆波蘭)求四則表示式值,採用STL中的stack

簡介:

20 世紀50 年代, 波蘭邏輯學家JanLukasiewicz ,想到了一種不需要括號的字尾表達法,我們也把它稱為逆波蘭( Reverse Polish Notation, RPN) 表示,對於"如9 + (3 -1 ) X3 +10-/2 " ,如果要用字尾表示法應該是: "9 3 1-3*+10 2 / + " ,這樣的表示式稱為字尾表示式.

中綴表示式轉字尾表示式規則: 

從左到右遍歷中綴表示式的每個數字和符號,若是數字就輸出,即成為字尾表示式的一部分; 若是符號,則判斷其與棧頂符號的優先順序,是右括號或優先順序低於棧頂符號(乘除優先加減)則棧頂元素依次出棧並輸出, 並將當前符號進棧,一直到最終輸出字尾表示式為止。

字尾表示式計算求值:

從左到右遍歷表示式的每個數字和符號,遇到是數字就進棧,遇到是符號,就將處於棧頂兩個數字出棧,進行運算,運算結果進錢, 一直到最終獲得結果。

示例程式碼:

<pre name="code" class="cpp">//============================================================================
// Name        : SiZeCal.cpp
// Author      : guo
// Version     : 0.1
// Copyright   : nupt
// Description : 四則運算表示式求值,輸入整數的四則運算表示式,計算其值。
//				PS:此版本不支援輸入中有空格!
//============================================================================

#include <iostream>
#include <stack>		//use STL
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;

const int MAXSIZE=256;


int InfixToPostfix(char *infix,char *postfix);
double Calculate(char *arr);
int main() {
	cout << "四則運算,請輸入運算式:" << endl; // prints 四則運算
	char in[MAXSIZE]={0};
	char postfix[MAXSIZE]={'\0'};
	fgets(in,MAXSIZE,stdin);
	if(InfixToPostfix(in,postfix)!=1)
	{	cout<<"InfixToPostfix wrong!!!";
		return -1;
	}
	puts(in);puts(postfix);
	cout<<Calculate(postfix);
	return 0;
}
/*
將中綴表示式轉換為字尾表示式
引數:infix 指向中綴表示式,以回車鍵即\n結尾。
postfix 指向字尾表示式臨時緩衝區,用來存放轉換後的結果。
附轉換規則:從左到右遍歷中綴表示式的每個數字和符號,若是數字則直接儲存在postfix陣列中;若是符號,則判斷其與棧頂符號的優先順序,是右括號或者優先順序不大於棧頂符號,則棧頂元素依次出棧並輸出,直到遇到左括號或者棧空時,才將剛才的那個符號入棧。
*/
int InfixToPostfix(char *infix,char *postfix)
{
  stack<char> s;
  char c,e;
  int j=0,i=0;
  c=*(infix+i); //取出中綴表示式中的第一個字元
  i++;
  while('\n'!=c) //遇到換行符,表示轉換結束
  {
       while(c>='0'&&c<='9') //先判斷一下取出的字元是否是數字,如果是數字的話,則直接存入postfix陣列
       {
          postfix[j++]=c;
          c=*(infix+i);
          i++;
          if(c<'0'||c>'9') //如果不是數字,則在後面新增空格,以便區分各個符號
            {
            postfix[j++]=' ';
            }
       }
      if(')'==c) //不是數字,則判斷是否為右括號。[括號的優先順序最高,所以,如果是右括號的話,就得先進行括號裡的各種運算]
      {
    	  e=s.top();s.pop();
          while('('!=e) //直到遇到左括號為止
          {
             postfix[j++]=e;
             postfix[j++]=' ';
             e=s.top();s.pop();
          }
      }
      else if('+'==c||'-'==c) //如果是加減號,因為他倆的優先順序最低了,所以此時先將棧裡的所有符號出棧後(除非遇到左括號),再把此符號入棧
        {
          if(!(s.size())) //如果是空棧,則直接將加減號入棧
          {
              s.push(c);
          }
          else//如果不是空棧,首先將所有優先順序大於加減的出棧,然後再把加減號入棧
          {
              do{
                  e=s.top();s.pop();
                  if('('==e)
                  {
                    s.push(e);
                  }
                  else
                  {
                    postfix[j++]=e;
                    postfix[j++]=' ';
                  }
                }while(s.size()&&'('!=e);  //將棧裡的所有符號出棧(除非遇到左括號)
              s.push(c); //最後將新來的加減號再入棧
            }
        }
      else if('*'==c||'/'==c||'('==c) //如果是乘除號或左括號,因為他們的優先順序高,所以直接入棧。
        {
          s.push(c);
        }
      else if('\n'==c) //判斷一下,所有符號是否都已轉換完成
        {
          break;
        }
      else //能走到這個else的,都是我不認識的符號了
        {
          // printf("\nError:input error,the character %d cann't recognize!\n",c);
          return -1;
        }
      c=*(infix+i); //取出下一個字元進行轉換
      i++;
    }
  while(s.size()) //轉換完成後,棧裡可能還有沒出棧的運算子號
    {
      e=s.top();s.pop();
      postfix[j++]=e;
      postfix[j++]=' ';
    }
  return true;
}

/*
計算字尾表示式的結果
引數:arr使用空格分隔的字尾表示式字串。例:arr="31 5 + "
result 儲存計算完畢後的結果
注:如何利用棧來計算字尾表示式的結果:依次取出字尾表示式中的符號進行比較,如果是數字,則直接入棧;如果是符號,則出棧兩次,彈出兩個要計算的因數,進行計算,之後再將計算結果入棧。知道字尾表示式中所有符號都已比較完畢。
*/
double Calculate(char *arr)
{
  // printf("%s\n",arr);
  double d,e,f; //d,e 存放兩個因數。f存放d,e計算後的結果.
  stack<double> s;
  char *op; //存放字尾表示式中的每個因數或運算子
  char *buf=arr; //宣告bufhe saveptr兩個變數,是strtok_r函式的需要。
  char *saveptr=NULL;
  while((op=strtok(buf," "))!=NULL) //利用strtok_r函式分隔字串
    {
      buf=NULL;
      switch(op[0])
      {
        case '+':
            d=s.top();s.pop();
            e=s.top();s.pop();
          f=d+e;
          s.push(f);
          break;
        case '-':
            d=s.top();s.pop();
            e=s.top();s.pop();
          f=e-d;
          s.push(f);
          break;
        case '*':
          d=s.top();s.pop();
          e=s.top();s.pop();
          f=d*e;
          s.push(f);
          break;
        case '/':
          d=s.top();s.pop();
          e=s.top();s.pop();
          f=e/d;
          s.push(f);
          break;
        default:
          d=atof(op); //不是運算子,就肯定是因數了。所以,用atof函式,將字串轉換為double型別
          s.push(d);
          break;
        }
    }
  double result=s.top();s.pop();
  return result;
}