- 整理数据结构学习笔记
- 基于C++
一、表达式的分类
1、中缀表达式
(1)中缀表达式就是常见的运算表达式,如(3+4)×5-6
(2)中缀表达式的计算机求值方法:
- 先在表达式前后加上起止符#(其运算优先级最低)
- 建立两个辅助栈,一个运算数栈和一个运算符栈
- 从左至右遍历表达式,如果遇到数字直接压入数字栈;如果遇到算符,若符号栈空则直接压入,否则与符号栈顶元素比较,若栈顶元素优先级低则入栈,若优先级一样则栈顶元素出栈与算符抵消(指针后移跳过当前算符),若栈顶元素优先级高,则弹出两个数字一个符号进行运算(次顶元素 op 栈顶元素),运算结果压入数字栈,再处理当前算符。最后留在数字栈中的数即为结果
- 重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
(3)运算符优先级如下表(如第4行代表*
的优先级低于(
,高于所有其他符号,#
是起止符)
(4)看一个例子,应该好理解一点,如下计算3*(7-2)
,OPTR=运算符栈,OPND=运算数栈
2、前缀表达式
(1)前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前,如(3+4)*5-6
转化为前缀表达式为- x + 3 4 5 6
(2)前缀表达式的计算机求值方法:
- 建立一个运算数辅助栈
- 从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
- 重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
(3)看一个例子,应该好理解一点,如下计算1+2*(6-4)-12/4
,转为-+1*2-6 4/12 4
3、后缀表达式
(1)后缀表达式又称逆波兰表达式运算符位于操作数之后,例如(3+4)*5-6
转化为后缀表达式为3 4 + 5 × 6 -
(2)后缀表达式的计算机求值方法:
- 建立一个运算数辅助栈
- 从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 op 栈顶元素),并将结果入栈
- 重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
(3)看一个例子,应该好理解一点,如下计算1+2*(6-4)-12/4
,转为1 2 6 4-*12 4/-+
二、表达式的互相转化
有三种方法可以实现中缀转前后缀的表达式转化
- 利用表达式树
- 利用辅助栈
- 简便方法(加括号法 )
可以参考这两篇文章:
三、中缀表达式计算代码(部分)
数据结构
使用两个栈,用动态申请数组实现,如下:
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
//+ - * / ( ) #
enum sign{add,sub,mul,divis,Lbracket,Rbracket,SE};
enum cmp{s=-1,e,b,err};
//优先级比较矩阵
cmp CM[7][7]={b,b,s,s,s,b,b,
b,b,s,s,s,b,b,
b,b,b,b,s,b,b,
b,b,b,b,s,b,b,
s,s,s,s,s,e,err,
b,b,b,b,err,b,b,
s,s,s,s,s,err,s};
//数字栈
typedef struct
{
float *base;
float *top;
int stacksize;
}NumStack;
//符号栈
typedef struct
{
char *base;
char *top;
int stacksize;
}SignStack;
主循环
//算数符号转换为枚举类型,便于查找优先级矩阵
sign char2sign(char a)
{
sign res;
switch(a)
{
case '+':res=add;break;
case '-':res=sub;break;
case '*':res=mul;break;
case '/':res=divis;break;
case '(':res=Lbracket;break;
case ')':res=Rbracket;break;
case '#':res=SE;break;
}
return res;
}
//符号a和b相比优先级情况
int Sign_Cmp(char a,char b)
{
sign A=char2sign(a);
sign B=char2sign(b);
return CM[A][B];
}
//进行一次四则运算
float cal(char sign,float a,float b)
{
switch(sign)
{
case '+':return a+b;
case '-':return a-b;
case '*':return a*b;
case '/':return a/b;
}
}
int main()
{
char equation[100];
NumStack NS;
SignStack SS;
NumStack_Init(NS);
SignStack_Init(SS);
char *p=&equation[0];
char sign;
float num=0;
int init=1;
int flag=1;//辅助从字符串转换小数
int temp=10;//辅助从字符串转换小数
while(1)
{
//指针重定位
p=&equation[0];
//算式输入,添加起止符#
equation[0]='#';
scanf("%s",&equation[1]);
int len=strlen(equation);
equation[len]='#';
equation[len+1]='\0';
//主循环,分析&计算
while(*p!='\0')
{
while((*p<'0' || *p>'9') && *p!='\0')
{
// NumStack_show(NS); //for debug
// SignStack_show(SS); //for debug
if(SignStack_Empty(SS))
SignStack_push(SS,*p),p++;
else
{
SignStack_GetTop(SS,sign);
if(Sign_Cmp(sign,*p)==s) //栈中的比新来的优先级低,入栈
SignStack_push(SS,*p),p++;
else if(Sign_Cmp(sign,*p)==e) //栈中的和新来的优先级一样,出栈抵消
SignStack_pop(SS,sign),p++;
else if(Sign_Cmp(sign,*p)==b) //栈中的比新来的优先级高,弹出一个符号两个数进行计算,结果入数字栈
{
float t1,t2;
NumStack_pop(NS,t2);
NumStack_pop(NS,t1);
SignStack_pop(SS,sign);
num=cal(sign,t1,t2);
NumStack_push(NS,num);
}
}
}
while(((*p>='0' && *p<='9') || *p=='.') && *p!='\0')
{
if(init)
{
num=0;
temp=10;
flag=1;
init=0;
}
if(*p!='.')
{
if(flag)//分离整数部分
num=num*10+(*p-'0'),p++;
else //分离小数部分
{
num+=1.0*(*p-'0')/temp;
temp*=10;
p++;
}
}
else if(*p=='.')
p++,flag=0;
}
init=1;
NumStack_push(NS,num);
}
SignStack_Clear(SS); //清空栈,准备下一轮
NumStack_Clear(NS);
NumStack_pop(NS,num); //本轮运算结果
cout<<num<<endl;
}
return 0;
}