假定给定一个四则运算字符串:8+7*2 –9/3;那么该表达式的值为19. 但是如何用C/C++程序上实现计算一个字符串表达式呢?一个常用的做法是将表达式的字符串转换为后缀表达式,即:1)表达式8+2,转换为后缀表达式是8 2+;2)表达式8 +3*4,转换为后缀表达式是834*+;
然后通过栈的操作可以求得它们的结果:
以2)例为例:
压入8,
压入3,
压入4,
遇到*,
然后弹出两个字符,即3和4,进行*的运算,得到12
压入12;
遇到+
弹出两个字符,8和12,进行+的运算,得到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。