假定给定一个四则运算字符串: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
1)、遍历substring的链表,如果是操作数直接加入list
2)、如果遇到“(”,则将其直接压入操作符栈,以后的的操作符同上处理,知道遇到“)”,则依次弹出“(”和“)”之间的内容。后面依据重复第一步。
3、计算后缀表达式
需要一个栈,即依次遍历后缀表达式的链表list
举一个例子,能更清晰的了解其中的过程:
假设字符串表达式是 -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
using namespace std;
#include
#include
#include
#include
queue substring_queue;
list suffix_list;
stack cal_stack;
void create_substring_list(char *pch)
{
if(NULL == pch)
{
cout<<"str is NULL!"< substring_list; //
while('\0' != *pch)
{
string str="";
str += *pch++;
substring_list.push_back(str);
}
list::iterator iter = substring_list.begin();
while(iter != substring_list.end())
{
substring_queue.push(*iter);
++iter;
}
}
void create_suffix_list(queue 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 lst)
{
if(lst.empty())
return ;
int num,n1,n2;
stack int_stack;
char ch;
list::iterator iter = lst.begin();
while(iter != lst.end())
{
ch = (*iter).at(0);
//cout<::iterator iter = suffix_list.begin();
while(iter != suffix_list.end())
{
cout<<*iter;
++iter;
}
cout<
以上代码有些冗杂,是因为我想多用几个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。