前言:
本来把这个表达式求值只是我们一次数据结构的简单实验但是把,刚开在脑子里构思的时候好好的
但是把每当自己亲手动手实验时你就会发现原来我有这么多的细节没有想到。所以说嘛,不能只停
留在思考和看懂的层面,你不动手实践的话你永远不知道你到底掌握到生么程度了。
实验目的:
1:加深对线性表的熟练度
2:掌握栈和对列的基本操作
实验原理:
1:四则运算规则:
先乘除 后 加减,有括号先算括号里面的。
2:运算符之间的优先关系
这个运算符之间的优先级关系,最开始我是觉得这个图没有什么好讲的但是由于我在上课时看到我们班的同学有很多看不懂这个图问老师老师他也不知道怎么解释,于是我就在这里解释一下吧!
我看了很多的博客他们都没有讲怎么看这个优先级关系图这里面把
我就拿我们本次的实验来说 上面不是有θ1和θ2吗看的方式是从左向右看 其中就拿第一个大于号
来说(就是第一行第一列中+所对应的)可以看作是θ1加号的优先级>θ2加号的优先级 这个解释过
后就拿我们的实验来说 因为我们本实验是需要创建两个栈的一个是符栈(用来存取字符的)一个
是数栈(用来存取数据和计算结果)
那么第一种理解方式:
我们就可以把θ1当作是符栈中的字符而。
把θ2当作是我们输入的表达式中的字符。
即一个是栈中的字符另一个是未入栈的字符。
第二种理解方式:
我们可以找到两个相连的字符。
把前面的那个当作θ1 后面的θ2来使用 进行比较。
我这我们本实验重难点的理解。
但是把 我这次写的代码没有使用这个图但是原理是相通的!
我相信你能看懂这个图之后也一定能看懂我的代码!
编写代码思路:
我们这个实验由于是和栈相关的实验,所以嘛!我们肯定会用到栈的相关操作,我们就这分析就会发现本实验我们需要两个栈 符栈 和 数栈 那么我们首先就需要
1:创建两个结构体
结构体中一个用来存储初始化符栈另一个用来存储初始化(这里说的不太严谨哈!)数栈
经过分析我们还需要有栈的入栈,出栈,取栈值,初始化。
2:创建两个栈的初始化方法
两个初始化 一个用于初始化 符栈 另一个用于初始化 数栈
3:创建两个入栈方法
两个入栈方法 一个用于 符栈的入栈 另一个用于 数栈入栈
4:创建两个出栈方法
两个出栈方法 一个用于 符栈出栈 另一个用于 数栈出栈
5:创建两个取值方法
两个取值方法 一个用于 符栈取值 另一个用于 数栈求值
注:(出栈和取值的区别
出栈:取出原来的栈顶元素的值,并删除栈顶元素,由洗一个元素顶替
取值:只获取栈顶元素的值,而不删除栈顶元素的值)
6:创建一个判断字符优先级方法
7:创建运算具体实验的方法(即+,-,*,/)
8:创建一个总的操作方法用于实现表达式求值
9:创建主函数
实验过程:
下面这张上图是借用我们上课用的ppt用来理解的 步骤和我的有那么一点点的不同但原理是一样的
下面这几个步骤图是借用网上的用来理解的 步骤和我的有那么一点点的不同但原理是一样的
步骤一:
步骤二:
步骤三:
步骤四:
步骤五:
步骤六:
步骤七:
步骤八:
步骤九:
步骤十:
实验的算法步骤:
1:首先输入被求值的表达式,调用操作方法 既是上面的第八步
2:首先创建两个符型和数型的结构体对象 初始化两个结构体对象
3:使用for循环调用相关的方法把我们所输入的表达式进行入栈操作
a:我们使用for循环一个一个字符的进行判断 首先判断我们输入的字符是否是数字
若是数字直接入栈
b:若是字符的话我们会进行判断
当符栈为空 我们的字符会直接入栈
或者是满足栈中字符的优先级小于表达式字符中的优先级进行入栈
或者是遇到“(”这个字符我们也会直接入栈
c:当遇到栈中字符的优先级大于表达式字符中的优先级时我们需要进行的是
表达式的相关操作 然后把操作结果存入数栈中
此时我们不能忘记 现在我们这里进行判断的是表达式中的字符
我们还需要进行表达式中的字符进行入栈操作(必须是在在栈顶字符出站后)
d:最后判断符栈中是否还存有字符这里的话(造成有这一步的原因是 我们的在进行入栈时
遇见"("是直接入栈的 还有一个原因就是表达式中没有一个是与我们栈中最后一个字符进
行
比价的(这里就是有一些难理解,自己再好好想想理解理解))
你可以结合我的代码再看一下,我在其中个各个步骤都加入了详细的解释,加上我的代码的话因该是能更好的理解的
代码展示:
#include<iostream>
#include<stdlib.h>
#define max 100
using namespace std;
typedef struct stu1 //创建存储运算符的结构体
{
char data[max];
int top;
}stu1;
typedef struct stu2 //创建存储数字的结构体
{
int data[max];
int top;
}stu2;
//这里面是运算符的方法
stu1 *Init_stu1() //初始化运算符结构体
{
stu1 *s;
s=new stu1;
if(!s)
{
exit(1);
}
s->top=-1;
return s;
}
int Empty_stu1(stu1 *L) //判断运算符栈是否为空
{
if(L->top==-1)
{
return 0;
}
else
{
return 1;
}
}
int Push_stu1(stu1 *L,char a) //运算符 栈的入栈操作
{
if(L->top==max-1)
{
return 0;
}
L->data[++L->top]=a;
return 1;
}
char Pop_stu1(stu1 *L,char a) //运算符 出栈操作
{
if(L->top==-1)
{
return 0;
}
else
{
a=L->data[L->top--];
}
return a;
}
char GetTop_stu1(stu1 *L,char a) //运算符 取值操作
{
if(L->top==-1)
{
return 0;
}
else
{
a=L->data[L->top];
}
return a;
}
//这里是 操作数的方法
stu2 *Init_stu2() //操作数的初始化操作
{
stu2 *s;
s=new stu2;
s->top=-1;
return s;
}
int Empty_stu2(stu2 *L) //操作数的判空操作
{
if(L->top==-1)
{
return 0;
}
else
{
return 1;
}
}
int Push(stu2 *L,int a) //操作数的入栈操作
{
if(L->top==(max-1))
{
return 0;
}
else
{
L->data[++L->top]=a;
}
return 1;
}
int Pop_stu2(stu2 *L,int a) //操作数 出栈操作
{
if(L->top==-1)
{
return 0;
}
else
{
a=L->data[L->top--];
}
return a;
}
int GetTop_stu2(stu2 *L,int a) //操作数 取值操作
{
if(L->top==-1)
{
return 0;
}
else
{
a=L->data[L->top];
}
return a;
}
int Judge_optr(char a) //判断字符串ch是否属于运算符
{
int x; //我们在这里定义这个函数的主要想法是 是实现比较运算符的优先级
switch(a) //和其他人写的 使用二位数组的原理是相同的
{
case '(':
case ')':
x=0;
break;
case '+':
case '-':
x=1;
break;
case '*':
case '/':
x=2;
break;
}
return x;
}
int Operate(int x,int y,char ch) //计算当前的值并将该值返回
{
int a=0;
switch(ch)
{
case '+':
a=x+y;
break;
case '-':
a=x-y;
break;
case '*':
a=x*y;
break;
case '/':
if(y==0)
{
printf("分母为零!!!");
return 0;
}
else
{
a=x/y;
}
break;
default:
printf("输入的数据错误\n");
}
return a;
}
void Jsbds(char str[])
{
stu1 *optr=Init_stu1(); //初始化 符栈和数栈
stu2 *opnd=Init_stu2();
for(int i=0;str[i];i++)
{
char d[100]; //用来暂时存储数字型字符的
int j=0;
int a;
if(str[i]>'0' && str[i]<'9') //我们这里的if判断主要作用是判断输入的是否是1到9的字符
{
/*执行这个do-while操作主要是把我们输入的数字型 字符全部存贮在 另一个字符数组中
就比如 我们开始输入的表达式是 12311+53435 当开始我们读取时 我们由于使用的是
for()循环数组字符 我们会一个一个的读取字符 刚开始读取的是1这个字符 进入do循环
然后在循环里面接着依次读取 2,3,1,1这几个字符把他们都放入到d[]字符数组中然后直到我们都+为止 */
do
{
d[j]=str[i];
i++;
j++;
}while(str[i]>='0' && str[i]<='9');
d[j]='\0'; //这里面我们加 \0的主要原因是 因为我们的\0是字符数组结束的标志
i--; //这里进行i--的原因是因为此时我们已经读到非数字位的字符
//若是不进行--for循环会自身进行--从而是我们跳过该字符入栈
//以上的do while 操作是使出现 十位和白位
a=atoi(d); //将字符型转化为数字型
Push(opnd,a); //进行入栈操作
}
else
{
int b=0;
char c=0;
if(!Empty_stu1(optr) || Judge_optr(GetTop_stu1(optr,c))<Judge_optr(str[i]) || str[i]=='(')
{
Push_stu1(optr,str[i]);
}
else
{
if(str[i]==')') //查看 这个语句是否是 括号语句
{
int num_b=1;
while(num_b)
{
if(GetTop_stu1(optr,c)=='(') //查看括号是否配对
{
Pop_stu1(optr,c);
num_b=0;
}
else //若两个括号不配对 进行括号内的运算
{
int x=0; //下面定义的变量都是用来存储数据的
int y=0;
int sum=0;
char s=0;
y=Pop_stu2(opnd,b); //获取运算的第一位数
x=Pop_stu2(opnd,b); //获取运算的第二位数
s=Pop_stu1(optr,c); //获取相应的操作符
sum=Operate(x,y,s); //获取计算结果
Push(opnd,sum); //将计算结果 入数栈
}
}
}
else
{
int x; //下面定义的变量都是用来存储数据的
int y;
int sum;
char s;
y=Pop_stu2(opnd,b); //获取运算的第一位数
x=Pop_stu2(opnd,b); //获取运算的第二位数
s=Pop_stu1(optr,c); //获取相应的操作符
sum=Operate(x,y,s); //获取计算结果
Push(opnd,sum); //将计算结果 入数栈
Push_stu1(optr,str[i]); //我们这里为什么有要加一个入符栈的 函数的原因
/*举个例子来说 如果 我们输入的是 3*2+3 由于*号的优先级大于+号的优先级 我们不会执行我们最开始的if入栈操作
而是执行我们else的两个数相加操作 而此时我们执行完 else操作后i+1 系统会接着读取下一个字符3 从而忽视了我们的+入栈*/
}
}
}
}
while(Empty_stu1(optr))
{
int b=0;
char c=0;
int x; //下面定义的变量都是用来存储数据的
int y;
int sum;
char s;
x=Pop_stu2(opnd,b); //获取运算的第二位数
cout<<x<<" ";
s=Pop_stu1(optr,c); //获取相应的操作符
cout<<s<<" ";
y=Pop_stu2(opnd,b); //获取运算的第一位数
cout<<y<<" =";
if(s=='-' || s=='/')
{
sum=Operate(y,x,s);//获取计算结果
}
else
{
sum=Operate(x,y,s); //获取计算结果
}
cout<<sum<<endl;
Push(opnd,sum); //将计算结果 入数栈
}
int num_a=0;
cout << "该表达式的计算结果为:";
std::cout << GetTop_stu2(opnd,num_a)<< endl;//将操作数栈中的元素(即表达式的最终结果)打印出来
}
int main()
{
char str[100];
std::cout << "请输入算术表达式(功能:+,-,*,/)" << endl;
cin >> str;
Jsbds(str);
return 0;
}
效果展示:
哈哈总的来说我觉的我的代码写的还行 就是有些小瑕疵 没有加什么限制条件。如果你在你的电脑上运行有误的话可能是编译器原因我用的是devc++6.5版本。
最后谢谢大家能够认真的看完。