字符串四則運算求值

假定給定一個四則運算字符串:8+7*2 –9/3;那麼該表達式的值爲19. 但是如何用C/C++程序上實現計算一個字符串表達式呢?一個常用的做法是將表達式的字符串轉換爲後綴表達式,即:1)表達式8+2,轉換爲後綴表達式是8 2+;2)表達式8 +3*4,轉換爲後綴表達式是834*+;

然後通過棧的操作可以求得它們的結果:

2)例爲例:

壓入8                    

壓入3

壓入4

遇到*

然後彈出兩個字符,即34,進行*的運算,得到12

壓入12

遇到+

彈出兩個字符,812,進行+的運算,得到20

此時到達後綴表達式的末尾,因此最終的結果是20.

 

 

上面考慮的是簡單的情況,比較複雜的情況是:
1、表達式中包含浮點型的數據以及負數

2、表達式中包含“(”和“)”的括號操作

不過以上兩種情況都有可以解決的方法:

對於情況1,可以先對字符串表達式進行處理,即定義一個string類的鏈表,將每個可以作爲合法的子串存儲到鏈表上,即比如:-8 + 3.77*2.5 + -3;那麼將它轉換爲的鏈表就是:

-8

+

3.77

*

2.5

+

-3

然後再將其轉換爲後綴表達式就可以了。

對於情況2 主需要在轉換爲後綴表達式時,注意下括號的影響和去除就可以了。

 

那麼就總結下字符串表達式求值的方法:

1、  分析字符串中是否存在負數和浮點數,然後將他們依次存入一個叫做substring的鏈表中,如上面所示;

2、  然後再利用一個list<stirng>存儲將要生成的後綴表達式,利用一個stack<string >存儲操作符;具體方法如下

1)、遍歷substring的鏈表,如果是操作數直接加入list<string>;如果是操作符,則比較其和前一個操作符的優先級,如果後來的操作符的優先級高於或等於前者,則直接壓入棧,如果後來的操作如的優先級低於前者,則彈出前者,並將前者加入list<string>中去。

2)、如果遇到“(”,則將其直接壓入操作符棧,以後的的操作符同上處理,知道遇到“)”,則依次彈出“(”和“)”之間的內容。後面依據重複第一步。

3、計算後綴表達式

需要一個棧,即依次遍歷後綴表達式的鏈表list<string>;如果遇到操作數,則直接壓棧;如果遇到操作符,則彈出兩個操作數,並進行相應的計算;然後將計算結果壓入棧;繼續遍歷,直到到達鏈表的末尾。

舉一個例子,能更清晰的瞭解其中的過程:

假設字符串表達式是 -7*(8+7)-6*2 + -8;那麼它的substring鏈表是

-7

*

8

+

7

-

6

*

2

+

-8

那麼對其做轉換爲後綴表達式的處理:(紅色部分代表後綴表達式的鏈表,藍色代表操作符棧)

第一步:將“-7”放入後綴表達式鏈表,*”壓入棧;

第二步:遇到“(”,將其壓入棧,並將“8”放入後綴表達式鏈表;

第三步:遇到“+”,將其壓入棧,並將“7放入後綴表達式鏈表;

第四步:遇到“)”,將棧中的操作符依次放入後綴表達式鏈表;此時的後綴表達式是:-787+

第五步:遇到“-”,由於“-”的優先級小於之前的“*”,因此“*”退棧並放入後綴表達式鏈表;然後將“-”壓入棧;將“6放入後綴表達式鏈表;此時的後綴表達式是:-787+*6

第六步:遇到“*”,由於“*”的優先級大於“-”,將其壓入棧,並將“2放入後綴表達式鏈表;

第七步:遇到“+”,由於“+”的優先級小於“*”,因此類似第五步得到的後綴表達式是:

-787+*62*-8+-

 

下面編寫C++代碼實現字符串的四則運算,在這裏假設字符串裏出現的都是正整數,即沒有負數和浮點數,因此忽略的了第一步的有關substring鏈表的計算。

#include <iostream>
using namespace std;
#include <stack>
#include <list>
#include <cstring>
#include <queue>
queue<string> substring_queue;
list<string> suffix_list;
stack<string> cal_stack;

void create_substring_list(char *pch)
{
	if(NULL == pch)
	{
		cout<<"str is NULL!"<<endl;
		return;
	}
	list<string> substring_list; //
	while('\0' != *pch)
	{
		string str="";
		str += *pch++;
		substring_list.push_back(str);
	}
    list<string>::iterator iter = substring_list.begin();
	while(iter != substring_list.end())
	{
		substring_queue.push(*iter);
		++iter;
	}
}

void create_suffix_list(queue<string> que)
{
    if(que.empty())
        return;
    string str_temp;
    while(!que.empty())
    {
        str_temp = que.front();
        if(str_temp == "(")
            {
                cal_stack.push(str_temp);
                }
        else if(str_temp == ")")
            {
                while(!cal_stack.empty()&&cal_stack.top()!="(")
                {
                    suffix_list.push_back(cal_stack.top());
                    cal_stack.pop();
                    }
                if(!cal_stack.empty())
                    cal_stack.pop();
                }

        else if(str_temp == "+" ||str_temp == "-")
        {
            if(cal_stack.empty() || cal_stack.top() == "(")
                cal_stack.push(str_temp);
            else
            {
                while(!cal_stack.empty())
                {
                    suffix_list.push_back(cal_stack.top());
                    cal_stack.pop();
                }
                cal_stack.push(str_temp);
                }
        }
        else if(str_temp == "*" || str_temp == "/")
        {
            cal_stack.push(str_temp);
            }
        else
        {
            suffix_list.push_back(str_temp);
            }
        que.pop();
    }
    while(!cal_stack.empty())
    {
        suffix_list.push_back(cal_stack.top());
        cal_stack.pop();
    }
    }
void calculate(list<string> lst)
{
    if(lst.empty())
        return ;
    int num,n1,n2;
    stack<int> int_stack;
    char ch;
    list<string>::iterator iter = lst.begin();
    while(iter != lst.end())
    {
        ch = (*iter).at(0);
        //cout<<ch<<endl;
        switch(ch)
        {
            case '+':
            n1 = int_stack.top();
            int_stack.pop();
            n2 = int_stack.top();
            int_stack.pop();
            num = n1 +n2;
            int_stack.push(num);
            cout<<"+ num  "<<num<<endl;
            break;
            case '-':
            n1 = int_stack.top();
            int_stack.pop();
            n2 = int_stack.top();
            int_stack.pop();
            num = n2-n1;
            int_stack.push(num);
            cout<<"+ num  "<<num<<endl;
            break;
            case '*':
            n1 = int_stack.top();
            int_stack.pop();
            n2 = int_stack.top();
            int_stack.pop();
            num = n1*n2;
            int_stack.push(num);
            cout<<"+ num  "<<num<<endl;
            break;
            case '/':
            n1 = int_stack.top();
            int_stack.pop();
            n2 = int_stack.top();
            int_stack.pop();
            num = n2/n1;
            int_stack.push(num);
            cout<<"+ num  "<<num<<endl;
            break;
            default:
            num = ch-'0';
            cout<<num<<"  shuzi"<<endl;
            int_stack.push(num);
            break;
        }
        ++iter;
        }
    cout<<int_stack.top()<<endl;
    }
int main()
{
	char *pch = "8+7*2-(9+1)";
	create_substring_list(pch);
    /*while(!substring_queue.empty())
    {
        string str = substring_queue.front();
        cout<<str.at(0);
        substring_queue.pop();
        }
	cout<<endl;*/
	create_suffix_list(substring_queue);
    list<string>::iterator iter = suffix_list.begin();
	while(iter != suffix_list.end())
	{
		cout<<*iter;
		++iter;
	}
	cout<<endl;
    calculate(suffix_list);
	return 0;
}

 

以上代碼有些冗雜,是因爲我想多用幾個STL中的數據類型,其實裏面很多的都是不必要的操作,比如create_substring_list()函數中:首先將字符串的內容,賦給一個鏈表,然後再把鏈表的內容賦給一個隊列;其實這裏面的鏈表是多餘的,完全可以直接把字符串的內容賦給隊列。還有就是代碼裏面的stack,list和queue都是用string類型實例化的,其實在這裏只考慮了簡單的0~9的數字的四則運算(不包括浮點和負數),因此完全可以用char類型實例化它們的。這裏用string類型只不過是爲了以後能處理浮點和負數等類型做準備的。而且還學習下如果把一個char字符轉換爲string類型:如:char ch = ‘a’; string str; str+= ch;就實現了把char字符轉換爲string類型了。或者也可以先把char轉換爲char[],然後將char[]賦給string。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章