1. 算法思路
(1)首先穷举的可行性问题。我把表达式如下分成三类
1、 无括号的简单表达式。
2、 有一个括号的简单表达式。
3、 有两个括号的较复4,杂表达式。
穷举的开始我对给出的四个数进行排列,其可能的种数为4*3*2*1=24。我利用一个嵌套函数实现四个数的排列
(2)那么作为栈的著名应用,表达式的计算可以有两种方法。
第一种方法:
首先建立两个栈,操作数栈OVS和运算符栈OPS。其中,操作数栈用来记忆表达式中的操作数,其栈顶指针为topv,初始时为空,即topv=0;运算符栈用来记忆表达式中的运算符,其栈顶指针为topp,初始时,栈中只有一个表达式结束符,即topp=1,且OPS(1)=‘;’。此处的‘;’即表达式结束符。
然后自左至右的扫描待处理的表达式,并假设当前扫描到的符号为W,根据不同的符号W做如下不同的处理:
1、若W为操作数
2、则将W压入操作数栈OVS
3、且继续扫描下一个字符
4、若W为运算符
5、则根据运算符的性质做相应的处理:
(1)、若运算符为左括号或者运算符的优先级大于运算符栈栈顶的运算符(即OPS(top)),则将运算符W压入运算符栈OPS,并继续扫描下一个字符。
(2)、若运算符W为表达式结束符‘;’且运算符栈栈顶的运算符也为表达式结束符(即OPS(topp)=’;’),则处理过程结束,此时,操作数栈栈顶元素(即OVS(topv))即为表达式的值。
(3)、若运算符W为右括号且运算符栈栈顶的运算符为左括号(即OPS(topp)=’(‘),则将左括号从运算符栈谈出,且继续扫描下一个符号。
(4)、若运算符的右不大于运算符栈栈顶的运算符(即OPS(topp)),则从操作数栈OVS中弹出两个操作数,设先后弹出的操作数为a、b,再从运算符栈OPS中弹出一个运算符,设为+,然后作运算a+b,并将运算结果压入操作数栈OVS。本次的运算符下次将重新考虑。
第二种方法——
首先对表达式进行线性化,然后将线性表达式转换成机器指令序列以便进行求值。
那么什么是表达式的线性化呢?人们所习惯的表达式的表达方法称为中缀表示。中缀表示的特点是运算符位于运算对象的中间。但这种表示方式,有时必须借助括号才能将运算顺序表达清楚,而且处理也比较复杂。
1929年,波兰逻辑学家Lukasiewicz提出一种不用括号的逻辑符号体系,后来人们称之为波兰表示法(Polish notation)。波兰表达式的特点是运算符位于运算对象的后面,因此称为后缀表示。在对波兰表达式进行运算,严格按照自左至右的顺序进行。下面给出一些表达式及其相应的波兰表达式。
表达式波兰表达式
A-B AB-
(A-B)*C+D AB-C*D+
A*(B+C/D)-E*F ABCD/+*EF*-
(B+C)/(A-D) BC+AD-/
OK,所谓表达式的线性化是指将中缀表达的表达式转化为波兰表达式。对于每一个表达式,利用栈可以把表达式变换成波兰表达式,也可以利用栈来计算波兰表达式的值
下面是代码
//西安科技大学-计算机科学与技术学院
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
const double PRECISION = 1E-6;
const int COUNT_OF_NUMBER = 4;
const int NUMBER_TO_BE_CAL = 24;
double number[COUNT_OF_NUMBER];
string expression[COUNT_OF_NUMBER];
bool Judgement = false; //判断是否有解。
int count = 0;
void Search(int n)
{
if (n == 1)
{
if ( fabs(number[0] - NUMBER_TO_BE_CAL) <= PRECISION ) //对于除法,要小心小数的精确位数
{
cout << expression[0] << "\t\t";
Judgement = true;
count ++;
if((count % 3)==0)
cout<<endl;
}
else
{ }
}
for(int i=0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
double a, b;
string expa, expb;
a = number[i];
b = number[j];
number[j] = number[n - 1]; //递归之后,n比以前小一位,所以可以不停向前赋值
expa = expression[i];
expb = expression[j];
expression[j] = expression[n - 1]; //递归之后,n比以前小一位,所以可以不停向前赋值
expression[i] = '(' + expa + '+' + expb + ')'; //加法不需要分顺序
number[i] = a + b;
Search(n-1);
expression[i] = '(' + expa + '-' + expb + ')'; //减法应该分顺序,减数以及被减数
number[i] = a - b;
Search(n-1);
expression[i] = '(' + expb + '-' + expa + ')'; //减法应该分顺序,减数以及被减数
number[i] = b - a;
Search(n-1);
expression[i] = '(' + expa + '*' + expb + ')'; //乘法不需要分顺序
number[i] = a * b;
Search(n-1);
if (b != 0)
{
expression[i] = '(' + expa + '/' + expb + ')'; //除法应该分顺序,除数以及被除数
number[i] = a / b;
Search(n-1);
}
if (a != 0)
{
expression[i] = '(' + expb + '/' + expa + ')'; //除法应该分顺序,除数以及被除数
number[i] = b / a;
Search(n-1);
}
number[i] = a; //这4句语句是为了防止如果上面几种可能都失败了的话,
number[j] = b; //就把原来的赋值撤消回去,以无干扰的正确的进入到下一次
expression[i] = expa; //for循环队列中。
expression[j] = expb; //
}
}
}
int main()
{
cout<<"请依次输入4个数字:\n";
for (int i = 0; i < COUNT_OF_NUMBER; i++)
{
char buffer[20];
cout<<"第"<<i+1<<"个卡片:";
cin >> number[i];
itoa(number[i], buffer, 10); //itoa()函数的作用是把第一个参数(数值)传送到第二个参数(字符串)中去,第三个
//参数(int型)是该数值在字符串里以什么进制存放。
expression[i] = buffer;
}
cout<<endl;
Search(COUNT_OF_NUMBER) ;
if(Judgement==true)
{
cout << "\n成功" << endl;
cout<<"所以可以计算的次数总和 = "<<count<<endl;
}
else
{
cout << "失败" << endl;
}
system("pause");
return 0;
}